Skip to content

WebGLRenderer: Auto shader output definitions for MRT#27808

Open
vanruesc wants to merge 29 commits intomrdoob:devfrom
vanruesc:auto-shader-outputs
Open

WebGLRenderer: Auto shader output definitions for MRT#27808
vanruesc wants to merge 29 commits intomrdoob:devfrom
vanruesc:auto-shader-outputs

Conversation

@vanruesc
Copy link
Contributor

@vanruesc vanruesc commented Feb 22, 2024

Description

This PR aims to make Multiple Render Targets (MRT) easier to use by implementing automatic shader output definitions for built-in materials and ShaderMaterial.

// Example output definitions:
layout(location = 0) out lowp vec4 out_FragData0;
#define out_Color out_FragData0
layout(location = 1) out mediump vec4 out_FragData1;
#define out_Normal out_FragData1

Context

For MRT workflows it's up to the user to define what data should be written to the respective textures. The MRT example promotes the use of RawShaderMaterial to achieve this. However, MRT is most useful for rendering out additional pixel data during the main render pass. In most situations the main scene will consist of meshes that use built-in materials like MeshStandardMaterial, but those materials are currently difficult to use with MRT.

Improving compatibility

It's possible to use onBeforeCompile on every object in a scene to modify built-in materials, but this has some downsides:

  • An object's material may already have an onBeforeCompile hook, so care must be taken to preserve it.
  • All objects need to be tracked to avoid assigning onBeforeCompile hooks multiple times.
  • Objects and materials may be added or removed at runtime which requires additional scene processing to ensure that all materials have the required onBeforeCompile hooks.

If it wasn't necessary to define shader outputs, we could avoid using onBeforeCompile altogether and modify the ShaderChunk library instead (example). But at the moment there's no way around onBeforeCompile.

Meeting halfway

With the changes in this PR, three can define shader outputs automatically based on the current render target. To avoid breakage, this is only done for built-in materials and for ShaderMaterial if the shader doesn't already define shader outputs. This implementation also uses the names of the MRT textures to define macros of the form out_${name} for convenience which allows for conditional MRT code like the following:

#ifdef out_Normal

	out_Normal = vec4(normal, 1.0);

#endif

I've considered using the texture names as-is but opted for a prefix approach to reduce the risk of name conflicts.

Open issue

This PR is in draft mode because the material/program doesn't get updated after switching the render target. I've tried modifying WebGLPrograms.getProgramCacheKey to account for the render target, but that didn't work out. Setting material.needsUpdate to true also didn't trigger a program update.

@github-actions
Copy link

github-actions bot commented Feb 22, 2024

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 359.27
85.31
360.06
85.59
+788 B
+281 B
WebGPU 630.13
174.94
630.13
174.94
+0 B
+0 B
WebGPU Nodes 628.72
174.69
628.72
174.69
+0 B
+0 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 491.13
119.74
491.93
120.02
+798 B
+274 B
WebGPU 703.8
190.02
703.8
190.02
+0 B
+0 B
WebGPU Nodes 653.03
177.44
653.03
177.44
+0 B
+0 B

@puxiao
Copy link
Contributor

puxiao commented Feb 24, 2024

Some checks were not successful

DeepScan:1 new and 1 fixed issues

Local variable 'xr' is not used.

editor/js/Viewport.js

....

@vanruesc This issue has been fixed, you need to pull the code again.


Solution:

git checkout auto-shader-outputs
git remote add upstream https://github.com/threejs/three.js.git
git fetch upstream
git merge upstream/dev

... All checks have passed ...

@Mugen87 Mugen87 added this to the r??? milestone Feb 26, 2024
@vanruesc vanruesc marked this pull request as ready for review April 12, 2025 19:02
@vanruesc
Copy link
Contributor Author

This PR is in draft mode because the material/program doesn't get updated after switching the render target. I've tried modifying WebGLPrograms.getProgramCacheKey to account for the render target, but that didn't work out. Setting material.needsUpdate to true also didn't trigger a program update.

I've fixed this now by including the render target in the material properties to detect if the current render target differs from the one that was previously associated with the material. The needsUpdate flag did in fact work before, but I didn't realize that I had to call render() manually in the demo I was testing with.

I've adjusted webgl_multiple_rendertargets to test switching between render targets and it appears to be working as expected. One downside is that render targets are only checked superficially. If the texture attachments are changed, materials will not be updated unless needsUpdate is set.

@mrdoob mrdoob changed the title Auto shader output definitions for MRT WebGLRenderer: Auto shader output definitions for MRT Dec 26, 2025
@vanruesc
Copy link
Contributor Author

vanruesc commented Jan 3, 2026

@Mugen87 @mrdoob

Any thoughts on this PR? I still think it's worth merging since it makes working with MRT in WebGLRenderer more practical.

The only issues that appeared due to this change were preexisting configuration problems:

  • examples/webgl_rendertarget_texture2darray.html wrote a float into the previous default vec4 output with a swizzle despite the render target using RedFormat. The new generated output definition accurately declares the output as float which removes the need for the swizzle in this case.
  • examples/webgl_shadow_contact.html repurposes the shader of the builtin MeshDepthMaterial which was shaky to begin with.

If there are concerns about breakage, I'd be fine with changing the default output defnition gl_FragColor to always use vec4 instead. The more valuable aspect of this feature is that it also generates macros with predictable names for each output. This opens up more possibilities, especially regarding clear values.

If this gets merged, I'd like to contribute improvements for the current scene background rendering system to support clear values for specific MRT attachments which would internally rely on the output macros.

@vanruesc
Copy link
Contributor Author

If there are concerns about breakage, I'd be fine with changing the default output defnition gl_FragColor to always use vec4 instead.

I went ahead with this to reduce friction: 51cb2ed
I've also updated ShaderMaterial to document the behavior: 13cfc88

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants