This shows an example of how to create a two-color gradient along a THREE.js line:
Color Gradient for Three.js line
How do you implement a multi-stop color gradient along a line? It looks like attributes will only interpolate across two values (I tried passing in three, it only worked with the first two values).
This is the do-it-yourself color gradient approach:
Create a line geometry and add some vertices:
var lineGeometry = new THREE.Geometry();
lineGeometry.vertices.push(
new THREE.Vector3( -10, 0, 0 ),
new THREE.Vector3( -10, 10, 0 )
);
Use some helper functions for convenience:
var steps = 0.2;
var phase = 1.5;
var coloredLine = getColoredBufferLine( steps, phase, lineGeometry );
scene.add( coloredLine );
jsfiddle: http://jsfiddle.net/jfd58hbm/
Explaination:
getColoredBufferLine creates a new buffer geometry from the geometry, which is just for convenience. It then iterates the vertices, assigning each vertex a color. The color is calculated using another helper: color.set ( makeColorGradient( i, frequency, phase ) );.
Where basically frequency defines how many color changes you want the line to receive.
And phase is a shift of the color spectrum (= what color does the line start with).
I have added a dat.gui so you can play around with the parameters. If you want to change the color repetition or type, you can alter the makeColorGradient function to your needs. This page offers some good explaination how gradients are generated and where my example is based upon: http://krazydad.com/tutorials/makecolors.php.
Related
With Three.js I want to create the effect of an object swinging from a cable or rope. It doesn't require real physics as the "swinging" object simply follows a fixed animation. The easiest solution is using the THREE.Line, however the problem is that THREE.Line can only be 1px thick and looks kinda awful.
In the three.js examples there is a "fat lines" example :
https://threejs.org/examples/?q=lines#webgl_lines_fat
however the problem is that once I have created the line using LineGeometry() I cannot figure out how to animate it.
The only solution I have found so far is to delete then create a new line every single frame, which works but seems like a really uneconomical, poorly optimized way to do it.
Does anyone know of a better way to either animate Line Geometry without having to delete and replace each frame? Or is there another method within three.js which would allow me to create thicker animated lines?
Thanks!!
I actually have a small project where I animate a bulb swinging along some rope. You can access it here, the functions I'm talking about below are in helperFns.js.
Actually, what I basically do is create my attached object separately :
let geometry = new THREE.SphereGeometry( 1, 32, 32 );
var material = new THREE.MeshStandardMaterial({color:0x000000,emissive:0xffffff,emissiveIntensity:lightIntensity});
bulb = new THREE.Mesh( geometry, material );
light = new THREE.PointLight(0xF5DCAF,lightIntensity,Infinity,2)
light.power = lightIntensity*20000
light.position.set(0,length*Math.sin(theta),z0-length*Math.cos(theta))
light.add(bulb)
light.castShadow = true;
hemiLight = new THREE.HemisphereLight( 0xddeeff, 0x0f0e0d, 0.1 );
scene.add(hemiLight)
scene.add(light)
I then add a spline connected to it :
// Create the wire linking the bulb to the roof
var curveObject = drawSpline(light.position,{x:0,y:0,z:z0},0xffffff);
scene.add(curveObject)
Where drawSpline is the following function :
// Build a spline representing the wire between the roof and the bulb. The new middle point is computed as the middle point shifted orthogonally from the lign by shiftRatio
function drawSpline(beginning,end,clr){
// Compute y sign to know which way to bend the wire
let ySign = Math.sign((end.y+beginning.y)/2)
// Compute the bending strength and multiply per Math.abs(beginning.y) to ensure it decreases as the bulb gets closer to the theta = 0 position, and also to ensure
// that the shift is null if thete is null (no discontinuity in the wire movement)
let appliedRatio = -shiftRatio*Math.abs(beginning.y)
// Compute middle line position vector and the direction vector from the roof to the bulb
let midVector = new THREE.Vector3( 0, (end.y+beginning.y)/2, (end.z+beginning.z)/2 )
let positionVector = new THREE.Vector3(0,end.y-beginning.y,end.z-beginning.z)
// Compute the orthogonal vector to the direction vector (opposite sense to the bending shift)
let orthogVector = new THREE.Vector3(0,positionVector.z,-positionVector.y).normalize()
// Compute the curve passing by the three points
var curve = new THREE.CatmullRomCurve3( [
new THREE.Vector3( beginning.x, beginning.y, beginning.z ),
midVector.clone().addScaledVector(orthogVector,ySign*appliedRatio),
new THREE.Vector3( end.x, end.y, end.z ),
]);
// Build the curve line object
var points = curve.getPoints( 20 );
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : clr } );
// Create the final object to add to the scene
var curveObject = new THREE.Line( geometry, material );
return curveObject;
}
It creates the CatmullRomCurve3 interpolating the 3 points (one fix end at (0, 0, 0), one middle point to apply the bend, and the bulb position. You can actually start with a straight line, and then try to compute some curve.
To do so, you want to get the vector orthogonal to the line and shift the line (on the good side) along this vector.
And finally, at each animate() call, I redraw the spline for the new position of the bulb :
scene.children[2] = drawSpline(light.position,{x:0,y:0,z:z0},0xffffff)
Tell me if there is a point you do not get, but it should help for your problem.
Just wanted to post a more detailed version of West Langleys great reply. To animate a THREE Line2 you need to use the commands :
line.geometry.attributes.instanceStart.setXYZ( index, x, y, z );
line.geometry.attributes.instanceEnd.setXYZ( index, x, y, z );
What confused me was the index value - rather than thinking about a Line2 as being vertex points (the method used for creating the line) you need to think of a Line2 as being made of separate individual lines between 2 sets of points... so each line has a Start point and and an End point.
A "W" is therefore NOT defined as 5 vertices but by 4 lines. So you can "split" a Line2 by setting a different Start point to the previous lines End point. The index is the number of lines that make up your object. In my case I have two lines forming a V shape... so I set my index to 1 to affect the end of line 0 and the start of line 1, as in West's example :
var index = 1;
line.geometry.attributes.instanceEnd.setXYZ( index - 1, x, y, z );
line.geometry.attributes.instanceStart.setXYZ( index, x, y, z );
And then you just need to update the line using :
line.geometry.attributes.instanceStart.data.needsUpdate = true;
Thanks again to West for this really useful answer. I'd never have guessed this as you cannot see these variables when you look at the Line2 object properties. Very useful info. I hope it helps someone else at some point.
I have been creating a simple Three.js application and so far I have a centered text in the scene that shows "Hello World". I have been copying the code examples to try and understand what is happening and so far Ihave it working but I am failing to completely understand why.
My confusion comes from reading all the Three.js tutorials describing that a Geometry object is responsible for creating the shape of the object in the scene. Therefore I did not think it would not make sense to have a position on something that is describing the shape of the mesh.
/* Create the scene Text */
let loader = new THREE.FontLoader();
loader.load( 'fonts/helvetiker_regular.typeface.json', function (font) {
/* Create the geometry */
let geometry_text = new THREE.TextGeometry( "Hello World", {
font: font,
size: 5,
height: 1,
});
/* Create a bounding box in order to calculate the center position of the created text */
geometry_text.computeBoundingBox();
let x_mid = geometry_text.boundingBox.max.x - geometry_text.boundingBox.min.x;
geometry_text.translate(-0.5 * x_mid, 0, 0); // Center the text by offsetting half the width
/* Currently using basic material because I do not have a light, Phong will be black */
let material_text = new THREE.MeshBasicMaterial({
color: new THREE.Color( 0x006699 )
});
let textMesh = new THREE.Mesh(geometry_text, material_text);
textMesh.position.set(0, 0, -20);
//debugger;
scene.add(textMesh);
console.log('added mesh')
} );
Here is the code that I use to add to shape and my confusion comes from the following steps.
/* Create a bounding box in order to calculate the center position of the created text */
geometry_text.computeBoundingBox();
let x_mid = geometry_text.boundingBox.max.x - geometry_text.boundingBox.min.x;
geometry_text.translate(-0.5 * x_mid, 0, 0); // Center the text by offsetting half the width
First, we translate the geometry to the left to center the text inside the scene.
let textMesh = new THREE.Mesh(geometry_text, material_text);
textMesh.position.set(0, 0, -20);
Secondly, we set the position of the mesh.
My confusion comes from the fact that we need both of these operations to occur to move the mesh backwards and become centered.
However I do not understand why these operations should be done of the geometry, infact what confuses me more is that why does textMesh.position.set(0, 0, -20); not override my previously performed translation and simply move the mesh to (0,0,-20). removing my previous translation. It seems that both are required.
AFAIK it is recommended (in scenegraph) to transform (translate, rotate, scale) the whole mesh (with simple) rather than prepare transformed geometry and use it to create "untransformed" mesh, since the mesh in second case is not transform-friendly. Basically "cumulative" transform will be just illegal, giving wrong, unexpected results. Even simple movement.
But sometimes it is useful to create transformed geometry and use it for some algos/computations or in meshes.
You are getting somehow "expected" results in your "combined transform" case because it is just particular case (for example it can work only if object position is (0, 0, 0) etc)
mesh.position.set doesn't modify geometry: it is only a property of mesh and it is used to compute final mesh triangles. This computation involves geometry and object matrix which is composed from object position, object quaternion (3D-rotation) and object scale. Object's geometry can be modified by "matrix" operations but none of such operations are performed dynamically by mesh.
I'm trying to make a linear regression plane visualization tool for a math project. Currently I have the math parts completed, but I am not sure how to graph the plane. I have a equation in the form of z=C+xD+yE, where C, D, and E are known constants. How do I graph the plane using these information? Thanks.
github page: https://saxocellphone.github.io/LAProject/
z=C+xD+yE
This equation gives full information about the plane. What else you need to graph (plot, draw?) it? Probably it depends on your graphic software possibilities.
Canonical form of given equation:
xD + yE - z + C = 0
Normal to the plane is (D, E, -1). Distance to the coordinate origin Abs(C)/Sqrt(D^2+E^2+1).
Plane intersects coordinate axes at values (-C/D), (-C/E), (C)
I see your problem is not with math, but with three,
as WestLangley pointed out in his comment you can play with rotations etc. or create a simple triangle which is the easiest way
since you have your equation for the plane create 3 points to form a triangle
// z=C+xD+yE
// i assume here that the plane is not aligned with any axis
// and does not pass through the origin, otherwise choose the points in another way
var point1 = new THREE.Vector3(-C/D,0,0);//x axis intersection
var point2 = new THREE.Vector3(0,-C/E,0);//y axis intersection
var point3 = new THREE.Vector3(0,0,C);//z axis intersection
now form a new geometry as in How to make a custom triangle in three.js
var geom = new THREE.Geometry();
geom.vertices.push(point1);// adding vertices to geometry
geom.vertices.push(point2);
geom.vertices.push(point3);
// telling geometry that vertices 0,1,2 form a face = triangle
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
create a simple material and add it to a scene
var material = new THREE.MeshBasicMaterial({
color: 0xff0000, // RGB hex color for material
side: THREE.DoubleSide // do not hide object when viewing from back
});
scene.add(new THREE.Mesh(geometry,material));
that should get you going, you can add another triangles, or make it larger with choosing points that are further apart
I would like to know how threejs ordering multiple matrix?
For instance ,
......
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 0, 20, 0 ); // T , transform matrix
mesh.rotation.set( 0, Math.PI, 0 );//R , rotation matrix
mesh.scale.set( 1, 1, 10 );//S , scale matrix
So , how threejs to combine three matrix? It will according my set value order(In my example , it is TRS , so final matrix should T*R*S ) or fixed order (For instance, it is always using SRT ordering , the finally matrix is S*R*T)?
You can specify the parameters as you have done so long as the mesh property 'matrixAutoUpdate' is true and you only want it in order by scale, rotation then translation (typical).
If you need something more complicated, such as a specific series of translations and rotations, then you should set matrixAutoUpdate to false and calculate the matrix yourself.
Important note: when using the matrix multiply function, you must do it in 'reverse' order. For example if you want to position a limb then logically you would:
1) do local rotation (such as arm up/down)
2) move limb with respect to its offset (put it at its joint)
3) rotate according to body rotation
4) move to body offset
The actual operations would be done in reverse:
arm_mesh.matrixAutoUpdate = false;
var mat4: THREE.Matrix4 = new THREE.Matrix4();
var arm_matrix = arm_mesh.matrix;
arm_matrix.identity(); // reset
arm_matrix.multiply(mat4.makeTranslation(body_pos.x, body_pos.y, body_pos.z));
arm_matrix.multiply(mat4.makeRotationFromQuaternion(body_rotation));
arm_matrix.multiply(mat4.makeTranslation(arm_offset.x, arm_offset.y, arm_offset.z));
arm_matrix.multiply(mat4.makeRotationFromEuler(new THREE.Euler(arm_angle, 0, 0)));
Of course this does not utilize or account for parent/child relationships.
I have multiple WebGL lines to render, and they all have the same rendering style. So for performance, I want to render them all as a single object, in a single draw call.
But the problem is that the lines don't all connect to each other.
See example here: http://jsfiddle.net/b6jgS/6/
As you can see the rings connect, but I don't want them to. Yet I still want to draw them in a single draw call.
The relevant code is this, which simply generates some geometry for some rings:
# Pardon the coffeescript!
ringsGeom = new THREE.Geometry()
for u in [-2..2]
for v in [0..100]
ringsGeom.vertices.push new THREE.Vector3(
Math.sin(v/100 * 2 * Math.PI)
Math.cos(v/100 * 2 * Math.PI)
u
)
rings = new THREE.Line(
ringsGeom
new THREE.LineBasicMaterial(
color: 0xffff00
linewidth: 1
)
)
scene.add rings
How do I have a single object draw multiple discreet disconnected lines?
You construct your geometry as an array of pairs of points representing individual line segments, and then create your line like so:
var line = new THREE.Line( geometry, material, THREE.LinePieces );
For an example, see GridHelper.js.
three.js r.58
P.S. three.js includes the requestAnimationFrame() shim. You do not need to include it yourself.