Add shadow support for ShaderMaterial from Google 3D Tiles

Hi everyone! I would be grateful for any help in my question.
I load the Google 3D Tiles model, but it comes with the material of ShaderMaterial, without the support of the shadows. I was thinking add shadow support for 2 ways - by adding GLSL code to shaders to support shadows and 2 option is replacing the material with MeshStandardMaterial and adding shaders from the 3d Tiles material.
According to the second option, it was possible to change the material, but the shadows are not displayed. What could my miss? I have commented shaders code from 3d tiles material. Because with this code map is not visible. With this code map is visible


const fetchGoogleModel = async () => {
        setLoading(true);
        const model = await fetchDSM(quoteDetails, lat, lng, radius);
        // add shadow support for google models
        if (model) {
        model.traverse(child => {
          if (child.isMesh && child.material instanceof THREE.ShaderMaterial) {
            child.castShadow = true;
            child.receiveShadow = true;
            const oldUniforms = {
              center: child.material.uniforms.center,
              rotation: child.material.uniforms.rotation,
              mirrorX: child.material.uniforms.mirrorX,
              mirrorY: child.material.uniforms.mirrorY,
              map: child.material.uniforms.map,
            };
            const oldVertexShader = child.material.vertexShader;
            const oldFragmentShader = child.material.fragmentShader;
            const newMaterial = new THREE.MeshStandardMaterial({
              // alphaTest: 0.5,
              // transparent: true,
            });

            newMaterial.onBeforeCompile = (shader) => {
              shader.uniforms.map = oldUniforms.map;
              shader.uniforms.center = oldUniforms.center;
              shader.uniforms.rotation = oldUniforms.rotation;
              shader.uniforms.mirrorX = oldUniforms.mirrorX;
              shader.uniforms.mirrorY = oldUniforms.mirrorY;

              // shader.fragmentShader = oldFragmentShader;
              // shader.fragmentShader.replace(
              //     "void main() {",
              //     `
              //     uniform vec2 center;
              //     uniform float rotation;
              //     uniform bool mirrorX;
              //     uniform bool mirrorY;
              //     uniform sampler2D map;
              //     varying vec2 vUv; // если GLSL 1.0, или in vec2 vUv; если WebGL2

              //     void main() {
              //       vec2 uv = vUv;
  
              //       // Apply mirroring
              //       if (mirrorX) {
              //           uv.x = center.x - (uv.x - center.x);
              //       }
              //       if (mirrorY) {
              //           uv.y = center.y - (uv.y - center.y);
              //       }
        
              //       // Apply rotation
              //       float cosTheta = cos(rotation);
              //       float sinTheta = sin(rotation);
              //       uv -= center; // Translate UV to the center for rotation
              //       uv = vec2(
              //           uv.x * cosTheta - uv.y * sinTheta,
              //           uv.x * sinTheta + uv.y * cosTheta
              //       );
              //       uv += center; // Translate UV back to its original position
        
              //       vec4 color = texture2D(map, uv);
              //       gl_FragColor = color;
              //     `
              //   );

            // shader.vertexShader = oldVertexShader;
            //  shader.vertexShader.replace(
            //   "void main() {",
            //   `varying vec2 vUv;\nvoid main() {
            //   gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );`
            // );
            };
            child.material = newMaterial;
            const depthMaterial = new THREE.MeshDepthMaterial({
              depthPacking: THREE.RGBADepthPacking,
              alphaTest: 0.5,
            });
            depthMaterial.transparent = true;

            const distanceMaterial = new THREE.MeshDistanceMaterial({
              alphaTest: 0.5,
            });
            distanceMaterial.transparent = true;

            child.customDepthMaterial = depthMaterial;
            child.customDistanceMaterial = distanceMaterial;
          }
        });
        
      }```

I would suggest posting the vertexShader and fragmentShader of the
child.material here and we might be able to tell why it doesn’t work.

FragmentShader and VetexShader from Child.material are in the code, they are commented.

Can you remove the custumDepth/Distance and just put a regular StandardMaterial with no onBeforeCompile?

Also perhaps collect the meshes, and add a cube with the same StandardMaterial + castShadow+receiveShadow = true
so you can see if shadows are just not setup up correctly globally?

(Make sure this setup is happening for your renderer/light:)

I didnt mention, that shadow are setup correctly globally because they are working good with another object.

Can you remove the custumDepth/Distance and just put a regular StandardMaterial with no onBeforeCompile

Then there will be just geometry without texture and yes, the shadows work on it. Google Tiles texture comes to Uniforms.map with ShaderMaterial. And installed through FragmentShader

So just adding this:



            newMaterial.onBeforeCompile = (shader) => {
              shader.uniforms.map = oldUniforms.map;
              shader.uniforms.center = oldUniforms.center;
              shader.uniforms.rotation = oldUniforms.rotation;
              shader.uniforms.mirrorX = oldUniforms.mirrorX;
              shader.uniforms.mirrorY = oldUniforms.mirrorY;
}

causes the shadows to break?

I have checked now again. Without customDepth/Distance materials and with this

            newMaterial.onBeforeCompile = (shader) => {
              shader.uniforms.map = oldUniforms.map;
              shader.uniforms.center = oldUniforms.center;
              shader.uniforms.rotation = oldUniforms.rotation;
              shader.uniforms.mirrorX = oldUniforms.mirrorX;
              shader.uniforms.mirrorY = oldUniforms.mirrorY;
}

shadows still working. But without textuer. If I will add fragment and vertext shaders with replace func texture does not work but shadows works. If I change shaders this way shader.fragmentShader = oldFragmentShader;
then textures works but not the shadows:))

I think that it is necessary to configure something in Meshstandardmaterial so that the textures from Uniforms work.

Instead of swapping out the map uniform..

Try just copying material.map = shaderMaterial.uniforms.map.value
before the onbeforecompile…

See if that makes textures come back?

In this case, the texture is applying with a distortion, with a displacement. Textures from Google Tiles have a displacement, rotations, mirrorX/Y settings in uniforms. And also the colors do not correspond

colors might just be the textures output encoding.

rotation/mirror/ etc will have to be injected into the standardmaterial to get them to work.

You can use this site to view the source for standardmateiral to see where you need to inject your custom logic + uniforms to make it work.

Specifically… probably before this chunk, or removing/patching the chunk entirely:

2 Likes

Thanks for this link!

1 Like