-
-
Notifications
You must be signed in to change notification settings - Fork 36.2k
Add envMapIntensityNode for node materials in WebGLRenderer #27156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@sunag: I tried to also add the functionality in a meaningful way to WebGPU using a multiplication of the P.S: I understand that WebGPU is the "new" thing and ultimately everything will shift towards it. But it would be really appreciated if we could still have the overall WebGL functionality kept more or less on par until this happens wherever this is possible. 👍 |
📦 Bundle sizeFull ESM build, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
483ddce to
130da14
Compare
130da14 to
cdcd8da
Compare
| vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 ); | ||
| return PI * envMapColor.rgb * envMapIntensity; | ||
| return PI * envMapColor.rgb; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@WestLangley Are these changes appropriate?
I'm pinging you since one can easily overlook that this PR modifies shaders in the core.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just for additional clarification:
Before: getIBLIrradiance, getIBLRadiance and getIBLAnisotropyRadiance were returning the environment intensity from the map with the scaling already applied inside the function before the value is returned. This had the drawback that only global scaling per material were possible.
Now: getIBLIrradiance, getIBLRadiance and getIBLAnisotropyRadiance are returning the unmodified environment intensity from the map. The scaling is then applied to their returned values after they are called in lights_fragment_maps.glsl.js (this is the only place that they are ever called inside ThreeJS). This has the advantage that we can define a pixel wise scaling per material (e.g. we use it in order to let the environment illumination affect only for specular parts of a material, which was not possible before).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we use it in order to let the environment illumination affect only for specular parts of a material, which was not possible before
This is a physically-based shader, and the radiance and irradiance are jointly-consistent and scale together.
If I understand correctly, the OP wants to modify the shader so he can use Node to violate the physics of light in his app and have the environment intensity apply only to radiance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the additional explanation! Against this backdrop, I see this change critical, tbh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TobiasNoell Would it be an acceptable workaround to apply your shader modifications via onBeforeCompile()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to add a more generic envMapIntensityNode slot to the node materials (since we already have a envMapIntensity property for the classic materials).
It is true that the user can decide to setup a specific combination of nodes as input for envMapIntensityNode that would then yield a result that is not strictly correct in a physical sense, but still achieves the intended result.
In any case, it would be the user that is setting up the node. If the user decides to apply a (physically incorrect) spatially varying scaling of the environment for a material, the user should be aware of what its intentions are, right?
If the envMapIntensityNode is ignored or a node combination that yields any uniform floating value, everything would still remain strictly physically plausible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an example what we are trying to achieve, to make it more practical:
We have a scene that is illuminated by point lights. This gives a nice appearance including shadows on the diffuse parts of the surface, but the chrome like specular parts look too dark:

To make the chrome look nicer, we setup an environment map for the scene. This makes the chrome parts look nice, but destroys the appearance of the diffuse parts:

To fix this, we want to scale down the effect of the environment map for the rough parts of the surface, but this is only possible with the changes this PR proposes:

It is done by setting material.envMapIntensityNode = THREENodes.pow(material.roughnessNode, THREENodes.float(4.0));
It is true, that the last image is not strictly physically correct, but it looks the nicest out of the three 😄
All materials in the scene are node materials. If there is any better way to achieve the same I'm happy to hear it.
One additional note regarding the violation of the physics of light:
It should be mentioned that also with the classical materials it is already possible to do this by setting
material1.envMapIntensity = 0.5 and material2.envMapIntensity = 2.0 (i.e. different scaling factors per material). The WebGPURenderer even allows entirely different environment maps per material.
| vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness ); | ||
| return envMapColor.rgb * envMapIntensity; | ||
| return envMapColor.rgb; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this PR, the methods getIBLRadiance(), getIBLIrradiance(), and getIBLAnisotropyRadiance() are no longer returning the units their names imply.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, got it (was not aware that the multiplication is also a unit conversion). I could revert everything there and keep the scaling / unit conversion inside these functions and thus revert all changes in envmap_physical_pars_fragment.glsl.js. Instead initialize envMapIntensityFactor in lights_fragment_maps.glsl.js as 1.0? This way it would be much less „invasive“.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed a less invasive version that requires no changes in envmap_physical_pars_fragment.glsl.js
|
@sunag @Mugen87 @WestLangley After the extended discussions above and adaptions to the code, is there any chance this gets merged? |
That seems to me to be the natural solution for your use case. I am curious as to why that is not acceptable to you. |
I want to scale the Thus I'd need something like: |
|
I think your use case is too specific that it justifies a modification of Fortunately, you can achieve your requirements with @sunag I feel the time is right to remove |
|
That's fine, I don't have any particular interest of this being merged anymore because I already forked Three. Is there some documentation how node materials can be used with the A migration guide from WebGL to WebGPU would be a prerequisite IMO before stuff is removed. Also there are devices that still do not support WebGPU and require WebGL, so Three will stop working on these? |
|
Regarding the documentation, expect more to see in this regard during this year. |
|
@Mugen87 to clarify – are we removing support for node-based materials in WebGL? or is the idea that we can use node-based materials with WebGPURenderer, and WebGPURenderer will fall back to WebGL2 while still supporting those node-based materials (with some limitations). |
Yes, that is the plan. Part of the discussion above mentioned the legacy node support for |
Related issue: #27141
Description
ThreeJS already supports scaling the effect of the environment map by the
envMapIntensityproperty of a material.For the node materials, it would be very handy to also have a
envMapIntensityNodeproperty available to setup more complex intensity values rather than just a single float value.While a similar effect can already be achieved by
material.envNode = cubeTexture( myCubeTexture ).mul( intensity );for theWebGPURenderer, theWebGLRenderercurrently only supports setting the environment viascene.environment = texture;, i.e. all materials share one common environment map.For some use-cases it is desirable to scale the intensity of the environment map for each material individually.
It is already possible to do this via
material.envMapIntensity = 0.5;. However, this allows to only set static values. For the node materials, it would be really good to have aenvMapIntensityNodeparameter which allows more sophisticated node based settings. For examplematerial.envMapIntensityNode = THREENodes.pow(material.roughnessNode, THREENodes.float(4.0));to let the environment map only contribute to the really mirroring parts of a surface.