I want to overlay points from a GeoJSON file onto the surface of my Mars basemap using D3.js, similar to what Mike Bostock has done with a MultiLineString, as well as match the rotation angle and speed:
https://bl.ocks.org/mbostock/2b85250396c17a79155302f91ec21224
I am completely new to Three.js, but have been learning D3.js, so I am not sure how to mix the two together. Any help would be much appreciated.
var renderer, scene, camera;
var control;
var stats;
var cameraControl;
var radius = 15;
// Initialize the scene, camera and objects.
function init() {
// To display anything, you need 3 things: (1) Scene, (2) Camera, (3) Renderer
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
// Mars needs (1) geometry, (2) material, (3) mesh
var sphereGeometry = new THREE.SphereGeometry(15, 60, 60);
var sphereMaterial = createMarsMaterial();
var marsMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
marsMesh.name = 'mars';
scene.add(marsMesh);
// position and point the camera to the center of the scene
camera.position.x = 25;
camera.position.y = 26;
camera.position.z = 23;
camera.lookAt(scene.position);
// add controls
cameraControl = new THREE.OrbitControls(camera);
// setup the control object for the control gui
control = new function () {
this.rotationSpeed = 0.001;
};
// add extras
addControlGui(control);
addStatsObject();
// add the output of the renderer to the html element
document.body.appendChild(renderer.domElement);
// start animating
render();
}
function createMarsMaterial() {
// 4096 is the maximum width for maps
var marsTexture = THREE.ImageUtils;
marsTexture.crossOrigin = "";
marsTexture = THREE.ImageUtils.loadTexture("https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/mars.jpg");
var marsMaterial = new THREE.MeshBasicMaterial();
marsMaterial.map = marsTexture;
return marsMaterial;
}
function addControlGui(controlObject) {
var gui = new dat.GUI();
gui.add(controlObject, 'rotationSpeed', -0.01, 0.01);
}
function addStatsObject() {
stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.body.appendChild(stats.domElement);
}
function render() {
stats.update();
cameraControl.update();
scene.getObjectByName('mars').rotation.y += control.rotationSpeed;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
function handleResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.onload = init;
window.addEventListener('resize', handleResize, false);
d3.json("https://tatornator12.github.io/classes/final-project/Mars_LandingSites.json", function(error, topology) {
if (error) throw error;
scene.add(graticule = wireframe(graticule10(), new THREE.LineBasicMaterial({color: 0xaaaaaa})));
scene.add(mesh = wireframe(topojson.mesh(topology, topology.features), new THREE.LineBasicMaterial({color: 0xff0000})));
d3.timer(function(t) {
graticule.rotation.x = mesh.rotation.x = Math.sin(t / 11000) * Math.PI / 3 - Math.PI / 2;
graticule.rotation.z = mesh.rotation.z = t / 10000;
renderer.render(scene, camera);
});
});
// Converts a point [longitude, latitude] in degrees to a THREE.Vector3.
function vertex(point) {
var lambda = point[0] * Math.PI / 180,
phi = point[1] * Math.PI / 180,
cosPhi = Math.cos(phi);
return new THREE.Vector3(
radius * cosPhi * Math.cos(lambda),
radius * cosPhi * Math.sin(lambda),
radius * Math.sin(phi)
);
}
// Converts a GeoJSON MultiLineString in spherical coordinates to a THREE.LineSegments.
function wireframe(multilinestring, material) {
var geometry = new THREE.Geometry;
multilinestring.coordinates.forEach(function(line) {
d3.pairs(line.map(vertex), function(a, b) {
geometry.vertices.push(a, b);
});
});
return new THREE.LineSegments(geometry, material);
}
// See https://github.com/d3/d3-geo/issues/95
function graticule10() {
var epsilon = 1e-6,
x1 = 180, x0 = -x1, y1 = 80, y0 = -y1, dx = 10, dy = 10,
X1 = 180, X0 = -X1, Y1 = 90, Y0 = -Y1, DX = 90, DY = 360,
x = graticuleX(y0, y1, 2.5), y = graticuleY(x0, x1, 2.5),
X = graticuleX(Y0, Y1, 2.5), Y = graticuleY(X0, X1, 2.5);
function graticuleX(y0, y1, dy) {
var y = d3.range(y0, y1 - epsilon, dy).concat(y1);
return function(x) { return y.map(function(y) { return [x, y]; }); };
}
function graticuleY(x0, x1, dx) {
var x = d3.range(x0, x1 - epsilon, dx).concat(x1);
return function(y) { return x.map(function(x) { return [x, y]; }); };
}
return {
type: "MultiPoint",
coordinates: d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X)
.concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y))
.concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return Math.abs(x % DX) > epsilon; }).map(x))
.concat(d3.range(Math.ceil(y0 / dy) * dy, y1 + epsilon, dy).filter(function(y) { return Math.abs(y % DY) > epsilon; }).map(y))
};
}
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/three.js"></script>
<script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/OrbitControls.js"></script>
<script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/dat.gui.min.js"></script>
<script src="https://tatornator12.github.io/classes/final-project/Using_Three_-_D3/stats.min.js"></script>
Related
I am trying to create a random point within a sphere, and I am not sure how to do this. I came up with this but I think it is returning a point within a cube I think I have to do something with Math.PI but not sure how.
#createParticlePosition() {
const shape = this.options.shape;
// shape.radius = 2;
if (shape.type === 'sphere') {
return new Three.Vector3(
(Math.random() * shape.radius - (shape.radius / 2)) * 1.0,
(Math.random() * shape.radius - (shape.radius / 2)) * 1.0,
(Math.random() * shape.radius - (shape.radius / 2)) * 1.0
);
}
}
You are indeed just creating boxes. You're only calculating the x,y,z values linearly, not spherically. Three.js has a Vector3.randomDirection method that could do these calculations for you:
const maxRadius = 2;
// Randomize to range [0, 2]
const randomRadius = Math.random() * maxRadius;
// Create vec3
const randomVec = new THREE.Vector3();
// Make vector point in a random direction with a radius of 1
randomVec.randomDirection();
// Scale vector to match random radius
randomVec.multiplyScalar(randomRadius);
This method utilizes this approach internally to avoid density accumulation in the poles.
To distribute points inside a sphere evenly, having direction and radius, the radius computes with Math.sqrt( r * r * Math.random());
In the snippet, the red point cloud utilizes simple r * Math.random(), the aqua one utilizes that I wrote above:
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls";
let scene = new THREE.Scene();
scene.background = new THREE.Color(0x160016);
let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 8);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.enablePan = false;
let pts = new Array(1000).fill().map(p => {
let rMax = 2 * Math.random();
return new THREE.Vector3().randomDirection().multiplyScalar(rMax);
})
let g = new THREE.BufferGeometry().setFromPoints(pts);
let m = new THREE.PointsMaterial({size: 0.1, color: "red"})
let p = new THREE.Points(g, m);
p.position.x = -2;
scene.add(p)
let pts2 = new Array(1000).fill().map(p => {
let rMax = 2;
let r = Math.sqrt(rMax * rMax * Math.random());
return new THREE.Vector3().randomDirection().multiplyScalar(r);
})
let g2 = new THREE.BufferGeometry().setFromPoints(pts2);
let m2 = new THREE.PointsMaterial({size: 0.1, color: "aqua"})
let p2 = new THREE.Points(g2, m2);
p2.position.x = 2;
scene.add(p2)
renderer.setAnimationLoop(() => {
controls.update();
renderer.render(scene, camera);
});
</script>
For reference: https://discourse.threejs.org/t/random-points-on-surfaces/34153
I am using
var geometry = new THREE.SphereGeometry( 15, 32, 16, 0, 2*Math.PI, 0, x);
with x < 2*PI to create a part of a sphere looking like that :
The thing is I need to have tens of thousands of those, all with the same center and radius but with different rotations and different and very small x values.
I have done research about instancing but can't find a way to use it the way I want to. Any suggestions ?
Use an additional InstancedBufferAttribute to pass phi angles per instance in InstancedMesh, and process those values in vertex shader to form the parts you want, using .onBeforeCompile() method.
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0/build/three.module.js";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 5000);
camera.position.set(0, 0, 50);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", (event) => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
let control = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
const MAX_COUNT = 10000;
let g = new THREE.SphereGeometry(1, 20, 10, 0, Math.PI * 2, Math.PI * 0.25, Math.PI * 0.5);
let m = new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
onBeforeCompile: shader => {
shader.vertexShader = `
attribute float instPhi;
// straight from the docs on Vector3.setFromSphericalCoords
vec3 setFromSphericalCoords( float radius, float phi, float theta ) {
float sinPhiRadius = sin( phi ) * radius;
float x = sinPhiRadius * sin( theta );
float y = cos( phi ) * radius;
float z = sinPhiRadius * cos( theta );
return vec3(x, y, z);
}
${shader.vertexShader}
`.replace(
`#include <beginnormal_vertex>`,
`#include <beginnormal_vertex>
vec3 sphPos = setFromSphericalCoords(1., instPhi * (1. - uv.y), PI * 2. * uv.x); // compute position
objectNormal = normalize(sphPos); // normal is just a normalized vector of the computed position
`).replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
transformed = sphPos; // set computed position
`
);
//console.log(shader.vertexShader);
}
});
let im = new THREE.InstancedMesh(g, m, MAX_COUNT);
scene.add(im);
let v3 = new THREE.Vector3();
let c = new THREE.Color();
let instPhi = []; // data for Phi values
let objMats = new Array(MAX_COUNT).fill().map((o, omIdx) => {
let om = new THREE.Object3D();
om.position.random().subScalar(0.5).multiplyScalar(100);
om.rotation.setFromVector3(v3.random().multiplyScalar(Math.PI));
om.updateMatrix();
im.setMatrixAt(omIdx, om.matrix);
im.setColorAt(omIdx, c.set(Math.random() * 0xffffff))
instPhi.push((Math.random() * 0.4 + 0.1) * Math.PI);
return om;
});
g.setAttribute("instPhi", new THREE.InstancedBufferAttribute(new Float32Array(instPhi), 1));
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
</script>
PS May be turned into the using of InstancedBufferGeometry. Creativity is up to you :)
i have simple 3d project made in three.js.
Currently its controlled by mouse motion which rotates camera around the scene (this also works by touch on mobile).
I want to implement to control this basic camera movement to be controlled by device motion / tilting / gyroscope while accesing web on mobile devices.
Here is my code:
const nearDist = 0.1;
const farDist = 10000;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
55,
window.innerWidth / window.innerHeight,
nearDist,
farDist
);
camera.position.x = farDist * -2;
camera.position.z = 500;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor("#e8a82b");
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.querySelector("#canvas-wrapper").appendChild(renderer.domElement);
// Object
const cubeSize = 300;
const geometry = new THREE.IcosahedronGeometry(cubeSize, 0 ); // BufferAttribute allows for more efficient passing of data to the GPU
const material = new THREE.MeshNormalMaterial({roughness: 1});
const group = new THREE.Group();
for (let i = 0; i < 260; i++) {
const mesh = new THREE.Mesh(geometry, material);
const dist = farDist / 3;
const distDouble = dist * 2;
const tau = 2 * Math.PI; // One turn
mesh.position.x = Math.random() * distDouble - dist;
mesh.position.y = Math.random() * distDouble - dist;
mesh.position.z = Math.random() * distDouble - dist;
mesh.rotation.x = Math.random() * tau;
mesh.rotation.y = Math.random() * tau;
mesh.rotation.z = Math.random() * tau;
// Manually control when 3D transformations recalculation occurs for better performance
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
group.add(mesh);
}
scene.add(group);
// Typo
const loader = new THREE.FontLoader();
const textMesh = new THREE.Mesh();
const createTypo = font => {
const word = "be creative";
const typoProperties = {
font: font,
size: cubeSize,
height: cubeSize / 2,
curveSegments: 16
};
const text = new THREE.TextGeometry(word, typoProperties);
textMesh.geometry = text;
textMesh.material = material;
textMesh.position.x = cubeSize * -2;
textMesh.position.z = cubeSize * -1;
scene.add(textMesh);
};
loader.load(
"https://threejs.org/examples/fonts/helvetiker_regular.typeface.json",
createTypo
);
// Mouse move
let mouseX = 0;
let mouseY = 0;
const mouseFX = {
windowHalfX: window.innerWidth / 2,
windowHalfY: window.innerHeight / 2,
coordinates: function(coordX, coordY) {
mouseX = (coordX - mouseFX.windowHalfX) * 2;
mouseY = (coordY - mouseFX.windowHalfY) * 2;
},
onMouseMove: function(e) {
mouseFX.coordinates(e.clientX, e.clientY);
},
onTouchMove: function(e) {
mouseFX.coordinates(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
}
};
document.addEventListener("mousemove", mouseFX.onMouseMove, false);
document.addEventListener("touchmove", mouseFX.onTouchMove, false);
// Render
const render = () => {
requestAnimationFrame(render);
// Camera animation
camera.position.x += (mouseX - camera.position.x) * 0.05;
camera.position.y += (mouseY * -1 - camera.position.y) * 0.05;
camera.lookAt(scene.position);
const t = Date.now() * 0.001;
const rx = Math.sin(t * 0.6) * 0.5;
const ry = Math.sin(t * 0.3) * 0.5;
const rz = Math.sin(t * 0.2) * 0.5;
group.rotation.x = rx;
group.rotation.y = ry;
group.rotation.z = rz;
textMesh.rotation.x = rx;
textMesh.rotation.y = ry;
textMesh.rotation.z = rx; // :)
renderer.render(scene, camera);
};
render();
const resizeCanvas = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
window.addEventListener("resize", resizeCanvas, false);
I tried to implement threeVR library but without any luck. But since this is my first three.js prject i belive i did it something wrong. I couldnt find any other solution for this.
Thanks for any ideas.
I have a situation where I use an OrbitControls to have a limited view in a room/space. The OrbitControls gets min/max values for azimuth and polar angles to control the view.
The OrbitControls' target is close by the camera to achieve the correct view, compare to the "panorama / cube" example (https://threejs.org/examples/?q=cube#webgl_panorama_cube).
In one situation this works fine. In an other situation this does not work. The reason is that because of the placement of objects, the starting azimuth is -179.999 degrees.
I have not found a way to tell the orbitcontrol to have the azimuth between -179.999 +/- 20 degrees.
I have experimented a little with the algorithm of #Αλέκος (Is angle in between two angles) and it looks like I can calculate the correct angles (not fully implemented). For that I would have set an azimuth and a delta in stead of min/max (and change the OrbitControls code).
Any suggestions for an easier solution? Thanks!
Test code:
var scene = new THREE.Scene();
var orbitControls;
var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
vertexColors: THREE.FaceColors
});
var geometry = new THREE.BoxGeometry(1, 1, 1);
// colors
red = new THREE.Color(1, 0, 0);
green = new THREE.Color(0, 1, 0);
blue = new THREE.Color(0, 0, 1);
var colors = [red, green, blue];
console.log("FACES", geometry.faces.length)
for (var i = 0; i < 3; i++) {
geometry.faces[4 * i].color = colors[i];
geometry.faces[4 * i + 1].color = colors[i];
geometry.faces[4 * i + 2].color = colors[i];
geometry.faces[4 * i + 3].color = colors[i];
}
geometry.faces[0].color = new THREE.Color(1, 1, 1);
geometry.faces[4].color = new THREE.Color(1, 1, 1);
geometry.faces[8].color = new THREE.Color(1, 1, 1);
var cube = new THREE.Mesh(geometry, material);
cube.position.x = 0;
cube.position.y = 4;
cube.position.z = 44;
console.log("CUBE", cube.position);
var cubeAxis = new THREE.AxesHelper(20);
cube.add(cubeAxis);
scene.add(cube);
camera.position.x = 0.8;
camera.position.y = 4.5;
camera.position.z = 33;
orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
orbitControls.enablePan = false;
orbitControls.enableZoom = false;
orbitControls.minPolarAngle = (90 - 10) * Math.PI / 180;
orbitControls.maxPolarAngle = (90 + 10) * Math.PI / 180;
// These values do not work:
orbitControls.maxAzimuthAngle = 200 * Math.PI / 180;
orbitControls.minAzimuthAngle = 160 * Math.PI / 180;
orbitControls.target.x = 0.8;
orbitControls.target.y = 4.5;
orbitControls.target.z = 33.1;
orbitControls.enabled = true;
var animate = function () {
requestAnimationFrame(animate);
if (orbitControls) {
orbitControls.update();
}
renderer.render(scene, camera);
// console.log("orbitControls", "azi", orbitControls.getAzimuthalAngle() * 180 / Math.PI);
};
animate();
I am trying to build something like this site hero-unit: http://www.nodeplus.com.cn/
My approach is to make a temporary 2d canvas, print out my message to it, scan the pixels and then use them to position the meshes in 3d space:
// STEP 1: if a pixel is detected on every gridX / gridY step, put it in the pos array
for (y = 0; y <= height; y += gridY) {
for (x = 0; x <= width; x += gridX) {
if (buffer32[y * width + x]) {
positions.push({x: x, y: y});
}
}
}
then i put them in 3d space accordingly
for (i = 0; i < len; i++) {
pos = positions[i];
shape = new Shape({
x: pos.x / 10,
y: -pos.y / 10,
z: Math.random() * 4
});
shape.init(scene);
shapes.push(shape);
}
Thats all fine and good, but i want them to be positioned at the scene center.
Because of the way im scanning the 2d canvas and getting the positions from there (starting at 0, 0), they get some very weird positioning in 3d space with three.js (because i have pixels that are positioned like {x: 1600, y:500} on bigger screens)
I had to put a strange camera.lookAt position just to look at it, something like:
camera.position.set(100, 0, 80);
camera.lookAt({
x: 95,
y: -50,
z: scene.position.z
});
This kinda works for now, but its not a good option for me. I want to rotate the camera around the letters and dont want to rotate it around some weird position like this.
You can check out the code here:
var heroUnit = (function(self) {
function Shape(pos) {
this.pos = pos;
this.mesh = null;
this.radius = 2 + Math.random() * 2;
this.speed = 0.4 + Math.random() * 0.4;
}
Shape.prototype.init = function(scene) {
var geometry, material, mesh,
v1, v2, v3;
geometry = new THREE.Geometry();
v1 = new THREE.Vector3(this.pos.x - this.radius / 2, this.pos.y - this.radius / 2, this.pos.z - this.radius / 2);
v2 = new THREE.Vector3(this.pos.x + this.radius / 2, this.pos.y - this.radius / 2, this.pos.z - this.radius / 2);
v3 = new THREE.Vector3(this.pos.x + this.radius / 2, this.pos.y + this.radius / 2, this.pos.z + this.radius / 2);
geometry.vertices.push(v1);
geometry.vertices.push(v2);
geometry.vertices.push(v3);
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.computeFaceNormals();
material = new THREE.MeshLambertMaterial({color: Math.random() * 0xFFFFFF});
mesh = new THREE.Mesh(geometry, material);
this.mesh = mesh;
this.mesh.receiveShadow = true;
//this.mesh.scale.set(Math.random() * 20, Math.random() * 20, Math.random() * 20);
this.mesh.rotation.set(0, 0, 0)
scene.add(this.mesh);
}
var width, height,
aspectRatio, pov,
near, far,
scene, camera,
renderer,
effectPositions,
shapes, light;
function init(string) {
// constants
width = window.innerWidth;
height = window.innerHeight;
pov = 45;
aspectRatio = width / height;
near = 0.1;
far = 1000;
// three.js specific stuff
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(pov, aspectRatio, near, far);
renderer = new THREE.WebGLRenderer();
light = new THREE.DirectionalLight(0x000000, 0.6);
// containers
effectPositions = getPixels(string);
shapes = [];
// set up light
light.position.set(100, 0, 200);
light.castShadow = true;
scene.add(light);
// set size to renderer, color it in dark blue & append it to dom
renderer.setSize(width, height);
renderer.setClearColor(0x17293a);
document.body.appendChild(renderer.domElement);
// set up camera
camera.position.set(100, 0, 80);
camera.lookAt({
x: 50,
y: -50,
z: scene.position.z
});
scene.add(camera);
generateShapes(effectPositions);
}
// generate 3d shapes according to our 2d positions array
function generateShapes(positions) {
var i, n, len,
pos, shape,
maxIterations
len = positions.length;
maxIterations = 2;
for (n = 0; n <= maxIterations; n++) {
for (i = 0; i < len; i++) {
pos = positions[i];
shape = new Shape({
x: pos.x / 10,
y: -pos.y / 10,
z: Math.random() * 4
});
shape.init(scene);
shapes.push(shape);
}
}
}
// get 2d positions
function getPixels(string) {
var canvas, ctx,
idata, buffer32,
x, y,
gridX, gridY,
positions;
//make temp canvas on which to draw and get pixel data
canvas = document.createElement('canvas');
ctx = canvas.getContext('2d');
positions = [];
string = string.toUpperCase().split("").join(String.fromCharCode(8202));
canvas.width = width;
canvas.height = height;
//document.body.appendChild(canvas);
ctx.fillStyle = '#000';
ctx.font = 'bold 260px Arial';
ctx.fillText(string, width / 2 - ctx.measureText(string).width / 2, height / 2);
idata = ctx.getImageData(0, 0, width, height);
buffer32 = new Uint32Array(idata.data.buffer);
gridX = gridY = 14;
// if a pixel is detected on every gridX / gridY step, put it in the pos array
for (y = 0; y <= height; y += gridY) {
for (x = 0; x <= width; x += gridX) {
if (buffer32[y * width + x]) {
positions.push({x: x, y: y});
}
}
}
// return pos array
return positions;
}
// render function
function render(ts) {
renderer.render(scene, camera);
shapes.forEach(function(shape) {
// shape.mesh.position.x = shape.dist + (Math.sin(ts / 500)) * shape.speed;
})
}
return {
init: init,
render: render
}
}(heroUnit || {}))
// init our program
heroUnit.init('100');
// redraw each frame
(function drawFrame(ts) {
window.requestAnimationFrame(drawFrame);
heroUnit.render(ts);
}());
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.js"></script>
Any ideas and suggestions are more then welcome, cheers