Skip to content

Auto shader output definitions for MRT #27808

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

Open
wants to merge 16 commits into
base: dev
Choose a base branch
from

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.

Copy link

github-actions bot commented Feb 22, 2024

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 336.49
78.38
337.41
78.71
+917 B
+329 B
WebGPU 542.05
150.13
542.05
150.13
+0 B
+0 B
WebGPU Nodes 541.51
150.03
541.51
150.03
+0 B
+0 B

🌳 Bundle size after tree-shaking

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

Before After Diff
WebGL 465.42
112.22
466.35
112.55
+924 B
+331 B
WebGPU 614.89
166.17
614.89
166.17
+0 B
+0 B
WebGPU Nodes 569.88
155.58
569.88
155.58
+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.

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