How to animate THREE.Line objects from startPoint to endPoint?

Hi all, I’m struggling over a month now to figure out how to simultaneously animate multiple lines (Ray Tracing Animation). So I’m trying to shoot lines from a start point (0,500,0) and create a pyramid of lines using a nested for loop. The problem is that I add all the frames in the scene, instead of animating them.

I used several examples I found online such as increasing fraction/geometry.dashSize and more but its all confusing for me.

A demo is shown in this link.

And the code I use is below:

FRT: function() {
            return {
                forward_RT: function(){

                    //Get the position of the spotLight
                    var spotLightPosition = new THREE.Vector3(spotLight.position.x, spotLight.position.y, spotLight.position.z);
                    var f_ray_endPoint_FIRST = new THREE.Vector3(-490, 0, 495); 

                    //Declare the start and end points of the first ray (startPoint never changes )
                    var f_ray_startPoint = spotLightPosition;
                    var f_ray_endPoint = f_ray_endPoint_FIRST;

                    //Declare ray geometry
                    var ray_geometry = new THREE.Geometry();
                    ray_geometry.vertices.push( f_ray_startPoint );
                    ray_geometry.vertices.push( f_ray_endPoint   );

                    //Declare values for 2d array grid
                    var rows = 18; //Number of rows 
                    var cols = 18; //Number of columns
                    var rayCounter = 0; //Counts the number of fowrward Rays
                    var rayOffset = 55; //Offset of ray every iteration

                    /* FOR EVERY ROW IN THE 2D ARRAY */
                    for(var x=0; x<rows; x++){

                        //Create a 2D Array every row
                        f_Ray_List[x] = []; 
                        forward_rays_distances[x] = []; 

                        /* FOR EVERY COLUMN IN THE 2D ARRAY */
                        for(var z=0; z<cols; z++){

                            //Reposition or add offset for every iteration
                            if(f_ray_endPoint.x >= 500){
                                //Reposition 1st index of [i][j]
                                f_ray_endPoint.x -= (cols * rayOffset); 
                            }
                            else { 
                                //Add offset every iteration
                                f_ray_endPoint.x += rayOffset;
                            }

                            //Decalere a new Line Object
                            var f_ray = new THREE.Line(ray_geometry.clone(), ray_material_red);
                            f_ray.geometry.vertices[1].x = f_ray_endPoint.x;
                            f_ray.geometry.vertices[1].y = f_ray_endPoint.y;
                            f_ray.geometry.vertices[1].z = f_ray_endPoint.z;
                            f_ray.material.dashSize = 0.01;
                            console.log(f_ray);

                            //Calculate distance of ray's end Position from the center
                            rayStart.set(f_ray_startPoint.x, f_ray_startPoint.z);
                            rayEnd.set(f_ray_endPoint.x, f_ray_endPoint.z);
                            distanceFromCenter = rayEnd.distanceTo(rayStart);// Calculate distance to center

                            // Skip loop if distance to center is bigger than radius
                            if (distanceFromCenter > spotLight_Radius) {
                                f_ray.material = ray_material_black;
                                f_Ray_List[x][z] = f_ray; //Add in 2D Array
                                scene_Main.add(f_Ray_List[x][z]);

                            } else {
                                // Draw ray to x, z
                                f_Ray_List[x][z] = f_ray; //Add in 2D Array
                                scene_Main.add(f_Ray_List[x][z]);
                            }
                            rayCounter++; //Counts how many rays
                        }

                        //Add Ofset on Y Axis every row itteration
                        f_ray_endPoint.z -= rayOffset; //Add offset for ray position on Y Axis
                    }
                }
            };
        }

So my question is, what is the best way to animate the lines since I have the start and end points:

  • rayStart = (f_ray_startPoint.x, f_ray_startPoint.z)
  • rayEnd = (f_ray_endPoint.x, f_ray_endPoint.z)

It’s good to mention that I want to access all the values because I then want to make the rays bounce from the camera and MAYBE end up in the camera. If you have any suggestions for rays bouncing please let me know and thanks for your time.

Have you read this?
https://threejs.org/docs/#manual/en/introduction/How-to-update-things

More specifically, you want to update the geometry buffer that contain the vertex data.

Are you looking for something like this: Drawing with THREE.LineDashedMaterial(), Line animations ?

Hi yombo, I think the documentation is terrible in my opinion. I can’t understand where to use which flag in this case.

Hi,
Sorry I thought you were using BufferGeometry.
The documentation is clear IMHO. To update the Geometry, just do something like:

ray_geometry.vertices[ 0 ].set( ..., ..., ... );
ray_geometry.vertices[ 1 ].set( ..., ..., ... );
ray_geometry.verticesNeedUpdate = true;

Do it each time you want to update a line. Of course the line can have more than two vertices.

Note: It is better to use BufferGeometry, though it is more difficult. Geometry will be not renderable in some future Three.js version.

1 Like

May I ask if I got it right? So instead of doing this:

//Declare ray geometry
var ray_geometry = new THREE.Geometry();
ray_geometry.vertices.push( f_ray_startPoint );
ray_geometry.vertices.push( f_ray_endPoint   );

I should be doing this, and then work based on the link you have provided right?

var ray_BufferGeometry = new THREE.BufferGeometry();
ray_BufferGeometry.vertices[0].set(f_ray_startPoint);
ray_BufferGeometry.vertices[1].set(f_ray_endPoint);
ray_BufferGeometry.verticesNeedUpdate = true;

Well, you have to do both things. At the start create the lines once, and keep references to them so you can later update them in each frame.

2 Likes

This way:

//During creation
//Declare ray geometry
var ray_geometry = new THREE.Geometry();
ray_geometry.vertices.push( f_ray_startPoint );
ray_geometry.vertices.push( f_ray_endPoint   );

...
// During animate()
ray_BufferGeometry.vertices[0].copy(f_ray_startPoint);
ray_BufferGeometry.vertices[1].copy(f_ray_endPoint);
ray_BufferGeometry.verticesNeedUpdate = true;

Note, is is .copy, not .set

Are you sure it will work for a buffer geometry? All the data of vertices is stored in a buffer attribute .position.

Something like this should work:

ray_BufferGeometry.attributes.position.setXYZ(0, f_ray_startPoint.x, f_ray_startPoint.y, f_ray_startPoint.z);
ray_BufferGeometry.attributes.position.setXYZ(1, f_ray_endPoint.x, f_ray_endPoint.y, f_ray_endPoint.z);

or

f_ray_startPoint.toArray(ray_BufferGeometry.attributes.position.array, 0);
f_ray_endPoint.toArray(ray_BufferGeometry.attributes.position.array, 3);

Of couse, but the OP is using Geometry. You should create the BufferGeometry first (then update it).

@yombo I thought he’s already using it :slight_smile:

@prisoner849 sorry I only see Geometry in the code snippet.

This is also needed:
ray_BufferGeometry.attributes.position.needsUpdate = true;

1 Like

I suggest using a THREE.LineSegments object, and specify all lines in one THREE.BufferGeometry object.

The following code is untested, but sketches how you can achieve the desired animation.

let geom = new THREE.BufferGeometry();
geom.addAttribute(
    "position",
    new THREE.BufferAttribute(new Float32Array(6*LINE_COUNT), 3)
);
let mat = new THREE.LineBasicMaterial();
let lines = new THREE.LineSegments(geom, mat);
scene.add(lines);
//To update:
let pos = geom.getAttribute("position");
let pa = pos.array;
for (let line_index= 0; line_index < LINE_COUNT; line_index++) {
    //coordinates of line start (if different than before)
    pa[6*line_index] = x_start;
    pa[6*line_index+1] = y_start;
    pa[6*line_index+2]= z_start;
    //coordinates of line end, typically from THREE.Raycaster
    pa[6*line_index+4] = x_end;
    pa[6*line_index+5] = y_end;
    pa[6*line_index+6] = z_end;
}
pos.needsUpdate = true;
//then render, if not on every frame

As an alternative to specifying the coordinates one by one, if line_start and line_end are arrays of THREE.Vector3s, you can use line_start[line_index].toArray(pa, 6*line_index) and line_end[line_index].toArray(pa, 6*line_index+3), respectively.

If you want to add one level of specularly reflected rays, you can have the position array twice the length and update the reflected rays in the same loop, typically using normal information from the Raycaster to bounce another ray in the reflected direction.

Individual line coloring should be possible using vertex colors. Add an attribute named “color”, with attribute length 3, then modify it using r,g,b (in [0,1]) as x,y,z, usually with the same colors for both ends of a line (but I am almost sure it will interpolate otherwise). You will have to specify a vertexColors argument to the material. From the docs:

.vertexColors : Integer

Defines whether vertex coloring is used. Default is THREE.NoColors. Other options are THREE.VertexColors and THREE.FaceColors.

https://threejs.org/docs/#api/en/objects/LineSegments
https://threejs.org/docs/#api/en/core/BufferGeometry
https://threejs.org/docs/#api/en/core/Raycaster

1 Like