Multiple moving spotlight with GLSL shaders

I’m trying to achieve multiple spotlights that move across each mesh material individually, illuminating the specific areas of the bump map in each spotlight’s path.

Currently, a single spotlight moves across the mesh and highlights the bump map area within the spotlight’s effect.

live code : Glitch :・゚✧

What’s the question? What have you tried and where did you get stuck?

1 Like

I have passed the uLightPosArray as following in shaderMaterial. when I am trying to access that array in fragment shader, spotlight isn’t getting generated. even though for testing purpose I have passed the same object as the uLightPos object (by using that spotlight is correctly generated) in array still spotlight isn’t getting generated.

script.js

 uLightPosArray: {
          value: [
            new THREE.Vector3(
              surfaceData["position-x"],
              surfaceData["position-y"],
              surfaceData["position-z"]
            ),
            new THREE.Vector3(
              surfaceData["position-x"],
              surfaceData["position-y"]+1,
              surfaceData["position-z"]
            ),
          ],
        },



const shaderMaterial = new THREE.ShaderMaterial({
      uniforms: {
        textureArray1: { value: paddedTextures1 }, // Main textures 1
        bumpTextureArray1: { value: paddedCarvingTextures1 }, // Bump textures 1
        textureArray2: { value: paddedTextures2 }, // Main textures 2
        bumpTextureArray2: { value: paddedCarvingTextures2 }, // Bump textures 2
        tileWidth: { value: tiWidthM },
        tileHeight: { value: tiHeightM },
        planeWidth: { value: planeWidthM },
        planeHeight: { value: planeHeightM },
        maxTiles1: { value: maxTiles1 },
        maxTiles2: { value: maxTiles2 },
        layoutType: { value: layoutType },
        scaleFactor: { value: scaleFactor / zoomFactor }, // Scale for texture repeat
        groutColor: { value: parseRGBA(groutColor) },
        groutWidth: { value: groutWidth },
        uLightPos: {
          value: new THREE.Vector3(
            surfaceData["position-x"],
            surfaceData["position-y"],
            surfaceData["position-z"]
          ),
        },
        uLightPosArray: {
          value: [
            new THREE.Vector3(
              surfaceData["position-x"],
              surfaceData["position-y"],
              surfaceData["position-z"]
            ),
            new THREE.Vector3(
              surfaceData["position-x"],
              surfaceData["position-y"]+1,
              surfaceData["position-z"]
            ),
          ],
        },
        uSpotlightSize: { value: 10 },
        spotlightEnabled: { value: true },
      },
      vertexShader,
      fragmentShader,
      side: THREE.DoubleSide,
    });

fragmentShader.js

   // vec3 spotLightObject = uLightPosArray[0]; //trying to access array object here
     // float distToLight = length(vUv.xy - spotLightObject.xy);
     // distToLight *= 0.0004;
    float distToLight = length(vUv.xy - uLightPos.xy);
    
    // Spotlight falloff factor (smoothstep gives smooth edges)
    float spotlightFactor = smoothstep(uSpotlightSize * 2.0, uSpotlightSize, distToLight);

I am not able to use the passed array in shader and I am stuck in generating multiple spotlight on each mesh. if anyone can suggest a method or way by which we can generate a fix number of spotlight for each mesh (i.e. maxSpotlights : 3), and that spotlight movement can be handled on the mesh from animate() function.

note : spotlight is working correctly when single position is passed, and movement of that is managed in animate function of script.js

function animate() {
  requestAnimationFrame(animate);

  uLightPosX += velocityX;
  uLightPosY += velocityY;

  // Ensure spotlight stays within UV bounds
  if (uLightPosX <= 0 || uLightPosX >= 1) {
    velocityX = -velocityX;
  }
  if (uLightPosY <= 0 || uLightPosY >= 1) {
    velocityY = -velocityY;
  }

  scene.children.forEach((mesh) => {
    if (
      mesh.material &&
      mesh.material.uniforms &&
      mesh.material.uniforms.uLightPos
    ) {
      // Update spotlight position dynamically
      mesh.material.uniforms.uLightPos.value.set(uLightPosX, uLightPosY, 0);
      mesh.material.uniforms.uLightPos.needsUpdate = true;

      // Update spotlight size (optional)
      mesh.material.uniforms.uSpotlightSize.value = spotlightEnabled
        ? spotlightSize
        : 0;
      mesh.material.uniforms.uSpotlightSize.needsUpdate = true;
    }
  });

  controls.update(); // Update controls

  renderer.render(scene, camera);
}

You’re using version 84 of THREE.js, current version is 170. I’d suggest updating to the latest version, your code is small enough so it shouldn’t take too long.

1 Like

Is that version now all nodes? Maybe better to 163 or something like that that still has glsl

AFAIK, no. You can use materials (not with Node in the name), like you did before, including .onBeforeCompile(). :thinking:

1 Like

I have use version 163 of threejs.

 <!-- Import map for ES6 modules -->
    <script type="importmap">
      {
        "imports": {
          "three": "https://unpkg.com/three@0.163.0/build/three.module.js"
        }
      }
    </script>

I have removed the import of v84 script from index.html. I missed to remove that import previously.
with newer version of threejs, is it possible to access the positionArray in shaderMaterial ?

I understood a bit. I was using uLightPosArray for spotlight in fshader. and updating the uLightPos in animate function. that’s why it was not working.

  // Calculate distance to spotlight for spotlight effect
     vec3 spotLightObject = uLightPosArray[0]; //trying to access array object here
     float distToLight = length(vUv.xy - uLightPos.xy);
     //float distToLight = length(vUv.xy - spotLightObject.xy);
    
    // Spotlight falloff factor (smoothstep gives smooth edges)
    float spotlightFactor = smoothstep(uSpotlightSize * 2.0, uSpotlightSize, distToLight);

script.js

  requestAnimationFrame(animate);

  // uLightPosX += velocityX;
  // uLightPosY += velocityY;

  // Ensure spotlight stays within UV bounds
  if (uLightPosX <= 0 || uLightPosX >= 1) {
    velocityX = -velocityX;
  }
  if (uLightPosY <= 0 || uLightPosY >= 1) {
    velocityY = -velocityY;
  }

  scene.children.forEach((mesh) => {
    if (
      mesh.material &&
      mesh.material.uniforms &&
      mesh.material.uniforms.uLightPos
    ) {
      // Update spotlight position dynamically
      mesh.material.uniforms.uLightPos.value.set(uLightPosX, uLightPosY, 0);
      mesh.material.uniforms.uLightPos.needsUpdate = true;

      // Update spotlight size (optional)
      mesh.material.uniforms.uSpotlightSize.value = spotlightEnabled
        ? spotlightSize
        : 0;
      mesh.material.uniforms.uSpotlightSize.needsUpdate = true;
    }
  });

please can anyone guide, how can we manage mesh update using that array & how can we generate spotlight using all values of array in shader ?

I’m still stuck on this issue :frowning: can anyone provide guidance?