Backface Directional Lighting

If you want to ignore the faceDirection on any particular mesh group (folliage for instance) still using the new version of MeshLambertMaterial, you’ll need to ignore this bit of shader code directly on the fragment shader, specifically we are looking at this chunk: normal_fragment_begin

So the hack becomes:

shader.fragmentShader = shader.fragmentShader.replace(
  '#include <normal_fragment_begin>',
  'vec3 normal = normalize( vNormal );\nvec3 geometryNormal = normal;\n'
);

Which if you compare with the original shader, ignores DOUBLE_SIDED, USE_TANGENT pre-processor conditions and prevents your normal from being mirrored. This code should also work properly for any other variant of the core materials, since maintainers made sure that every one is using this same chunk. This is now a more standard “hack”. Let me know if you guys have any trouble.

Oh i see now there are front-facing blades, looked like grass-blade-less ground on first sight. And good to mention that change here @sciecode :+1:

Also as suggestion to @jackiejean388 for small foliage like grass ground aligned normals can make you get rid of any such shading issue blending it more seamless with the ground. Just making the blade normal face the direction of the ground.

1 Like

Thanks sciecode!
What i have done previously with the phongmaterial like below as you mentioned before

Now i changed into this;

shader.fragmentShader = shader.fragmentShader.replace(
#include <normal_fragment_begin>’,
[
‘float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;’,
‘vec3 normal = normalize( vNormal );’
].join(‘\n’)
);
mat.userData.shader = shader;

But this deosnt work either. Do you have a solution for the phong?

Thanks, Mert.

This goes inside material.onBeforeCompile, which is a pre-compile callback that Three.js exposes for custom shader changes. Follows an example:

folliageMaterial.onBeforeCompile = function swapNormals( shader, renderer ) {

    shader.fragmentShader = shader.fragmentShader.replace(
        '#include <normal_fragment_begin>',
        'vec3 normal = normalize( vNormal );\nvec3 geometryNormal = normal;\n'
);

This should work with MeshLambertMaterial, MeshPhongMaterial, MeshStandardMaterial & MeshPhysicalMaterial.

2 Likes

Once again (3+ years later lol) you save the day! Perfect! Thank you so much!

1 Like

You are missing the last line vec3 geometryNormal = normal;

Your code version would be:

shader.fragmentShader = shader.fragmentShader.replace(
‘#include <normal_fragment_begin>’,
[
‘float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;’,
‘vec3 normal = normalize( vNormal );’,
'vec3 geometryNormal = normal;'
].join(‘\n’)
);
1 Like

Thanks, yes i already tried it but didnt work for me and i dont know why
Shared below;

If you merge, it’s hard to target a specific sub mesh in your merged mesh. If you instance, it’s possible to control a submesh (one instance) via an attribute. So all you have to do is update a position, and move thousands of vertices in a cluster of a million.

I would suggest setting up a simplified version in jsfiddle or something so the issue can be examined.

Hello. I’ve tried to do the same for latest version (163), but I’m missing something. I’m really bad at shaders, so maybe someone can take a look: https://codesandbox.io/p/devbox/admiring-ioana-9yr7vl?file=%2Fsrc%2Findex.mjs%3A29%2C34

There is a commented code that changes the shader, but when I uncomment it, then it looks like there is only ambient light