-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
WebGLRenderer: Consolidate premultiplied alpha handling in opaque_fragment #32449
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
base: dev
Are you sure you want to change the base?
Conversation
📦 Bundle sizeFull ESM build, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
|
Oh no, this triggers some weird behaviour in SwiftShader... Screen.Recording.2025-12-02.at.09.05.00.mov |
|
This ordering is right to fix the original issue but it does change the color space in which alpha is applied which can have an impact on the final blend color. This is a quick demo of a sphere with sRGB color Before
After
Here's the same demo with a
For other context there have been some other forum posts in the past about this same phenomenon when using effect composer since it uses linear color targets (see here or here). Not exactly sure what the best thing to do here is but I'll let other's chime in. Applying tone mapping as a post process would avoid all of this but I know there are performance implications.
I'll take a look at what might be going on here later.
edit: It actually looks like there are a number of references to "premultiplied_alpha_fragment" still in the examples folder that would cause a number of examples to break, including the "ldraw" and "fat lines" ones |
Oh, now I can reproduce. I wonder why I couldn't reproduce before... Fixing... |
Possibly testing before deleting the Also one option that occurs to me in order to avoid the differences would be the following when premultiplied alpha is true:
Not the most elegant feeling and requires two additional color space conversion operations (possibly not so terrible in the grand scheme of some of these shaders) but it would address the color consistency issue while still allowing for premultiplied alpha tone mapping. |
|
Was curious to see what the changes would be if we added an internal MSAA RT to WebGLRenderer: #32461 |
|
After thinking through it for a few days I think I would probably take the approach of converting to the target color space, applying alpha, and converting back to the working color space. This should be not so expensive in the grand scheme of things and only need to be run when premultiplied alpha is true and blending is enabled. This will avoid any performance overhead from a new required half-float fullscreen pass and any functionality loss like per-object tonemap settings. Even if we do want to "detone" some objects to address I'm not sure if it will full work because there is generally information loss in a tone mapping step. More realistically I think we'd need some kind of "mask" for tone mapping application rendered using MRT like a deferred rendering system. On a related topic, I'm wondering if it's worth raising the idea of setting the default of "premultipliedAlpha" to true, again, since the results are generally much nicer for transparent objects in bright lighting. |
|
Not that we'd necessarily change anything in WebGLRenderer now, but I might prefer to think of tone mapping as a pass-level setting. So overlays, billboards, or UI elements that don't participate in scene lighting would ideally be drawn in a later pass, composited after tone mapping. Rather than trying to mask out specific objects within a 3D scene, which may have transparency, etc... In that case materials like MeshNormalMaterial do not need special treatment. If our premultiplied alpha blending equation is... ... is it a concern that our premultiplied 'src' may now have components greater than the alpha value? I'm not sure I understand the implications of that change, or whether it may introduce more clipping at 1.0 that the tone mapping would otherwise have avoided. |
Just gave that a try: #32470 |
Gave that a try too: #32472 |













Related issue: #32433
Description
opaque_fragment.glsl.js, pre-multiplying RGB by alpha before tone mappingpremultiplied_alpha_fragment.glsl.js(was causing double-multiplication)Motivation
Pre-multiplying in the shader preserves HDR information when rendering to LDR framebuffers: