Skip to content

Commit 01ae0d1

Browse files
authored
WebGPURenderer: Add per-attachment MRT blending support (mrdoob#32636)
1 parent 8a3694c commit 01ae0d1

11 files changed

Lines changed: 403 additions & 42 deletions

File tree

examples/webgpu_postprocessing_bloom_emissive.html

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<script type="module">
3333

3434
import * as THREE from 'three/webgpu';
35-
import { pass, mrt, output, emissive } from 'three/tsl';
35+
import { pass, mrt, output, emissive, vec4 } from 'three/tsl';
3636
import { bloom } from 'three/addons/tsl/display/BloomNode.js';
3737

3838
import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
@@ -92,10 +92,24 @@
9292
//
9393

9494
const scenePass = pass( scene, camera );
95-
scenePass.setMRT( mrt( {
96-
output,
97-
emissive
98-
} ) );
95+
96+
// set up MRT with emissive
97+
98+
const mrtNode = mrt( {
99+
output: output,
100+
emissive: vec4( emissive, output.a )
101+
} );
102+
103+
mrtNode.setBlendMode( 'emissive', new THREE.BlendMode( THREE.NormalBlending ) );
104+
105+
scenePass.setMRT( mrtNode );
106+
107+
// optimize the bandwidth
108+
109+
const emissiveTexture = scenePass.getTexture( 'emissive' );
110+
emissiveTexture.type = THREE.UnsignedByteType;
111+
112+
//
99113

100114
const outputPass = scenePass.getTextureNode().toInspector( 'Color' );
101115
const emissivePass = scenePass.getTextureNode( 'emissive' ).toInspector( 'Emissive' );

src/Three.WebGPU.Nodes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export { default as NodeObjectLoader } from './loaders/nodes/NodeObjectLoader.js
2020
export { default as NodeMaterialLoader } from './loaders/nodes/NodeMaterialLoader.js';
2121
export { default as InspectorBase } from './renderers/common/InspectorBase.js';
2222
export { default as CanvasTarget } from './renderers/common/CanvasTarget.js';
23+
export { default as BlendMode } from './renderers/common/BlendMode.js';
2324
export { ClippingGroup } from './objects/ClippingGroup.js';
2425
export * from './nodes/Nodes.js';
2526
import * as TSL from './nodes/TSL.js';

src/Three.WebGPU.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export { default as NodeObjectLoader } from './loaders/nodes/NodeObjectLoader.js
2222
export { default as NodeMaterialLoader } from './loaders/nodes/NodeMaterialLoader.js';
2323
export { default as InspectorBase } from './renderers/common/InspectorBase.js';
2424
export { default as CanvasTarget } from './renderers/common/CanvasTarget.js';
25+
export { default as BlendMode } from './renderers/common/BlendMode.js';
2526
export { ClippingGroup } from './objects/ClippingGroup.js';
2627
export * from './nodes/Nodes.js';
2728
import * as TSL from './nodes/TSL.js';

src/constants.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ export const MultiplyBlending = 4;
155155
*/
156156
export const CustomBlending = 5;
157157

158+
/**
159+
* Represents material blending.
160+
*
161+
* @type {number}
162+
* @constant
163+
*/
164+
export const MaterialBlending = 6;
165+
158166
/**
159167
* A `source + destination` blending equation.
160168
*

src/nodes/core/MRTNode.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import OutputStructNode from './OutputStructNode.js';
22
import { nodeProxy, vec4 } from '../tsl/TSLBase.js';
3+
import { MaterialBlending, NoBlending } from '../../constants.js';
4+
import BlendMode from '../../renderers/common/BlendMode.js';
5+
6+
// Predefined blend modes for MRT nodes.
7+
const _noBlending = /**@__PURE__*/ new BlendMode( NoBlending );
8+
const _materialBlending = /**@__PURE__*/ new BlendMode( MaterialBlending );
39

410
/**
511
* Returns the MRT texture index for the given name.
@@ -63,6 +69,15 @@ class MRTNode extends OutputStructNode {
6369
*/
6470
this.outputNodes = outputNodes;
6571

72+
/**
73+
* A dictionary storing the blend modes for each output.
74+
*
75+
* @type {Object<string, BlendMode>}
76+
*/
77+
this.blendModes = {
78+
output: _materialBlending
79+
};
80+
6681
/**
6782
* This flag can be used for type testing.
6883
*
@@ -74,6 +89,33 @@ class MRTNode extends OutputStructNode {
7489

7590
}
7691

92+
/**
93+
* Sets the blend mode for the given output name.
94+
*
95+
* @param {string} name - The name of the output.
96+
* @param {BlendMode} blend - The blending mode.
97+
* @return {MRTNode} The current MRT node.
98+
*/
99+
setBlendMode( name, blend ) {
100+
101+
this.blendModes[ name ] = blend;
102+
103+
return this;
104+
105+
}
106+
107+
/**
108+
* Returns the blend mode for the given output name.
109+
*
110+
* @param {string} name - The name of the output.
111+
* @return {BlendMode} The blend mode.
112+
*/
113+
getBlendMode( name ) {
114+
115+
return this.blendModes[ name ] || _noBlending;
116+
117+
}
118+
77119
/**
78120
* Returns `true` if the MRT node has an output with the given name.
79121
*
@@ -107,8 +149,12 @@ class MRTNode extends OutputStructNode {
107149
merge( mrtNode ) {
108150

109151
const outputs = { ...this.outputNodes, ...mrtNode.outputNodes };
152+
const blendings = { ...this.blendModes, ...mrtNode.blendModes };
153+
154+
const mrtTarget = mrt( outputs );
155+
mrtTarget.blendings = blendings;
110156

111-
return mrt( outputs );
157+
return mrtTarget;
112158

113159
}
114160

src/renderers/common/BlendMode.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { NormalBlending, AddEquation, SrcAlphaFactor, OneMinusSrcAlphaFactor } from '../../constants.js';
2+
3+
/**
4+
* Represents blending configuration.
5+
*
6+
* This class encapsulates all blending-related properties that control how
7+
* a material's colors are combined with the colors already in the frame buffer.
8+
*/
9+
class BlendMode {
10+
11+
/**
12+
* Constructs a new blending configuration.
13+
*
14+
* @param {(NoBlending|NormalBlending|AdditiveBlending|SubtractiveBlending|MultiplyBlending|CustomBlending|MaterialBlending)} [blending=NormalBlending] - The blending mode.
15+
*/
16+
constructor( blending = NormalBlending ) {
17+
18+
/**
19+
* Defines the blending type.
20+
*
21+
* It must be set to `CustomBlending` if custom blending properties like
22+
* {@link BlendMode#blendSrc}, {@link BlendMode#blendDst} or {@link BlendMode#blendEquation}
23+
* should have any effect.
24+
*
25+
* @type {(NoBlending|NormalBlending|AdditiveBlending|SubtractiveBlending|MultiplyBlending|CustomBlending|MaterialBlending)}
26+
* @default NormalBlending
27+
*/
28+
this.blending = blending;
29+
30+
/**
31+
* Defines the blending source factor.
32+
*
33+
* This determines how the source (incoming) fragment color is factored before being added
34+
* to the destination (existing) fragment color in the frame buffer.
35+
*
36+
* @type {(ZeroFactor|OneFactor|SrcColorFactor|OneMinusSrcColorFactor|SrcAlphaFactor|OneMinusSrcAlphaFactor|DstAlphaFactor|OneMinusDstAlphaFactor|DstColorFactor|OneMinusDstColorFactor|SrcAlphaSaturateFactor|ConstantColorFactor|OneMinusConstantColorFactor|ConstantAlphaFactor|OneMinusConstantAlphaFactor)}
37+
* @default SrcAlphaFactor
38+
*/
39+
this.blendSrc = SrcAlphaFactor;
40+
41+
/**
42+
* Defines the blending destination factor.
43+
*
44+
* This determines how the destination (existing) fragment color in the frame buffer
45+
* is factored before being combined with the source (incoming) fragment color.
46+
*
47+
* @type {(ZeroFactor|OneFactor|SrcColorFactor|OneMinusSrcColorFactor|SrcAlphaFactor|OneMinusSrcAlphaFactor|DstAlphaFactor|OneMinusDstAlphaFactor|DstColorFactor|OneMinusDstColorFactor|SrcAlphaSaturateFactor|ConstantColorFactor|OneMinusConstantColorFactor|ConstantAlphaFactor|OneMinusConstantAlphaFactor)}
48+
* @default OneMinusSrcAlphaFactor
49+
*/
50+
this.blendDst = OneMinusSrcAlphaFactor;
51+
52+
/**
53+
* Defines the blending equation.
54+
*
55+
* This determines how the source and destination colors are combined.
56+
*
57+
* @type {(AddEquation|SubtractEquation|ReverseSubtractEquation|MinEquation|MaxEquation)}
58+
* @default AddEquation
59+
*/
60+
this.blendEquation = AddEquation;
61+
62+
/**
63+
* Defines the blending source alpha factor.
64+
*
65+
* When set, this allows separate control of the alpha channel's source blending factor.
66+
* If `null`, {@link BlendMode#blendSrc} is used for the alpha channel as well.
67+
*
68+
* @type {?(ZeroFactor|OneFactor|SrcColorFactor|OneMinusSrcColorFactor|SrcAlphaFactor|OneMinusSrcAlphaFactor|DstAlphaFactor|OneMinusDstAlphaFactor|DstColorFactor|OneMinusDstColorFactor|SrcAlphaSaturateFactor|ConstantColorFactor|OneMinusConstantColorFactor|ConstantAlphaFactor|OneMinusConstantAlphaFactor)}
69+
* @default null
70+
*/
71+
this.blendSrcAlpha = null;
72+
73+
/**
74+
* Defines the blending destination alpha factor.
75+
*
76+
* When set, this allows separate control of the alpha channel's destination blending factor.
77+
* If `null`, {@link BlendMode#blendDst} is used for the alpha channel as well.
78+
*
79+
* @type {?(ZeroFactor|OneFactor|SrcColorFactor|OneMinusSrcColorFactor|SrcAlphaFactor|OneMinusSrcAlphaFactor|DstAlphaFactor|OneMinusDstAlphaFactor|DstColorFactor|OneMinusDstColorFactor|SrcAlphaSaturateFactor|ConstantColorFactor|OneMinusConstantColorFactor|ConstantAlphaFactor|OneMinusConstantAlphaFactor)}
80+
* @default null
81+
*/
82+
this.blendDstAlpha = null;
83+
84+
/**
85+
* Defines the blending equation of the alpha channel.
86+
*
87+
* When set, this allows separate control of the alpha channel's blending equation.
88+
* If `null`, {@link BlendMode#blendEquation} is used for the alpha channel as well.
89+
*
90+
* @type {?(AddEquation|SubtractEquation|ReverseSubtractEquation|MinEquation|MaxEquation)}
91+
* @default null
92+
*/
93+
this.blendEquationAlpha = null;
94+
95+
/**
96+
* Defines whether to premultiply the alpha (transparency) value.
97+
*
98+
* If `true`, the RGB color of the texture or material is multiplied by its alpha value.
99+
* This is useful for transparent textures/materials where the color data
100+
* should already include the transparency information.
101+
*
102+
* @type {boolean}
103+
* @default false
104+
*/
105+
this.premultiplyAlpha = false;
106+
107+
}
108+
109+
/**
110+
* Copies the blending properties from the given source to this instance.
111+
*
112+
* @param {BlendMode} source - The blending configuration to copy from.
113+
* @return {BlendMode} A reference to this instance.
114+
*/
115+
copy( source ) {
116+
117+
this.blending = source.blending;
118+
this.blendSrc = source.blendSrc;
119+
this.blendDst = source.blendDst;
120+
this.blendEquation = source.blendEquation;
121+
this.blendSrcAlpha = source.blendSrcAlpha;
122+
this.blendDstAlpha = source.blendDstAlpha;
123+
this.blendEquationAlpha = source.blendEquationAlpha;
124+
this.premultiplyAlpha = source.premultiplyAlpha;
125+
126+
return this;
127+
128+
}
129+
130+
/**
131+
* Returns a clone of this blending configuration.
132+
*
133+
* @return {BlendMode} A new Blending instance with the same properties.
134+
*/
135+
clone() {
136+
137+
return new this.constructor().copy( this );
138+
139+
}
140+
141+
}
142+
143+
export default BlendMode;

src/renderers/common/RenderContext.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ class RenderContext {
2525
*/
2626
this.id = _id ++;
2727

28+
/**
29+
* The MRT configuration.
30+
*
31+
* @type {?MRTNode}
32+
* @default null
33+
*/
34+
this.mrt = null;
35+
2836
/**
2937
* Whether the current active framebuffer has a color attachment.
3038
*

src/renderers/common/RenderContexts.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ class RenderContexts {
6262
if ( renderState === undefined ) {
6363

6464
renderState = new RenderContext();
65+
renderState.mrt = mrt;
6566

66-
this._renderContexts[ renderStateKey ] = renderState;
67+
this._renderContexts[ renderStateKey ] = renderState;
6768

6869
}
6970

src/renderers/webgl-fallback/WebGLBackend.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -958,9 +958,9 @@ class WebGLBackend extends Backend {
958958

959959
state.setMaterial( material, frontFaceCW, hardwareClippingPlanes );
960960

961-
if ( context.textures !== null && context.textures.length > 1 ) {
961+
if ( context.mrt !== null && context.textures !== null ) {
962962

963-
state.setMRTBlending( context.textures );
963+
state.setMRTBlending( context.textures, context.mrt, material );
964964

965965
}
966966

0 commit comments

Comments
 (0)