Related
I'm trying to create a textured surface along a path in three.js. I want the texture to tile/repeat along the direction of the path, like this example created in blender:
One way to accomplish this is to create each of the faces "by hand" and to apply a material/texture to each. That works out fine for simple paths (e.g. straight lines) but gets complicated for more elaborate paths.
One of the tools three.js provides is ExtrudeGeometry. Applying a texture to a mesh created this way looks like this with the default UV mapping:
So clearly I need to write a custom UVGenerator function to pass to ExtrudeGeometry. Unfortunately, this doesn't appear to be something for which there is documentation, and previous questions that show up in search results are either out of date (the most complete answers involve a different API for the UVGenerator functions: example) or have no answers (an example).
Here's a jsFiddle example that illustrates the undesired/default behavior. The code is reproduced below. The uvGenerator() function in it is functionally identical to the default three.js uvGenerator, THREE.WorldUVGenerator. It's in the example just to make it easier to fiddle with.
window.onload = function() {
var camera, dataURI, renderer, scene, surface;
var texture, uvGenerator;
var width = 800, height = 600;
dataURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAi0lEQVRIx83WQQ6AIAwEwMXwb/p0b4SI1CJsS08e2k0nKDGVUsCsDEBESOkikusTIx3ABXJdJEEN9BLsRbRRjoJdiEeIr2Ad0Y+7C1YQr4MRgn+I0UiQYBahNMcJ7Ai9LVRgQXw2RAv0HS2HdIBgtKnxHTtD0O9r/86PEbRbT11TdEHecukrldi/7zd8KDG/sdhdzgAAAABJRU5ErkJggg==';
function initCamera() {
camera = new THREE.PerspectiveCamera(60, width/height, 1, 1000);
camera.position.set(0, 0, 0);
scene.add(camera);
}
function initLights() {
var lights;
lights = [
new THREE.PointLight(0xffffff, 1, 0),
new THREE.PointLight(0xffffff, 1, 0),
new THREE.PointLight(0xffffff, 1, 0),
];
lights[0].position.set(0, 200, 0);
lights[1].position.set(100, 200, 100);
lights[2].position.set(-100, -200, -100);
scene.add(lights[0]);
scene.add(lights[1]);
scene.add(lights[2]);
}
function initRenderer() {
var canvas;
canvas = document.getElementById('canvas');
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
canvas.appendChild(renderer.domElement);
}
function initScene() {
scene = new THREE.Scene();
}
function initSurface() {
surface = extrudeSurface();
surface.position.set(50, -50, 50);
scene.add(surface);
}
function generalInit() {
initScene();
initCamera();
initLights();
initSurface();
initRenderer();
animate();
}
uvGenerator = {
generateTopUV: function(geometry, vertices, idxA, idxB, idxC) {
var ax, ay, bx, by, cx, cy;
ax = vertices[idxA * 3];
ay = vertices[(idxA * 3) + 1];
bx = vertices[idxB * 3];
by = vertices[(idxB * 3) + 1];
cx = vertices[idxC * 3];
cy = vertices[(idxC * 3) + 1];
return([
new THREE.Vector2(ax, ay),
new THREE.Vector2(bx, by),
new THREE.Vector2(cx, cy),
]);
},
generateSideWallUV: function(geometry, vertices,
idxA, idxB, idxC, idxD) {
var ax, ay, az, bx, by, bz, cx, cy, cz;
var dx, dy, dz, bb, bbx, bby, bbz;
geometry.computeBoundingBox();
bb = geometry.boundingBox;
bbx = bb.max.x - bb.min.x;
bby = bb.max.y - bb.min.y;
bbz = bb.max.z - bb.min.z;
ax = vertices[idxA * 3];
ay = vertices[(idxA * 3) + 1];
az = vertices[(idxA * 3) + 2];
bx = vertices[idxB * 3];
by = vertices[(idxB * 3) + 1];
bz = vertices[(idxB * 3) + 2];
cx = vertices[idxC * 3];
cy = vertices[(idxC * 3) + 1];
cz = vertices[(idxC * 3) + 2];
dx = vertices[idxD * 3];
dy = vertices[(idxD * 3) + 1];
dz = vertices[(idxD * 3) + 2];
if(Math.abs(ay - by) < 0.01) {
return([
new THREE.Vector2(ax, 1 - az),
new THREE.Vector2(bx, 1 - bz),
new THREE.Vector2(cx, 1 - cz),
new THREE.Vector2(dx, 1 - dz),
]);
} else {
return([
new THREE.Vector2(ay, 1 - az),
new THREE.Vector2(by, 1 - bz),
new THREE.Vector2(cy, 1 - cz),
new THREE.Vector2(dy, 1 - dz),
]);
}
},
}
function extrudeSurface() {
var extrudeCfg, geometry, material, mesh, size, shape, curve;
size = 20;
curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-50, 0, -25),
new THREE.Vector3(50, 0, 75),
]);
extrudeCfg = {
steps: 200,
bevelEnabled: false,
extrudePath: curve,
UVGenerator: uvGenerator,
//UVGenerator: THREE.WorldUVGenerator,
};
shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(0, size);
shape.lineTo(0.1, size);
shape.lineTo(0.1, 0);
shape.lineTo(0, 0);
geometry = new THREE.ExtrudeGeometry(shape, extrudeCfg);
geometry.computeBoundingBox();
geometry.computeVertexNormals(true);
material = new THREE.MeshBasicMaterial({ map: texture });
mesh = new THREE.Mesh(geometry, material);
new THREE.Vector3(0, 0, 0),
return(mesh);
}
function animate() {
if(!scene)
return;
animID = requestAnimationFrame(animate);
render();
update();
}
function render() {
if(!scene || !camera || !renderer)
return;
renderer.render(scene, camera);
}
function update() {
if(!scene || !camera || !surface)
return;
camera.lookAt(surface.position);
//surface.rotation.x += 0.01;
surface.rotation.y += 0.01;
}
function loadTexture() {
var loader;
loader = new THREE.TextureLoader();
loader.load(dataURI,
function(t) {
t.wrapS = THREE.RepeatWrapping;
t.wrapT = THREE.RepeatWrapping;
t.magFilter = THREE.NearestFilter;
t.minFilter = THREE.NearestFilter;
t.repeat.set(1/20, 1/20);
texture = t;
generalInit();
}
);
}
loadTexture();
};
I've tried converting the vertex coords into a UV-ish 0-1 range by dividing by the size of the geometry's bounding box, but this does not produce the desired result. E.g. something of the form (here only shown for one of the return values):
generateSideWallUV: function(geometry, vertices, idxA, idxB, idxC, idxD) {
var ax = vertices[idxA * 3];
geometry.computeBoundingBox();
var bb = geometry.boundingBox;
var bbx = bb.max.x - bb.min.x;
var bbz = bb.max.z - bb.min.z;
...
return([
new THREE.Vector2(ax / bbx, 1 - (az / bbz),
...
]);
}
}
This approach not working makes sense, because what we care about is the position of the vertex as a fraction of the length of the extruded path, not as a fraction of the bounding box. But I don't know and have not been able to look up how to get that information out of the geometry, vertices, or anything else documented in THREE.
Any help/pointers would be appreciated.
Correction: Dividing the vertex coords by the size of the bounding box also doesn't work in this case because when ExtrudeGeometry calls generateSideWallUV() the bounding box is always
min":{"x":null,"y":null,"z":null},"max":{"x":null,"y":null,"z":null}}
...which means anything/(max.x - min.x) will always evaluate as infinite.
So now I'm even more confused about what we can hope to accomplish in a custom UV generator.
If I'm missing something obvious (for example, if I shouldn't be using ExtrudeGeometry for this sort of thing at all) I'd love to be educated.
Answering my own question:
Here's a link to a jsFiddle of the solution.
Here's the interesting parts. First, instead of using THREE.RepeatWrapping we use ClampToEdgeWrapping and get rid of the repeat setting:
t.wrapS = THREE.ClampToEdgeWrapping;
t.wrapT = THREE.ClampToEdgeWrapping;
//t.repeat.set(1 / 20, 1/20);
Then when we create the config object to pass to ExtrudeGeometry we set the steps to be exactly the number of faces we want. This is kinda a kludge because it seems like we shouldn't have to make a decision about the geometry of the object just to get our UVs right--there are plenty of cases where we might have bends/twists were we probably want more vertices to avoid folding/tearing/just looking wonky. But eh, I'm willing to worry about that later. Anyway, our updated extrudeCfg (in extrudeSurface() in the example) becomes:
extrudgeCfg = {
steps: 20,
bevelEnabled: false,
extrudePath: curve,
UVGenerator: uvGenerator,
};
And then finally we re-write our UVGenerator. And since we're now clamping the texture and using a smaller number of faces, we can do something painfully simple like:
generateSideWallUV: function(geometry, vertices, idxA, idxB, idxC, idxD) {
return([
new THREE.Vector2(0, 0),
new THREE.Vector2(1, 0),
new THREE.Vector2(1, 1),
new THREE.Vector2(0, 1),
]);
}
...which is to say we just stretch a copy of the texture across each face (with the only complication being because the sides of the extruded geometry are a rectangle consisting of two triangles instead of a single quad).
Et voilĂ :
For my current task, I am making a 2D scatterplot with three.js, and need to make the points different sizes. Here is my current implementation. I can't figure out how to make the sizes of the points different and derived from the "radius" feature. Additionally, I want to make a small black border around every point that is the same thickness regardless of size, like this. For these types of features, I've seen that vertexShaders are traditionally used, but I want to render an almost static image and nothing needs to change during runtime. Here is relevant code:
var coordinates = new Float32Array(data_points.length*3);
var colors = new Float32Array(data_points.length*3);
var sizes = new Float32Array(data_points.length);
for (var i=0; i<data_points.length; i++) {
// Set vector coordinates from data
let vertex = new THREE.Vector3(data_points[i].x, data_points[i].y, 0);
let color = new THREE.Color(color_array[data_points[i].label]);
vertex.toArray(coordinates, i*3);
color.toArray(colors, i*3);
sizes[i] = data_points[i].radius*100;
}
let geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(coordinates, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1 ));
let pointsMaterial = new THREE.PointsMaterial({
size: 100,
sizeAttenuation: true,
vertexColors: THREE.VertexColors,
map: new THREE.TextureLoader().load("https://fastforwardlabs.github.io/visualization_assets/circle-sprite.png"),
transparent: true
});
let points = new THREE.Points(geometry, pointsMaterial);
This is a relatively easy problem, but I'm not experienced with javascript and could use any help I can get. Thank you so much!
In order to use Points, you are most likely looking at needing to make a custom shader (vertex+fragment).
But, as a thought, is your data so large that you couldn't use meshes instead? A combination of scaled CircleBufferGeometry as a Mesh and a Line (or fat lines) could do exactly what you want.
let W = window.innerWidth;
let H = window.innerHeight;
let aspect = W / H;
const frustumSize = 100;
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(frustumSize * aspect / -2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 1, 100);
camera.position.set(0, 0, 5);
camera.lookAt(scene.position);
scene.add(camera);
// circle
const geo = new THREE.CircleBufferGeometry(10, 32);
const mat = new THREE.MeshBasicMaterial({
color: 0xaaff55
});
const mesh = new THREE.Mesh(geo, mat);
// outline
const geo2 = geo.clone();
// strip the center point from CircleBufferGeometry:
geo2.attributes.position.array = geo.attributes.position.array.slice(3);
geo2.attributes.position.count--;
geo2.attributes.normal.array = geo.attributes.position.array.slice(3);
geo2.attributes.normal.count--;
geo2.deleteAttribute("uv"); // lines don't need UVs
geo2.index = null; // don't need indexing for this kind of line
const mat2 = new THREE.LineBasicMaterial({
color: 0x55aa00
});
const line = new THREE.LineLoop(geo2, mat2);
// data point
const group = new THREE.Group();
group.add(mesh);
group.add(line);
scene.add(group);
group.userData.scalingMatrix = new THREE.Matrix4().makeScale(2, 2, 2); // for storing the scaling matrix
function render() {
renderer.render(scene, camera);
}
function resize() {
W = window.innerWidth;
H = window.innerHeight;
aspect = W / H;
renderer.setSize(W, H);
camera.left = frustumSize * aspect / -2;
camera.right = frustumSize * aspect / 2;
camera.top = frustumSize / 2;
camera.bottom = frustumSize / -2;
camera.updateProjectionMatrix();
render();
}
window.addEventListener("resize", resize);
resize();
render();
// pretend this is reacting to your mouseovers
let over = true;
const resetMatrix = new THREE.Matrix4()
setInterval(() => {
group.applyMatrix4((over) ? group.userData.scalingMatrix : resetMatrix.getInverse(group.userData.scalingMatrix));
over = !over;
render();
}, 2000);
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
background: white;
}
<script src="https://threejs.org/build/three.js"></script>
There is also (and this is outside my sphere of knowledge) the SVGRenderer, which can render shapes as SVG objects. This may be easier to work with, again depending on your data. See a demo here: https://threejs.org/examples/#svg_sandbox
You can't use PointsMaterial to achieve the intended affect. You need a custom shader material for this or you try to enhance PointsMaterial with Material.onBeforeCompile(). I suggest you study the implementation of the following official example to check out the former approach:
https://threejs.org/examples/webgl_custom_attributes_points
An important part of the code is the vertex shader, which enables points of different sizes:
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}
Notice the additional size attribute.
I am trying to animate a cube along a path in three.js.
CODE
// Ellipse class, which extends the virtual base class Curve
var curve = new THREE.EllipseCurve(
0, 0, // ax, aY
16, 21.28, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
//defines the amount of points the path will have
var path = new THREE.Path( curve.getPoints( 100 ) );
var geometrycirc = path.createPointsGeometry( 100 );
var materialcirc = new THREE.LineBasicMaterial( {
color : 0xff0000
} );
// Create the final object to add to the scene
var ellipse = new THREE.Line( geometrycirc, materialcirc );
ellipse.position.set(0,1,0);
this.scene.add( ellipse );
// add the box to the scene
this.scene.add(this.box);
I have being doing some research into how this could be done and came across this fiddle animate on path This method uses a the THREE.SplineCurve3 method to create the points for the box to use.
My question is do I need to convert my path to use the THREE.SplineCurve3 method.
Or can I use the path as it is?
Any help or pointers would be appreciated.
many thanks
Object Animating on path
Code
// GLOBALS - ALLOCATE THESE OUTSIDE OF THE RENDER LOOP - CHANGED
var cubes = [], marker, spline;
var matrix = new THREE.Matrix4();
var up = new THREE.Vector3( 0, 1, 0 );
var axis = new THREE.Vector3( );
var pt, radians, axis, tangent, path;
// the getPoint starting variable - !important - You get me ;)
var t = 0;
//This function generates the cube and chooses a random color for it
//on initial load.
function getCube(){
// cube mats and cube
var mats = [];
for (var i = 0; i < 6; i ++) {
mats.push(new
THREE.MeshBasicMaterial({color:Math.random()*0xffffff}));
}
var cube = new THREE.Mesh(
new THREE.CubeGeometry(2, 2, 2),
new THREE.MeshFaceMaterial( mats )
);
return cube
}
// Ellipse class, which extends the virtual base class Curve
function Ellipse( xRadius, yRadius ) {
THREE.Curve.call( this );
// add radius as a property
this.xRadius = xRadius;
this.yRadius = yRadius;
}
Ellipse.prototype = Object.create( THREE.Curve.prototype );
Ellipse.prototype.constructor = Ellipse;
// define the getPoint function for the subClass
Ellipse.prototype.getPoint = function ( t ) {
var radians = 2 * Math.PI * t;
return new THREE.Vector3( this.xRadius * Math.cos( radians ),
this.yRadius * Math.sin( radians ),
0 );
};
//
var mesh, renderer, scene, camera, controls;
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 20, 20, 20 );
// controls
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
controls.minDistance = 10;
controls.maxDistance = 50;
// light
var light = new THREE.PointLight( 0xffffff, 0.7 );
camera.add( light );
scene.add( camera ); // add to scene only because the camera has a child
// axes
scene.add( new THREE.AxisHelper( 20 ) );
////////////////////////////////////////
// Create the cube //
////////////////////////////////////////
marker = getCube();
marker.position.set(0,0,0);
scene.add(marker);
////////////////////////////////////////
// Create an Extruded shape //
////////////////////////////////////////
// path
path = new Ellipse( 5, 10 );
// params
var pathSegments = 64;
var tubeRadius = 0.5;
var radiusSegments = 16;
var closed = true;
var geometry = new THREE.TubeBufferGeometry( path, pathSegments, tubeRadius, radiusSegments, closed );
// material
var material = new THREE.MeshPhongMaterial( {
color: 0x0080ff,
} );
// mesh
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
//////////////////////////////////////////////////////////////////////////
// Create the path which is based on our shape above //
//////////////////////////////////////////////////////////////////////////
//Please note that this red ellipse was only created has a guide so that I could be certain that the square is true to the tangent and positioning.
// Ellipse class, which extends the virtual base class Curve
var curve = new THREE.EllipseCurve(
0, 0, // ax, aY
6, 11, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
//defines the amount of points the path will have
var path2 = new THREE.Path( curve.getPoints( 100 ) );
geometrycirc = path2.createPointsGeometry( 100 );
var materialcirc = new THREE.LineBasicMaterial( {
color : 0xff0000
} );
// Create the final object to add to the scene
var ellipse = new THREE.Line( geometrycirc, materialcirc );
ellipse.position.set(0,0,0);
scene.add( ellipse );
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
// set the marker position
pt = path.getPoint( t );
// set the marker position
marker.position.set( pt.x, pt.y, pt.z );
// get the tangent to the curve
tangent = path.getTangent( t ).normalize();
// calculate the axis to rotate around
axis.crossVectors( up, tangent ).normalize();
// calcluate the angle between the up vector and the tangent
radians = Math.acos( up.dot( tangent ) );
// set the quaternion
marker.quaternion.setFromAxisAngle( axis, radians );
t = (t >= 1) ? 0 : t += 0.002;
renderer.render( scene, camera );
}
init();
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/82/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Conclusion
So I was very Fortunate to stumble upon the answer.
In my case it was the creation of a subclass to my object which allowed me to use it's data as points so that an object could use it as a guide.
Yes I am aware that you are thinking 'What is this guy talking about' so I have created a fiddle for you to look at and study.
Fiddle: Object Animating on path
I'm trying to recreate a functionality of a snippet I found that rotates any given element around another element, very much like a planetary rotation.
I want to use this 2D rotation system for a 3D project with three.js.
The 2D functionality is in this fiddle.
I'm having trouble implementing it in my own scene though. For starters, getting the elements' id's for meshes in three.js is tricky.
myObject.name = "objectName";
...
var object = scene.getObjectByName( "objectName" );
This example code where you name your meshes and pass it through isn't doing it for me - which marks the only search result I could find.
Also, I don't understand that by adding the third dimension how the trigonometric function would need to be written.
return {
x: Math.cos(ang) * r - Math.sin(ang) * r + coorX,
y: Math.sin(ang) * r + Math.cos(ang) * r + coorY
};
So there's some work involved that's simply beyond me. That's why I don't see how the functionality needs to look like for a 3D environment? Thank you if you can help me out with that.
I don't know why you had trouble implementing it because you could almost copy paste from the snippet you found.
I don't know how to use JSFiddle. I clicked update but I have no idea if you can see the changes. That's why I'll post them here as well.
//creates the scene
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, 500 / 400, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColorHex( 0xffffff, 1 );
renderer.setSize(625, 500);
document.body.appendChild(renderer.domElement);
//creates the two objects
var geometry = new THREE.SphereGeometry(1.5, 50, 50);
var material = new THREE.MeshPhongMaterial( {color: 0x9a806e} );
var sphere1 = new THREE.Mesh(geometry, material);
var sphere2 = new THREE.Mesh(geometry, material);
//sets positions
sphere1.position.set(0, 0, 0);
sphere2.position.set(-10, 0, 0);
//adds the two spheres to the scene
scene.add(sphere1);
scene.add(sphere2);
//default camera pos
camera.position.z = 400;
var object = {speed: 1.2, spin: 100, side: 0.0};
// copied from snippet
function rotation(coorX, coorY, object) {
object.side += (1.0 / object.speed);
var ang = object.side * 2.0 * Math.PI / 180.0;
var r = object.spin;
return {
x: Math.cos(ang) * r - Math.sin(ang) * r + coorX,
y: Math.sin(ang) * r + Math.cos(ang) * r + coorY
};
}
//renders the scene
var render = function () {
newpos = rotation(sphere1.position.x, sphere1.position.y, object);
sphere2.position.x = newpos.x;
sphere2.position.y = newpos.y;
renderer.render(scene, camera);
};
setInterval(render, 1000 / 60)
I also think you can use this in 3d as well. Because 3d only adds the z-axis and I think that makes no difference. But maybe you can explain me your concerns about using it in 3d.
Here's an alternative approach that lets three.js handle trigonometry for you: http://jsfiddle.net/SF9tX/438/
relevant code:
var sphere2Rig = new THREE.Object3D();
sphere2.position.x = -10;
sphere2Rig.add(sphere2);
scene.add(sphere2Rig);
var render = function () {
sphere2Rig.rotation.z -= 0.025;
renderer.render(scene, camera);
requestAnimationFrame(render);
};
So you create a "rig" that is positioned at the origin and contains the sphere2-object positioned somewhere else. Now if you rotate the rig, you automatically also rotate all children with it, using the rig's origin as center-point for rotation.
I read a Chinese book about WebGL. I run the particle system example of the book with firefox, I meet a problem my particle cannot move.
I put my source code and resource in the website:
https://onedrive.live.com/redir?resid=FBEB6373D9321A7F!649236&authkey=!AA4upPPcARcAvso&ithint=file%2czip
Console Logs on the browser are:
"THREE.WebGLRenderer" "75"
three.min.js:631:0 "THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead."
three.min.js:791:67 "THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."
three.min.js:777:230 "THREE.ParticleSystem has been renamed to THREE.Points."
three.min.js:769:197 "THREE.WebGLRenderer: image is not power of two (109x109). Resized to 128x128"
Here is partial code of the html file:
function updateParticles(){
var particleNum = particleSystem.geometry.vertices.length;
for(var i=0; i<particleNum; i++){
particle = particleSystem.geometry.vertices[i];
particle.z += 5;
if(particle.z>1000){
particle.z-=2000;
}
}
}
function animate() {
requestAnimationFrame(animate);
updateParticles();
renderer.render(scene, camera);
}
Could someone help to solve this problem? Thanks!
I think you maybe using an old version of three.js and it doesn't work with the particle system. Here is a sample that is pretty close to what you are doing. When I run this sample locally it works, then replacing by the three.min.js from your project it stops working. You can grab the three.js version used by this sample there.
// #see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
// set the scene size
var WIDTH = 400,
HEIGHT = 300;
// set some camera attributes
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
// get the DOM element to attach to
// - assume we've got jQuery to hand
var $container = $('#container');
// create a WebGL renderer, camera
// and a scene
var renderer = new THREE.WebGLRenderer();
var camera = new THREE.Camera( VIEW_ANGLE,
ASPECT,
NEAR,
FAR );
var scene = new THREE.Scene();
// the camera starts at 0,0,0 so pull it back
camera.position.z = 300;
// start the renderer - set the clear colour
// to a full black
renderer.setClearColor(new THREE.Color(0, 1));
renderer.setSize(WIDTH, HEIGHT);
// attach the render-supplied DOM element
$container.append(renderer.domElement);
// create the particle variables
var particleCount = 1800,
particles = new THREE.Geometry(),
pMaterial = new THREE.ParticleBasicMaterial({
color: 0xFFFFFF,
size: 20,
map: THREE.ImageUtils.loadTexture(
"./Images/icon_ch.png"
),
blending: THREE.AdditiveBlending,
transparent: true
});
// now create the individual particles
for(var p = 0; p < particleCount; p++) {
// create a particle with random
// position values, -250 -> 250
var pX = Math.random() * 500 - 250,
pY = Math.random() * 500 - 250,
pZ = Math.random() * 500 - 250,
particle = new THREE.Vertex(
new THREE.Vector3(pX, pY, pZ)
);
// create a velocity vector
particle.velocity = new THREE.Vector3(
0, // x
-Math.random(), // y
0); // z
// add it to the geometry
particles.vertices.push(particle);
}
// create the particle system
var particleSystem = new THREE.ParticleSystem(
particles,
pMaterial);
particleSystem.sortParticles = true;
// add it to the scene
scene.addChild(particleSystem);
// animation loop
function update() {
// add some rotation to the system
particleSystem.rotation.y += 0.01;
var pCount = particleCount;
while(pCount--) {
// get the particle
var particle = particles.vertices[pCount];
// check if we need to reset
if(particle.position.y < -200) {
particle.position.y = 200;
particle.velocity.y = 0;
}
// update the velocity
particle.velocity.y -= Math.random() * .1;
// and the position
particle.position.addSelf(
particle.velocity);
}
// flag to the particle system that we've
// changed its vertices. This is the
// dirty little secret.
particleSystem.geometry.__dirtyVertices = true;
renderer.render(scene, camera);
// set up the next call
requestAnimFrame(update);
}
requestAnimFrame(update);