FarazzShaikh / THREE-CustomShaderMaterial

🧩 Extend Three.js standard materials with your own shaders!
https://farazzshaikh.github.io/THREE-CustomShaderMaterial/
MIT License
897 stars 56 forks source link

Add csm_Bump? #33

Closed chasedavis closed 1 year ago

chasedavis commented 1 year ago

I successfully used CSM patchmaps to add surface bump in the fragment shader without need of map / bump texture. After chatting with Faraz, I'm putting my findings here to discuss what it could look like to add bump natively to CSM.

For starters, bump in three.js is essentially the detail that is drawn in the fragment shader without actually changing vertex positions or geometry. It does this by "perturbing the normals" so that the perturbed normals can be used in lighting calculations.

Findings: There are 4 references to bump currently in three.js shader chunks.

  1. bumpmap_pars_fragment.glsl.js --> this is where utility functions to perturb normals are defined when a bump map is present
  2. normal_fragment_maps.glsl.js --> this is where the normal actually gets perturbed when a bump map is present
  3. envmap_pars_vertex.glsl.js and envmap_pars_fragment.glsl.js --> conditionally defines ENV_WORLDPOS, refractionRatio, vWorldPosition, and vReflect depending on if bump map is used. I'm not sure how these are used downstream.

Basically in default three.js materials, none of the above bump logic takes place unless a bumpMap is provided as a parameter for a material (shown in WebGLProgram.js). In that case USE_BUMPMAP is defined.

I was hoping it could be straightforward to use a default patchmap to add csm_Bump to the fragshader in CSM, though the above findings make me less sure. Some ideas:

I did learn one other thing, which is that it looks like clearcoat (and others?) in three.js specifically does not use perturbed normals for lighting calculations (see: https://github.com/mrdoob/three.js/issues/12867), so we would not want csm_Bump to either?

FarazzShaikh commented 1 year ago

I think we can keep it simple and perturb the normal after #include <normal_fragment_maps> and after the bump map is applied. The normal will first be changed by the bump map, then the procedural normals. If no bump map is present then they would just be changed procedurally.

If the inbuild preturb function are needed then we can patch out #include <bumpmap_pars_fragment> with ${THREE.ShaderChunks.bumpmap_pars_fragment}.

CSM wont inject patch map values if the keyword specified isnt found in the input shader, so, if the user never uses csm_Bump nothing changes, else both bump map and csm_Bump are combined.

This is what AO does too, it combines the AO map result with the procedural AO using multiplication.

As far as lighting goes, we wouldnt have to worry as our bump will be placed right under the already existing bump mapping, thus the order of lighting calculations will be unaffected.

Off the top, Here is what the patch map might look like, however this is untested:

csm_Bump: {
  "#include <bumpmap_pars_fragment>": THREE.ShaderChunks.bumpmap_pars_fragment, // If using Perturb function from the shader 
  "#include <normal_fragment_maps>": `
      #include <normal_fragment_maps>
      normal = somePerturbFunction(normal, csm_Bump);
  `
}

somePerturbFunction can be a custom one defined in src/shaders.ts too

FarazzShaikh commented 1 year ago

Released in 5.3.0

Allakazan commented 4 months ago

Do you guys have any example of how procedural bumping works ? i'm having a bad time trying to make it work properly

FarazzShaikh commented 4 months ago

Here

https://github.com/FarazzShaikh/THREE-CustomShaderMaterial/blob/fa0da85bb2cc174bc5b919c8c812d46e1c8cdcb4/examples/clearcoat/src/App.tsx#L122

csm_Bump is the perturbation factor for the fragment normal. i.e. csm_Bump = vec3(1.0, 0.0, 0.0) will peturb the fragment normal by a factor of 1 in the X axes.

You can think of csm_Bump like a normal map in that regard.

Allakazan commented 4 months ago

Here

https://github.com/FarazzShaikh/THREE-CustomShaderMaterial/blob/fa0da85bb2cc174bc5b919c8c812d46e1c8cdcb4/examples/clearcoat/src/App.tsx#L122

csm_Bump is the perturbation factor for the fragment normal. i.e. csm_Bump = vec3(1.0, 0.0, 0.0) will peturb the fragment normal by a factor of 1 in the X axes.

You can think of csm_Bump like a normal map in that regard.

Suppose i have a float heightMap on my shader, that comes from a noise value

float noiseValue = noise(pos * freq);

How can i pass this float noiseMap to the bumpMap parameter ?