Skip to content

NodeMaterial: Honor material.premultipliedAlpha in the shader #31166

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

Merged
merged 1 commit into from
Jun 8, 2025

Conversation

WestLangley
Copy link
Collaborator

As the title says.

The alternative is to only add this code block in the built-in (node) materials -- as we do with WebGLRenderer's built-in materials.

@WestLangley WestLangley added this to the r177 milestone May 25, 2025
@WestLangley WestLangley changed the title NodeMaterial: Honor material.premultipliedAlpha in the shader NodeMaterial: Honor material.premultipliedAlpha in the shader May 25, 2025
Copy link

github-actions bot commented May 25, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 337.4
78.67
337.4
78.67
+0 B
+0 B
WebGPU 554.99
153.64
554.99
153.64
+0 B
+0 B
WebGPU Nodes 554.34
153.49
554.34
153.49
+0 B
+0 B

🌳 Bundle size after tree-shaking

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

Before After Diff
WebGL 468.59
113.33
468.59
113.33
+0 B
+0 B
WebGPU 629.96
170.36
629.96
170.36
+0 B
+0 B
WebGPU Nodes 584.81
159.69
584.81
159.69
+0 B
+0 B

@Mugen87
Copy link
Collaborator

Mugen87 commented May 26, 2025

The E2E screenshot of webgpu_postprocessing_ssaa requires an update since SSAAPassNode already sets material.premultipliedAlpha to true (this has been adopted from the old SSAARenderPass).

@Mugen87
Copy link
Collaborator

Mugen87 commented May 27, 2025

The alternative is to only add this code block in the built-in (node) materials -- as we do with WebGLRenderer's built-in materials.

Since premultipliedAlpha is a Material property, I would expect it works with all materials. I prefer this new approach compared to support just for built-in materials.

@WestLangley
Copy link
Collaborator Author

Since premultipliedAlpha is a Material property, I would expect it works with all materials. I prefer this new approach compared to support just for built-in materials.

Well, yes, but maybe you misunderstand what its purpose is...

Material.premultipliedAlpha is a hint to the renderer that the output of the shader returns premultiplied color values. That way, the renderer will set the proper blending function. It works for all materials.

In the case of built-in materials, the shader itself must also be modified to return premultiplied values instead of non-premultiplied values.

So built-in materials and custom materials are different in this regard.

Unfortunately, Material.premultipliedAlpha is serving double-duty and affects both the blending function (for all materials), and the shader itself (for built-in materials only).

@mrdoob mrdoob modified the milestones: r177, r178 May 30, 2025
@Mugen87
Copy link
Collaborator

Mugen87 commented May 31, 2025

Yeah, the double purpose of premultipliedAlpha came along in the past multiple times. But why do you think is it unfortunate? If the blending assumes premultiplied colors, the materials should honor that setting and produce the correct output in their shaders. Regardless of built-in or custom materials (this distinction isn't that relevant for node materials anyway).

The node material seems a good opportunity to establish a policy that produce more consistent outputs no matter if you customize a material or not.

AFAICS, if the developer needs full control, it's always possible to use custom blending, don't change premultipliedAlpha and then apply a custom color transformation in the shader.

@WestLangley
Copy link
Collaborator Author

Material.premultipliedAlpha is serving double-duty

But why do you think it is unfortunate?

Consider this use case:

renderer.setRenderTarget( renderTarget ); 
renderer.render( scene, camera );

In such a case, renderTarget.texture will have RGB premultiplied by alpha.

Now, use the render target texture as a map:

const material = new THREE.MeshBasicMaterial( {

	map: rederTarget.texture

} );

The material shader will outut premultiplied colors, but the renderer will use the wrong blending function.

So, try this instead:

const material = new THREE.MeshBasicMaterial( {

	map: rederTarget.texture,

	premultipliedAlpha: true

} );

Now this is wrong because the shader will pre-multiply the colors a second time.

@WestLangley
Copy link
Collaborator Author

@Mugen87 @sunag Do you understand what the issue is?

Currently, you can't pass in a texture with premultiplied data to any built-in material and expect to see correct output.

It is also pointless to set texture.premultiplyAlpha.

This PR, as currently written, is of no help.

@sunag
Copy link
Collaborator

sunag commented Jun 6, 2025

I think one way out could be apply an unpremultiplyAlpha() function to all texture samples with premultiplyAlpha if it is defined in the material as material.premultipliedAlpha=true.

@WestLangley
Copy link
Collaborator Author

@sunag There are other problematic cases. A texture can contain premultiplied color values, in which case setting texture. premultiplyAlpha = true would not be appropriate.

@sunag
Copy link
Collaborator

sunag commented Jun 7, 2025

A texture can contain premultiplied color values, in which case setting texture. premultiplyAlpha = true would not be appropriate.

I'm considering we can do this check a bit deeper in the backend like

const premultiplyAlpha = texture.premultiplyAlpha === true || texture.isRenderTargetTexture === true ...

I'm not sure now how many properties we should check, but it might be the way to go.

@WestLangley
Copy link
Collaborator Author

@sunag Any texture can contain premultiplied color values.

@WestLangley WestLangley marked this pull request as ready for review June 7, 2025 23:31
@WestLangley
Copy link
Collaborator Author

There does not appear to be valid solutions to the issue I raised, so I suggest merging this and dealing with the issue later.

The E2E screenshot of webgpu_postprocessing_ssaa requires an update

I would say the screenshot is not the problem. The code is the problem.

@sunag
Copy link
Collaborator

sunag commented Jun 8, 2025

@sunag Any texture can contain premultiplied color values.

Certainly, but I thought we were trying to automate a common routine here and not trying to hit every possible case. In this case we wouldn't even need anything special, the user just needs to call a function for this using node-based material.

const material = new THREE.MeshBasicMaterial();
material.premultipliedAlpha = true;
material.colorNode = unpremultiplyAlpha( texture( renderTarget.texture ) );

@sunag sunag closed this Jun 8, 2025
@sunag sunag reopened this Jun 8, 2025
@sunag sunag merged commit fce5937 into mrdoob:dev Jun 8, 2025
20 of 22 checks passed
sunag added a commit that referenced this pull request Jun 8, 2025
@WestLangley WestLangley deleted the dev-nodematerial_premultiplied_alpha branch June 8, 2025 19:28
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