mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
101.81k stars 35.31k forks source link

WebGLRenderTarget: Allow opt-in for output transforms / image formation #29429

Open donmccurdy opened 4 days ago

donmccurdy commented 4 days ago

Description

In recent three.js versions,renderer.outputColorSpace, render.toneMapping, and renderer.toneMappingExposure (jointly, the image formation step) are applied only when writing to the canvas drawing buffer (or WebXR) and disabled when writing to a render target. I think that's the right default. But it does constrain users' choices, and can force an additional (expensive!) pass in a post-processing stack. Based on feedback, this is problematic for mobile applications.

Solution

I'd like to suggest that we add a new property to WebGLRenderTarget, perhaps called target.needsOutputTransform. If enabled, WebGLRenderer would apply ...

when drawing to that render target. I believe renderer.outputColorSpace is not relevant here, since the render target already has a .colorSpace property.

Alternatives

Alternatively, we could make WebGLRenderer always apply these settings when drawing to a render target, and require explicit changes to the renderer's properties by the user. I expect this would be a significant breaking change, however.

Additional context

I don't know what this looks like in WebGPURenderer, or whether similar changes would be required there.

/cc @WestLangley

donmccurdy commented 17 hours ago

I believe renderer.outputColorSpace is not relevant here, since the render target already has a .colorSpace property.

Hm. If the user's intention is to force blending in a particular color space like sRGB (building a 2D compositing application, for example) then expressing that in terms of the render target's color space won't be correct. WebGL will decode an sRGB framebuffer to linear when doing blending, it blends in linear when writing to both linear and sRGB framebuffers. In contrast to writing to the drawing buffer, where it blends in sRGB. So instead, the custom blending space use case might require:

renderer.outputColorSpace = SRGBColorSpace;

const target = new WebGLRenderTarget(width, height, {
  type: HalfFloatType,
  colorSpace: NoColorSpace, // ???
  needsOutputTransform: true
});

I don't love that; this needs some thought.

Context: https://discourse.threejs.org/t/wrong-colors-in-transparent-materials-when-using-effectcomposer-post-processing/70895