Related
I have scene with elements size over 500 units and i want to create mirror effect for them. to Reach descripted effect i used Reflector library from three.js webgl_mirror example.
I placed mirror on ground and most of meshes disappears or showing only small parts of surface when i set background hdri without its displayes normally. I builded other scene for tests and it looks like this unexpected effect begins when distance between mirror and obiect is over around 75 units (sometimes its less i dont know what its depends).
Image to preview on that effect
Is there any possibility that i could increase range of this clipping box size for that mirror? (i really want to avoid of scaling my actual created scene)
What i already tryed:
-changing my perspective camera far and near distances. - no effect
-manipulate paramets for clipBias and recursion or even increasing texture size. -no effect
-adding multiple lights around elements. -no effect
code that i used for experiment:
sceneSetup = () => {
//initialize
const width = this.mount.clientWidth;
const height = this.mount.clientHeight;
this.scene = new THREE.Scene();
let helperScene = this.scene;
this.camera = new THREE.PerspectiveCamera(60, width / height, 1, 500);
this.camera.position.z = 200;
this.controls = new OrbitControls(this.camera, document.body);
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(width, height);
this.mount.appendChild(this.renderer.domElement); //render to container (React staff)
///Load HDR map
new RGBELoader()
.setDataType(THREE.UnsignedByteType)
.load(HdrFile, function(texture) {
var envMap = pmremGenerator.fromEquirectangular(texture).texture;
helperScene.background = envMap; // comment to see issue
helperScene.environment = envMap;
texture.dispose();
pmremGenerator.dispose();
});
var pmremGenerator = new THREE.PMREMGenerator(this.renderer);
pmremGenerator.compileEquirectangularShader();
//create ground mirror
let geometry = new THREE.PlaneBufferGeometry(200, 200);
let groundMirror = new Reflector(geometry, {
clipBias: 0,
textureWidth: 1024,
textureHeight: 1024,
color: 0x889999,
recursion: 1
});
groundMirror .position.z = -20;
groundMirror .rotation.x = Math.PI * -0.5;
//change groundMirror .position.y to -104 and evrything looks fine;
groundMirror .position.y = -105;
this.scene.add(groundMirror );
};
addCustomSceneObjects = () => {
//create cube for reflect
const geometry = new THREE.BoxGeometry(50, 50, 50);
const material = new THREE.MeshPhongMaterial({
color: 0x156289,
emissive: 0x072534,
side: THREE.DoubleSide,
depthTest: true,
depthWrite: true
});
this.cube = new THREE.Mesh(geometry, material);
this.cube.position.y = 0;
this.scene.add(this.cube);
//radding lights
const lights = [];
lights[0] = new THREE.PointLight(0xffffff, 1, 0);
lights[1] = new THREE.PointLight(0xffffff, 1, 0);
lights[2] = 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);
this.scene.add(lights[0]);
this.scene.add(lights[1]);
this.scene.add(lights[2]);
};
startAnimationLoop = () => {
//rotate cube
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
this.requestID = window.requestAnimationFrame(this.startAnimationLoop);
this.renderer.render(this.scene, this.camera);
};
i have tried a lot of ways to go around this topic, before asking and now i really have no clue how to accomplish object picking with gpu on a gltf loaded file, so im hoping for any help that i can get :(
I've loaded a huge GLTF file, with a lot of little objects in it, due to the file count its not possible to achieve a good fps, if i just add them to the scene, so i have managed to achieve 60fps merging the gltfs children into chunks, but when i try to implement the webgl_interactive_cubes_gpu example, but it doesn't seem to be working for me, I always get the same object when im clicking.
To debug i have tried rendering the pickingScene and everything seems to be in place, graphically speaking, but when it comes to picking it doesnt seem to be working as i expected, unless im doing something wrong.
Raycast picking is not a suitable option for me as there are a lot of objects and adding renderin them would kill the fps. (55k objects);
Below is the code once the gltf is loaded:
var child = gltf.scene.children[i];
var childGeomCopy = child.geometry.clone();
childGeomCopy.translate(geomPosition.x, geomPosition.y, geomPosition.z);
childGeomCopy.scale(child.scale.x * Scalar, child.scale.y * Scalar, child.scale.z * Scalar);
childGeomCopy.computeBoundingBox();
childGeomCopy.computeBoundingSphere();
childGeomCopy.applyMatrix(new THREE.Matrix4());
geometriesPicking.push(childGeomCopy);
var individualObj = new THREE.Mesh(childGeomCopy, IndividualObjMat);
individualObj.name = "individual_" + child.name;
pickingData[childCounter] = {
object: individualObj,
position: individualObj.position.clone(),
rotation: individualObj.rotation.clone(),
scale: individualObj.scale.clone()
};
childCounter++;
Edit:
gltf.scene.traverse(function (child) {
//console.log(child.type);
if (child.isMesh) {
let geometry = child.geometry.clone();
let position = new THREE.Vector3();
position.x = child.position.x;
position.y = child.position.y;
position.z = child.position.z;
let rotation = new THREE.Euler();
rotation.x = child.rotation.x;
rotation.y = child.rotation.y;
rotation.z = child.rotation.z;
let scale = new THREE.Vector3();
scale.x = child.scale.x;
scale.y = child.scale.y;
scale.z = child.scale.z;
quaternion.setFromEuler(rotation);
matrix.compose(position.multiplyScalar(Scalar), quaternion, scale.multiplyScalar(Scalar));
geometry.applyMatrix(matrix);
applyVertexColors(geometry, color.setHex(Math.random() * 0xffffff));
geometriesDrawn.push(geometry);
geometry = geometry.clone();
applyVertexColors(geometry, color.setHex(childCounter));
geometriesPicking.push(geometry);
pickingData[childCounter] = {
object: new THREE.Mesh(geometry.clone(), new THREE.MeshBasicMaterial({ color: 0xffff00, blending: THREE.AdditiveBlending, transparent: true, opacity: 0.8 })),
id: childCounter,
position: position,
rotation: rotation,
scale: scale
};
childCounter++;
//console.log("%c [childCounter] :", "", childCounter);
}
});
...
var pickingGeom = THREE.BufferGeometryUtils.mergeBufferGeometries(geometriesPicking);
pickingGeom.rotateX(THREE.Math.degToRad(90)); pickingScene.add(new THREE.Mesh(pickingGeom, pickingMaterial));
Then on my MouseUp function I call pick(mouse*) and pass in the mouse* information:
function pick(mouse) {
camera.setViewOffset(renderer.domElement.width, renderer.domElement.height, mouse.x * window.devicePixelRatio | 0, mouse.y * window.devicePixelRatio | 0, 1, 1);
renderer.setRenderTarget(pickingTexture);
renderer.render(pickingScene, camera);
camera.clearViewOffset();
var pixelBuffer = new Uint8Array(4);
renderer.readRenderTargetPixels(pickingTexture, 0, 0, 1, 1, pixelBuffer);
var id = (pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | (pixelBuffer[2]);
var data = pickingData[id];
if (data) {
console.log(data.object.name, ":", data.position); // Always return the same object
}}
I have a 3D sphere that I want to map an array of images onto, and I want to be able to control each individual image i.e. fading out/in each image independently. I'll provide an example image of what I'm trying to achieve as I feel like that's the best way to explain it.
So as you can see above, 8 images per column and 16(?) per row.
I have been able to recreate the above image by simply mapping that image to a SphereGeometry, however I would like to be able to dynamically swap out images, and fade them in at different times.
What I've tried so far / My ideas:
I tried pushing 8 test images to an array and using that as the material map, and then looping through each face of the SphereGeometry and assigning a material index of 1 through 8 and then resetting after every 8 using modulo, but that didn't work:
function createGlobe() {
var geomGlobe = new THREE.SphereGeometry(40, 32, 16);
var l = geomGlobe.faces.length;
imageArray.push(new THREE.MeshBasicMaterial({map: texture1}));
imageArray.push(new THREE.MeshBasicMaterial({map: texture2}));
imageArray.push(new THREE.MeshBasicMaterial({map: texture3}));
imageArray.push(new THREE.MeshBasicMaterial({map: texture4}));
imageArray.push(new THREE.MeshBasicMaterial({map: texture5}));
imageArray.push(new THREE.MeshBasicMaterial({map: texture6}));
imageArray.push(new THREE.MeshBasicMaterial({map: texture7}));
imageArray.push(new THREE.MeshBasicMaterial({map: texture8}));
for (var i = 0; i < l; i++) {
geomGlobe.faces[i].materialIndex = i % 8;
}
Globe = new THREE.Mesh(geomGlobe, imageArray);
scene.add(Globe);
}
I think I need to count every 4 or 8 faces and then set the material
index for each one of those faces to be the same so that they all use
the same image, but I'm not sure if the faces line up correctly in
that way.
So essentially what I need:
A way to dynamically add images to a sphere in an 8 per column, 16 per row fashion, and the ability to manipulate each one of those images individually.
Any help is very appreciated because I'm very stuck!
I recommend making a large canvas and using that as your texture, then animating your transitions into the canvas, followed by setting texture.needsUpdate = true to update it on the GPU.
You may find that the texture updating takes too much time.. in which case, you could try making 2 canvasses+spheres.. and crossfade between them by changing the frontmost ones opacity.
Below is a snippet showing one way to fade one sphere into another with some randomly filled canvasses..
var renderer = new THREE.WebGLRenderer();
var w = 300;
var h = 200;
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
45, // Field of view
w / h, // Aspect ratio
0.1, // Near
10000 // Far
);
camera.position.set(15, 10, 15);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var light = new THREE.PointLight(0xFFFF00);
light.position.set(20, 20, 20);
scene.add(light);
var light1 = new THREE.AmbientLight(0x808080);
light1.position.set(20, 20, 20);
scene.add(light1);
var light2 = new THREE.PointLight(0x00FFFF);
light2.position.set(-20, 20, -20);
scene.add(light2);
var light3 = new THREE.PointLight(0xFF00FF);
light3.position.set(-20, -20, -20);
scene.add(light3);
var sphereGeom = new THREE.SphereGeometry(5, 16, 16);
function rnd(rng) {
return (Math.random() * rng)
}
function irnd(rng) {
return rnd(rng) | 0
}
function randomCanvasTexture(sz) {
var canv = document.createElement('canvas');
canv.width = canv.height = sz;
var ctx = canv.getContext('2d')
for (var i = 0; i < 100; i++) {
ctx.fillStyle = `rgb(${irnd(256)},${irnd(256)},${irnd(256)})`
ctx.fillRect(irnd(sz), irnd(sz), 32, 32)
}
var tex = new THREE.Texture(canv);
tex.needsUpdate = true;
return tex;
}
var material = new THREE.MeshLambertMaterial({
color: 0x808080,
map: randomCanvasTexture(256)
});
var mesh = new THREE.Mesh(sphereGeom, material);
var mesh1 = mesh.clone()
mesh1.material = mesh.material.clone()
mesh1.material.transparent = true;
mesh1.material.opacity = 0.5;
mesh1.material.map = randomCanvasTexture(256)
scene.add(mesh);
scene.add(mesh1);
renderer.setClearColor(0xdddddd, 1);
(function animate() {
mesh1.material.opacity = (Math.sin(performance.now() * 0.001) + 1) * 0.5
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
})();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
Without gunning for any optimizations, one could try something like this:
textures.forEach( tex=>{
const s = mySphere.clone()
s.material = s.material.clone()
tex.offset.copy(someOffset)
tex.repeat.copy(someRepeat)
tex.wrapS = tex.wrapT = THREE.ClampToEdgeWrapping // or something like that
s.material.map = tex
s.material.transparent = true
scene.add(s)
})
The idea is to just draw the same sphere over and over, but masked with different offsets. It might not work with just the .map but it might work with alphaMap which is either all black or all white.
I am currently experimenting with three.js. I would like to change the code in the example below so the dots are round, not square.
Codepen example
I found another example called canvas particle random, which has round particles, and basically, the only difference in the script is the following:
var PI2 = Math.PI * 2;
var program = function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
};
I thought that if I add this to the other script, then the particles would become round. However, when I added the above script to the first script, it doesn't work (I just get a blue screen).
Anyone know what I am doing wrong?
Even though this question has been asked more than 2 years ago, I thought it would be useful to add that you could always write your own fragment shader using a three.js ShaderMaterial:
let geom = new three.Geometry();
geom.vertices.push(new three.Vector3(0,0,0));
let material = new three.ShaderMaterial({
transparent: true,
uniforms: {
size: {value: 10},
scale: {value: 1},
color: {value: new three.Color('maroon')}
},
vertexShader: three.ShaderLib.points.vertexShader,
fragmentShader: `
uniform vec3 color;
void main() {
vec2 xy = gl_PointCoord.xy - vec2(0.5);
float ll = length(xy);
gl_FragColor = vec4(color, step(ll, 0.5));
}
`
});
let points = new three.Points(geom, material);
As others have said, you can use a texture as the map in your PointsMaterial.
But if you just want circles, an easier method may be to create the map dynamically with a canvas (which is what the code you posted seems to be trying to do).
HERE is a fiddle with your code updated to use a canvas as your texture map.
NOTE: I have changed the colors in your paramters object to make it more obvious that different colors are being used.
The function that creates a circle on a canvas for use as a map.
function createCanvasMaterial(color, size) {
var matCanvas = document.createElement('canvas');
matCanvas.width = matCanvas.height = size;
var matContext = matCanvas.getContext('2d');
// create exture object from canvas.
var texture = new THREE.Texture(matCanvas);
// Draw a circle
var center = size / 2;
matContext.beginPath();
matContext.arc(center, center, size/2, 0, 2 * Math.PI, false);
matContext.closePath();
matContext.fillStyle = color;
matContext.fill();
// need to set needsUpdate
texture.needsUpdate = true;
// return a texture made from the canvas
return texture;
}
map canvas creation in the loop using the parameters object.
for (i = 0; i < parameters.length; i++) {
color = parameters[i][0];
size = parameters[i][1];
var hexColor = new THREE.Color(color[0], color[1], color[2]).getHexString();
materials[i] = new THREE.PointsMaterial({
size: 20,
map: createCanvasMaterial('#'+hexColor, 256),
transparent: true,
depthWrite: false
});
particles = new THREE.Points(geometry, materials[i]);
particles.rotation.x = Math.random() * 6;
particles.rotation.y = Math.random() * 6;
particles.rotation.z = Math.random() * 6;
scene.add(particles);
}
Have to set depthWrite to false on the marterial. see THIS issue.
I have now created a blog post on Three.js canvas particles
You can use a texture to your sprites:
var tex = new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/disc.png");
// load the texture
for (i = 0; i < parameters.length; i++) {
color = parameters[i][0];
size = parameters[i][1];
materials[i] = new THREE.PointsMaterial({
size: size,
map: tex // apply the texture in your material
});
particles = new THREE.Points(geometry, materials[i]);
particles.rotation.x = Math.random() * 6;
particles.rotation.y = Math.random() * 6;
particles.rotation.z = Math.random() * 6;
scene.add(particles);
}
I am able to display a THREE.TubeGeometry figure as follows
Code below, link to jsbin
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.js"></script>
<script>
// global variables
var renderer;
var scene;
var camera;
var geometry;
var control;
var count = 0;
var animationTracker;
init();
drawSpline();
function init()
{
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render, sets the background color and the size
renderer = new THREE.WebGLRenderer();
renderer.setClearColor('lightgray', 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
// position and point the camera to the center of the scene
camera.position.x = 0;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
// add the output of the renderer to the html element
document.body.appendChild(renderer.domElement);
}
function drawSpline(numPoints)
{
var numPoints = 100;
// var start = new THREE.Vector3(-5, 0, 20);
var start = new THREE.Vector3(-5, 0, 20);
var middle = new THREE.Vector3(0, 35, 0);
var end = new THREE.Vector3(5, 0, -20);
var curveQuad = new THREE.QuadraticBezierCurve3(start, middle, end);
var tube = new THREE.TubeGeometry(curveQuad, numPoints, 0.5, 20, false);
var mesh = new THREE.Mesh(tube, new THREE.MeshNormalMaterial({
opacity: 0.9,
transparent: true
}));
scene.add(mesh);
renderer.render(scene, camera);
}
</script>
</body>
</html>
However, I would like to display incrementally, as in, like an arc that is loading, such that it starts as the start point, draws incrementally and finally looks the below arc upon completion.
I have been putting in some effort, and was able to do this by storing all the points/coordinates covered by the arc, and drawing lines between the consecutive coordinates, such that I get the 'arc loading incrementally' feel. However, is there a better way to achieve this? This is the link to jsbin
Adding the code here as well
<!DOCTYPE html>
<html>
<head>
<title>Incremental Spline Curve</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<script>
// global variables
var renderer;
var scene;
var camera;
var splineGeometry;
var control;
var count = 0;
var animationTracker;
// var sphereCamera;
var sphere;
var light;
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render, sets the background color and the size
renderer = new THREE.WebGLRenderer();
// renderer.setClearColor(0x000000, 1.0);
renderer.setClearColor( 0xffffff, 1 );
renderer.setSize(window.innerWidth, window.innerHeight);
// position and point the camera to the center of the scene
camera.position.x = 0;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
// add the output of the renderer to the html element
document.body.appendChild(renderer.domElement);
// //init for sphere
// sphereCamera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
// sphereCamera.position.y = -400;
// sphereCamera.position.z = 400;
// sphereCamera.rotation.x = .70;
sphere = new THREE.Mesh(new THREE.SphereGeometry(0.8,31,31), new THREE.MeshLambertMaterial({
color: 'yellow',
}));
light = new THREE.DirectionalLight('white', 1);
// light.position.set(0,-400,400).normalize();
light.position.set(0,10,10).normalize();
//get points covered by Spline
getSplineData();
}
//save points in geometry.vertices
function getSplineData() {
var curve = new THREE.CubicBezierCurve3(
new THREE.Vector3( -5, 0, 10 ),
new THREE.Vector3(0, 20, 0 ),
new THREE.Vector3(0, 20, 0 ),
new THREE.Vector3( 2, 0, -25 )
);
splineGeometry = new THREE.Geometry();
splineGeometry.vertices = curve.getPoints( 50 );
animate();
}
//scheduler loop
function animate() {
if(count == 50)
{
cancelAnimationFrame(animationTracker);
return;
}
//add line to the scene
drawLine();
renderer.render(scene, camera);
// renderer.render(scene, sphereCamera);
count += 1;
// camera.position.z -= 0.25;
// camera.position.y -= 0.25;
animationTracker = requestAnimationFrame(animate);
}
function drawLine() {
var lineGeometry = new THREE.Geometry();
var lineMaterial = new THREE.LineBasicMaterial({
color: 0x0000ff
});
console.log(splineGeometry.vertices[count]);
console.log(splineGeometry.vertices[count+1]);
lineGeometry.vertices.push(
splineGeometry.vertices[count],
splineGeometry.vertices[count+1]
);
var line = new THREE.Line( lineGeometry, lineMaterial );
scene.add( line );
}
// calls the init function when the window is done loading.
window.onload = init;
</script>
<body>
</body>
</html>
Drawback : The drawback of doing it the above way is that, end of the day, I'm drawing a line between consecutive points, and so I lose out on a lot of the effects possible in TubeGeometry such as, thickness, transparency etc.
Please suggest me an alternative way to get a smooth incremental load for the TubeGeometry.
THREE.TubeGeometry returns a THREE.BufferGeometry.
With THREE.BufferGeometry, you have access to a property drawRange that you can set to animate the drawing of the mesh:
let nEnd = 0, nMax, nStep = 90; // 30 faces * 3 vertices/face
...
const geometry = new THREE.TubeGeometry( path, pathSegments, tubeRadius, radiusSegments, closed );
nMax = geometry.attributes.position.count;
...
function animate() {
requestAnimationFrame( animate );
nEnd = ( nEnd + nStep ) % nMax;
mesh.geometry.setDrawRange( 0, nEnd );
renderer.render( scene, camera );
}
EDIT: For another approach, see this SO answer.
three.js r.144
Normally you would be able to use the method .getPointAt() to "get a vector for point at relative position in curve according to arc length" to get a point at a certain percentage of the length of the curve.
So normally if you want to draw 70% of the curve and a full curve is drawn in 100 segments. Then you could do:
var percentage = 70;
var curvePath = new THREE.CurvePath();
var end, start = curveQuad.getPointAt( 0 );
for(var i = 1; i < percentage; i++){
end = curveQuad.getPointAt( percentage / 100 );
lineCurve = new THREE.LineCurve( start, end );
curvePath.add( lineCurve );
start = end;
}
But I think this is not working for your curveQuad since the getPointAt method is not implemented for this type. A work around is to get a 100 points for your curve in an array like this:
points = curve.getPoints(100);
And then you can do almost the same:
var percentage = 70;
var curvePath = new THREE.CurvePath();
var end, start = points[ 0 ];
for(var i = 1; i < percentage; i++){
end = points[ percentage ]
lineCurve = new THREE.LineCurve( start, end );
curvePath.add( lineCurve );
start = end;
}
now your curvePath holds the line segments you want to use for drawing the tube:
// draw the geometry
var radius = 5, radiusSegments = 8, closed = false;
var geometry = new THREE.TubeGeometry(curvePath, percentage, radius, radiusSegments, closed);
Here a fiddle with a demonstration on how to use this dynamically
I'm not really that familiar with three.js. But I think I can be of assistance. I have two solutions for you. Both based on the same principle: build a new TubeGeometry or rebuild the current one, around a new curve.
Solution 1 (Simple):
var CurveSection = THREE.Curve.create(function(base, from, to) {
this.base = base;
this.from = from;
this.to = to;
}, function(t) {
return this.base.getPoint((1 - t) * this.from + t * this.to);
});
You define a new type of curve which just selects a segment out of a given curve. Usage:
var curve = new CurveSection(yourCurve, 0, .76); // Where .76 is your percentage
Now you can build a new tube.
Solution 2 (Mathematics!):
You are using for your arc a quadratic bezier curve, that's awesome! This curve is a parabola. You want just a segment of that parabola and that is again a parabola, just with other bounds.
What we need is a section of the bezier curve. Let's say the curve is defined by A (start), B (direction), C (end). If we want to change the start to a point D and the end to a point F we need the point E that is the direction of the curve in D and F. So the tangents to our parabola in D and F have to intersect in E. So the following code will give us the desired result:
// Calculates the instersection point of Line3 l1 and Line3 l2.
function intersection(l1, l2) {
var A = l1.start;
var P = l2.closestPointToPoint(A);
var Q = l1.closestPointToPoint(P);
var l = P.distanceToSquared(A) / Q.distanceTo(A);
var d = (new THREE.Vector3()).subVectors(Q, A);
return d.multiplyScalar(l / d.length()).add(A);
}
// Calculate the tangentVector of the bezier-curve
function tangentQuadraticBezier(bezier, t) {
var s = bezier.v0,
m = bezier.v1,
e = bezier.v2;
return new THREE.Vector3(
THREE.CurveUtils.tangentQuadraticBezier(t, s.x, m.x, e.x),
THREE.CurveUtils.tangentQuadraticBezier(t, s.y, m.y, e.y),
THREE.CurveUtils.tangentQuadraticBezier(t, s.z, m.z, e.z)
);
}
// Returns a new QuadraticBezierCurve3 with the new bounds.
function sectionInQuadraticBezier(bezier, from, to) {
var s = bezier.v0,
m = bezier.v1,
e = bezier.v2;
var ns = bezier.getPoint(from),
ne = bezier.getPoint(to);
var nm = intersection(
new THREE.Line3(ns, tangentQuadraticBezier(bezier, from).add(ns)),
new THREE.Line3(ne, tangentQuadraticBezier(bezier, to).add(ne))
);
return new THREE.QuadraticBezierCurve3(ns, nm, ne);
}
This is a very mathematical way, but if you should need the special properties of a Bezier curve, this is the way to go.
Note: The first solution is the simplest. I am not familiar with Three.js so I wouldn't know what the most efficient way to implement the animation is. Three.js doesn't seem to use the special properties of a bezier curve so maybe solution 2 isn't that useful.
I hope you have gotten something useful out of this.