DEV Community

Cover image for Infinite Lights
Sharon Anderson
Sharon Anderson

Posted on

Infinite Lights

Through this article, we’ll use Three.js and learn how to:
instantiate geometries to create thousands (up to millions) of lights.

  • Preparing the road and camera

To begin we’ll create a new "Road" class to encapsulate all the logic for our plane. It’s going to be a basic "PlaneBufferGeometry" with its height being the road’s length.

We want this plane to be flat on the ground and go further away. But Three.js creates a vertical plane at the center of the scene. We’ll rotate it on the x-axis to make it flat on the ground (y-axis).

We’ll also move it by half its length on the z-axis to position the start of the plane at the center of the scene.

We’re moving it on the z-axis because position translation happens after the rotation. While we set the plane’s length on the y-axis, after the rotation, the length is on the z-axis.

  • Instantiating the lights

Although some lights are longer or thicker than others, they all share the same geometry. Instead of creating a bunch of meshes for each light, and causing lots of draw calls, we can take advantage of instantiation.

Instantiation is the equivalent of telling WebGL “Hey buddy, render this SAME geometry X amount of times”. This process allows you to reduce the amount of draw calls to 1.

Although it’s the same result, rendering X objects, the process is very different. Let’s compare it with buying 50 chocolates at a store:

A draw call is the equivalent of going to the store, buying only one chocolate and then coming back. Then we repeat the process for all 50 chocolates. Paying for the chocolate (rendering) at the store is pretty fast, but going to the store and coming back (draw calls) takes a little bit of time. The more draw calls, the more trips to the store, the more time.

With instantiation, we’re going to the store and buying all 50 chocolates and coming back. You still have to go and come back from the store (draw call) one time. But you saved up those 49 extra trips.

A fun experiment to test this even further: Try to delete 50 different files from your computer, then try to delete just one file of equivalent size to all 50 combined. You’ll notice that even though it’s the same combined file size, the 50 files take more time to be deleted than the single file of equivalent size 😉

Coming back to the code: to instantiate we’ll copy our tubeGeometry over to an InstancedBufferGeometry. Then we’ll tell it how many instances we’ll need. In our case, it’s going to be a number multiplied by 2 because we want two lights per “car”.

Next we’ll have to use that instanced geometry to create our mesh.

Although it looks the same, Three.js now rendered 100 tubes in the same position. To move them to their respective positions we’ll use an InstancedBufferAttribute.

While a regular BufferAttribute describes the base shape, for example, it’s position, uvs, and normals, an InstanceBufferAttribute describes each instance of the base shape. In our case, each instance is going to have a different aOffset and a different radius/length aMetrics.

When it’s time each instance passes through the vertex shader. WebGL is going to give us the values corresponding to each instance. Then we can position them using those values.

  • Positioning the lights

We want to have two roads of lights coming from different directions. Let’s create the second TailLights and move each to their respective position. To center them both, we’ll move them by half the middle island’s width and half the road’s width.

This article was inspired by Epic Programmer here is the original article :- https://www.epicprogrammer.com/2021/11/infinite-lights-with-threejs.html

Top comments (0)