FarazzShaikh / THREE-CustomShaderMaterial

Extend Three.js standard materials with your own shaders!
Other
846 stars 54 forks source link

Support macro defines like THREE.ShaderMaterial #39

Closed gydence closed 1 year ago

gydence commented 1 year ago

Hello!

THREE.ShaderMaterial supports custom macro definitions: https://threejs.org/docs/?q=shader#api/en/materials/ShaderMaterial.defines

When I try to do the same thing with this library, the shader fails to compile. I'm guessing the materials are compiled before threejs injects the custom defines (or maybe they're just ignored currently).

A basic example (using vanilla threejs but works the same with React):

let material= new CustomShaderMaterial({
  baseMaterial: THREE.MeshStandardMaterial,
  fragmentShader: `
    void main() {
#if RED
      csm_DiffuseColor = vec4(1, 0, 0, 1);
#else
      csm_DiffuseColor = vec4(0, 1, 0, 1);
#endif
    }
  `,
  defines: {
    RED: false,
  }
});

Fails to compile the shader when you use it with:

ERROR: 0:1253: 'RED' : unexpected token after conditional expression

With ShaderMaterial defines, #define RED false would be injected at the top of the shader so things would compile. Obviously changes to defines would need to also trigger a recompilation.

FarazzShaikh commented 1 year ago

I believe this is a quirk of the GLSL language. Defines are simple replaced "as is" inside code, thus If you set the define RED as true then after conpilation the conditional will become:

 #if true
     ...

Which fails to compile even when not using defines or CSM (See: https://codesandbox.io/s/inspiring-blackwell-2ldy3x?file=/src/App.tsx:608-638).

Howver if you set the deine RED as 1 and slightly change the conditional to:

 #if RED == 1
     ...

Then things work as expected. (See: https://codesandbox.io/s/cool-galileo-48fy47?file=/src/App.tsx:510-533)