ThreeJS Points (Point Cloud) with Lighting using custom Shader Material

1.4k Views Asked by At

Coded using:

  • Using ThreeJS v0.130.1
  • Framework: Angular 12, but that's not relevant to the issue.
  • Testing on Chrome browser.

I am building an application that gets more than 100K points. I use these points to render a THREE.Points object on the screen.

I found that default THREE.PointsMaterial does not support lighting (the points are visible with or without adding lights to the scene).

So I tried to implement a custom ShaderMaterial. But I could not find a way to add lighting to the rendered object.

Here is a sample of what my code is doing: Sample App on StackBlitz showing my current attempt In this code, I am using sample values for point cloud data, normals and color but everything else is similar to my actual application. I can see the 3D object, but need more proper lighting using normals.

I need help or guidance to implement the following:

  1. Add lighting to custom shader material. I have Googled and tried many things, no success so far.

  2. Using normals, show the effects of lighting (In this sample code, the normals are fixed to Y-axis direction, but I am calculating them based on some vector logic in actual application). So calculating normals is already done, but I want to use them to show light shine/shading effect in the custom shader material.

And in this sample, color attribute is set to fixed red color, but in actual application I am able to apply colors using UV range from a texture to color attribute.

Please advise how/if I can get lighting based on normals for Point Cloud. Thanks.

Note: I looked at this Stackoveflow question but it only deals with changing the alpha/transparency of points and not lighting.

1

There are 1 best solutions below

3
On BEST ANSWER

Adding lighting to a custom material is a very complex process. Especially since you could use Phong, Lambert, or Physical lighting methods, and there's a lot of calculations that need to pass from the vertex to the fragment shader. For instance, this segment of shader code is just a small part of what you'd need.

Instead of trying to re-create lighting from scratch, I recommend you create a PlaneGeometry with the material you'd like (Phong, Lambert, Physical, etc...) and use an InstancedMesh to create thousands of instances, just like in this example.

Based on that example, the pseudo-code of how you could achieve a similar effect is something like this:

const count = 100000;
const geometry = new PlaneGeometry();
const material = new THREE.MeshPhongMaterial();

mesh = new THREE.InstancedMesh( geometry, material, count );
mesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
scene.add( mesh );

const dummy = new THREE.Object3D();
update() {
    // Sets the rotation so it's always perpendicular to camera
    dummy.lookAt(camera);

    // Updates positions of each plane
    for (let i = 0; i < count; i++){
        dummy.position.set( x, y, z );

        dummy.updateMatrix();

        mesh.setMatrixAt( i ++, dummy.matrix );
    }
}

The for() loop would be the most expensive part of each frame, so if you need to update it on each frame, you might want to calculate this in the vertex shader, but that's another question altogether.