Skip to content

Commit 183258e

Browse files
committed
Add rudimentary masking support to EffectComposer2
1 parent da58315 commit 183258e

File tree

3 files changed

+153
-22
lines changed

3 files changed

+153
-22
lines changed

examples/js/postprocessing2/EffectComposer2.js

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ THREE.EffectComposer2 = function ( renderer, colorRenderTarget ) {
4949

5050
this._previousFrameTime = Date.now();
5151

52+
this.maskActive = false;
53+
this.renderer.autoClearStencil = false; // Only mask passes should clear the stencil.
54+
5255
};
5356

5457
Object.assign( THREE.EffectComposer2.prototype, {
@@ -127,11 +130,12 @@ Object.assign( THREE.EffectComposer2.prototype, {
127130
if ( bufferConfig.isOutput ) {
128131

129132
var buffer = this.getColorOutputBuffer( bufferConfig, writeToFinalColorTarget, finalColorRenderTarget );
130-
if ( buffer == finalColorRenderTarget && bufferConfig.isInput && !bufferConfig.clear )
133+
var passClearsWholeBuffer = bufferConfig.clear && !this.maskActive;
134+
if ( buffer == finalColorRenderTarget && bufferConfig.isInput && !passClearsWholeBuffer )
131135
{
132-
// The output buffer is also used as a color input in the pass.
136+
// The output buffer is also used as a color input in the pass and the pass does not clear it.
133137
// Copy the color buffer from the previous pass to the final target before executing the pass.
134-
this.copyPass.render( this.renderer, [ buffer, this.colorReadBuffer ], 0 );
138+
this.copyPass.render( this.renderer, [ buffer, this.colorReadBuffer ], 0, this.maskActive );
135139
}
136140
if ( buffer == this.colorWriteBuffer ) {
137141
writesToColorWriteBuffer = true;
@@ -171,6 +175,31 @@ Object.assign( THREE.EffectComposer2.prototype, {
171175

172176
},
173177

178+
handleMaskPass: function( pass ) {
179+
180+
if ( THREE.MaskPass2 !== undefined ) {
181+
182+
if ( pass instanceof THREE.MaskPass2 ) {
183+
184+
this.maskActive = true;
185+
186+
pass.render( this.renderer, [ this.colorWriteBuffer, this.colorReadBuffer ] );
187+
return true;
188+
189+
} else if ( pass instanceof THREE.ClearMaskPass2 ) {
190+
191+
this.maskActive = false;
192+
pass.render( this.renderer );
193+
return true;
194+
195+
}
196+
197+
}
198+
199+
return false;
200+
201+
},
202+
174203
render: function ( finalColorRenderTarget, deltaTime ) {
175204

176205
if ( finalColorRenderTarget == undefined ) {
@@ -191,6 +220,8 @@ Object.assign( THREE.EffectComposer2.prototype, {
191220

192221
var pass, i, il = this.passes.length;
193222

223+
this.maskActive = false;
224+
194225
for ( i = 0; i < il; i ++ ) {
195226

196227
pass = this.passes[ i ];
@@ -199,18 +230,20 @@ Object.assign( THREE.EffectComposer2.prototype, {
199230

200231
var writeToFinalColorTarget = this.isLastEnabledPass( i );
201232

233+
if ( this.handleMaskPass( pass ) ) continue;
234+
202235
var buffers = [];
203236
var writesToColorWriteBuffer = this.gatherBuffersForPass( pass, writeToFinalColorTarget, finalColorRenderTarget, buffers );
204237

205-
pass.render( this.renderer, buffers, deltaTime );
238+
pass.render( this.renderer, buffers, deltaTime, this.maskActive );
206239

207240
// Check if we need to copy the final output from a texture to the default framebuffer.
208241
if ( writeToFinalColorTarget && !finalColorRenderTarget ) {
209242

210243
var wroteToBuffer = this.passWroteToBuffer( pass, writeToFinalColorTarget, finalColorRenderTarget );
211244
if ( wroteToBuffer ) {
212245
// In case the pass is only able to use a texture as its write buffer, copy the result to screen here.
213-
this.copyPass.render( this.renderer, [ null, wroteToBuffer ], 0 );
246+
this.copyPass.render( this.renderer, [ null, wroteToBuffer ], 0, this.maskActive );
214247
}
215248

216249
} else if ( writesToColorWriteBuffer ) {
@@ -283,8 +316,8 @@ THREE.IntermediateBufferConfig = function() {
283316
this.mustBeTexture = false;
284317

285318
// Set to true if the pass clears this buffer before reading from or writing to it. Should only be set if isOutput
286-
// is also true. If this is set then isInput is essentially ignored and the contents of the buffer are undefined
287-
// when it is given to the pass.
319+
// is also true. If this is set and a mask is not active then isInput is essentially ignored and the contents of the
320+
// buffer are undefined when it is given to the pass.
288321
this.clear = false;
289322

290323
};
@@ -333,6 +366,8 @@ THREE.Pass2.renderWithClear = function( renderer, scene, camera, writeBuffer, cl
333366
var oldAutoClear = renderer.autoClear;
334367
renderer.autoClear = false;
335368

369+
// TODO: Maybe this function should specifically clear only color and depth and make sure that clearing stencil is
370+
// disabled?
336371
renderer.render( scene, camera, writeBuffer, clear );
337372

338373
renderer.autoClear = oldAutoClear;
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* @author alteredq / http://alteredqualia.com/
3+
* @author Oletus / http://oletus.fi/ - Ported to EffectComposer2
4+
*/
5+
6+
THREE.MaskPass2 = function ( scene, camera ) {
7+
8+
THREE.Pass2.call( this );
9+
10+
this.scene = scene;
11+
this.camera = camera;
12+
13+
this.clearStencil = true;
14+
15+
this.inverse = false;
16+
17+
};
18+
19+
THREE.MaskPass2.prototype = Object.assign( Object.create( THREE.Pass2.prototype ), {
20+
21+
constructor: THREE.MaskPass2,
22+
23+
render: function ( renderer, buffers, deltaTime, maskActive ) {
24+
25+
var context = renderer.context;
26+
var state = renderer.state;
27+
28+
// don't update color or depth
29+
30+
state.buffers.color.setMask( false );
31+
state.buffers.depth.setMask( false );
32+
33+
// lock buffers
34+
35+
state.buffers.color.setLocked( true );
36+
state.buffers.depth.setLocked( true );
37+
38+
// set up stencil
39+
40+
var writeValue, clearValue;
41+
42+
if ( this.inverse ) {
43+
44+
writeValue = 0;
45+
clearValue = 1;
46+
47+
} else {
48+
49+
writeValue = 1;
50+
clearValue = 0;
51+
52+
}
53+
54+
state.buffers.stencil.setTest( true );
55+
state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE );
56+
state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff );
57+
state.buffers.stencil.setClear( clearValue );
58+
59+
// draw into the stencil buffer
60+
61+
renderer.autoClearStencil = true;
62+
renderer.render( this.scene, this.camera, buffers[0], this.clearStencil );
63+
renderer.render( this.scene, this.camera, buffers[1], this.clearStencil );
64+
renderer.autoClearStencil = false;
65+
66+
// unlock color and depth buffer for subsequent rendering
67+
68+
state.buffers.color.setLocked( false );
69+
state.buffers.depth.setLocked( false );
70+
71+
// only render where stencil is set to 1
72+
73+
state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1
74+
state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP );
75+
76+
}
77+
78+
} );
79+
80+
81+
THREE.ClearMaskPass2 = function () {
82+
83+
THREE.Pass2.call( this );
84+
85+
};
86+
87+
THREE.ClearMaskPass2.prototype = Object.create( THREE.Pass2.prototype );
88+
89+
Object.assign( THREE.ClearMaskPass2.prototype, {
90+
91+
render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
92+
93+
renderer.state.buffers.stencil.setTest( false );
94+
95+
}
96+
97+
} );

examples/webgl_postprocessing_masking.html

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020

2121
<script src="js/shaders/CopyShader.js"></script>
2222

23-
<script src="js/postprocessing/EffectComposer.js"></script>
24-
<script src="js/postprocessing/ClearPass.js"></script>
25-
<script src="js/postprocessing/TexturePass.js"></script>
26-
<script src="js/postprocessing/ShaderPass.js"></script>
27-
<script src="js/postprocessing/MaskPass.js"></script>
23+
<script src="js/postprocessing2/EffectComposer2.js"></script>
24+
<script src="js/postprocessing2/ClearColorPass2.js"></script>
25+
<script src="js/postprocessing2/TexturePass2.js"></script>
26+
<script src="js/postprocessing2/ShaderPass2.js"></script>
27+
<script src="js/postprocessing2/MaskPass2.js"></script>
2828

2929
<script src="js/WebGL.js"></script>
3030
<script src="js/libs/stats.min.js"></script>
@@ -66,22 +66,21 @@
6666

6767
//
6868

69-
var clearPass = new THREE.ClearPass();
69+
var clearPass = new THREE.ClearColorPass2();
7070

71-
var clearMaskPass = new THREE.ClearMaskPass();
71+
var clearMaskPass = new THREE.ClearMaskPass2();
7272

73-
var maskPass1 = new THREE.MaskPass( scene1, camera );
74-
var maskPass2 = new THREE.MaskPass( scene2, camera );
73+
var maskPass1 = new THREE.MaskPass2( scene1, camera );
74+
var maskPass2 = new THREE.MaskPass2( scene2, camera );
7575

7676
var texture1 = new THREE.TextureLoader().load( 'textures/758px-Canestra_di_frutta_(Caravaggio).jpg' );
7777
texture1.minFilter = THREE.LinearFilter;
7878
var texture2 = new THREE.TextureLoader().load( 'textures/2294472375_24a3b8ef46_o.jpg' );
7979

80-
var texturePass1 = new THREE.TexturePass( texture1 );
81-
var texturePass2 = new THREE.TexturePass( texture2 );
80+
var texturePass1 = new THREE.TexturePass2( texture1 );
81+
var texturePass2 = new THREE.TexturePass2( texture2 );
8282

83-
var outputPass = new THREE.ShaderPass( THREE.CopyShader );
84-
outputPass.renderToScreen = true;
83+
var outputPass = new THREE.ShaderPass2( THREE.CopyShader );
8584

8685
var parameters = {
8786
minFilter: THREE.LinearFilter,
@@ -92,7 +91,7 @@
9291

9392
var renderTarget = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, parameters );
9493

95-
composer = new THREE.EffectComposer( renderer, renderTarget );
94+
composer = new THREE.EffectComposer2( renderer, renderTarget );
9695
composer.addPass( clearPass );
9796
composer.addPass( maskPass1 );
9897
composer.addPass( texturePass1 );
@@ -121,7 +120,7 @@
121120
torus.rotation.y = time / 2;
122121

123122
renderer.clear();
124-
composer.render( time );
123+
composer.render( );
125124

126125
}
127126

0 commit comments

Comments
 (0)