-
Notifications
You must be signed in to change notification settings - Fork 7.1k
/
PostFXPipeline.js
488 lines (447 loc) · 18.5 KB
/
PostFXPipeline.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../../utils/Class');
var ColorMatrix = require('../../../display/ColorMatrix');
var GetFastValue = require('../../../utils/object/GetFastValue');
var ShaderSourceFS = require('../shaders/PostFX-frag.js');
var ShaderSourceVS = require('../shaders/Quad-vert.js');
var WebGLPipeline = require('../WebGLPipeline');
/**
* @classdesc
* The Post FX Pipeline is a special kind of pipeline specifically for handling post
* processing effects. Where-as a standard Pipeline allows you to control the process
* of rendering Game Objects by configuring the shaders and attributes used to draw them,
* a Post FX Pipeline is designed to allow you to apply processing _after_ the Game Object/s
* have been rendered. Typical examples of post processing effects are bloom filters,
* blurs, light effects and color manipulation.
*
* The pipeline works by creating a tiny vertex buffer with just one single hard-coded quad
* in it. Game Objects can have a Post Pipeline set on them. Those objects are then rendered
* using their standard pipeline, but are redirected to the Render Targets owned by the
* post pipeline, which can then apply their own shaders and effects, before passing them
* back to the main renderer.
*
* Please see the Phaser 3 examples for further details on this extensive subject.
*
* The default fragment shader it uses can be found in `shaders/src/PostFX.frag`.
* The default vertex shader it uses can be found in `shaders/src/Quad.vert`.
*
* The default shader attributes for this pipeline are:
*
* `inPosition` (vec2, offset 0)
* `inTexCoord` (vec2, offset 8)
*
* The vertices array layout is:
*
* -1, 1 B----C 1, 1
* 0, 1 | /| 1, 1
* | / |
* | / |
* |/ |
* -1, -1 A----D 1, -1
* 0, 0 1, 0
*
* A = -1, -1 (pos) and 0, 0 (uv)
* B = -1, 1 (pos) and 0, 1 (uv)
* C = 1, 1 (pos) and 1, 1 (uv)
* D = 1, -1 (pos) and 1, 0 (uv)
*
* First tri: A, B, C
* Second tri: A, C, D
*
* Array index:
*
* 0 = Tri 1 - Vert A - x pos
* 1 = Tri 1 - Vert A - y pos
* 2 = Tri 1 - Vert A - uv u
* 3 = Tri 1 - Vert A - uv v
*
* 4 = Tri 1 - Vert B - x pos
* 5 = Tri 1 - Vert B - y pos
* 6 = Tri 1 - Vert B - uv u
* 7 = Tri 1 - Vert B - uv v
*
* 8 = Tri 1 - Vert C - x pos
* 9 = Tri 1 - Vert C - y pos
* 10 = Tri 1 - Vert C - uv u
* 11 = Tri 1 - Vert C - uv v
*
* 12 = Tri 2 - Vert A - x pos
* 13 = Tri 2 - Vert A - y pos
* 14 = Tri 2 - Vert A - uv u
* 15 = Tri 2 - Vert A - uv v
*
* 16 = Tri 2 - Vert C - x pos
* 17 = Tri 2 - Vert C - y pos
* 18 = Tri 2 - Vert C - uv u
* 19 = Tri 2 - Vert C - uv v
*
* 20 = Tri 2 - Vert D - x pos
* 21 = Tri 2 - Vert D - y pos
* 22 = Tri 2 - Vert D - uv u
* 23 = Tri 2 - Vert D - uv v
*
* @class PostFXPipeline
* @extends Phaser.Renderer.WebGL.WebGLPipeline
* @memberof Phaser.Renderer.WebGL.Pipelines
* @constructor
* @since 3.50.0
*
* @param {Phaser.Types.Renderer.WebGL.WebGLPipelineConfig} config - The configuration options for this pipeline.
*/
var PostFXPipeline = new Class({
Extends: WebGLPipeline,
initialize:
function PostFXPipeline (config)
{
config.renderTarget = GetFastValue(config, 'renderTarget', 1);
config.fragShader = GetFastValue(config, 'fragShader', ShaderSourceFS);
config.vertShader = GetFastValue(config, 'vertShader', ShaderSourceVS);
config.attributes = GetFastValue(config, 'attributes', [
{
name: 'inPosition',
size: 2
},
{
name: 'inTexCoord',
size: 2
}
]);
config.batchSize = 1;
config.vertices = [
-1, -1, 0, 0,
-1, 1, 0, 1,
1, 1, 1, 1,
-1, -1, 0, 0,
1, 1, 1, 1,
1, -1, 1, 0
];
WebGLPipeline.call(this, config);
this.isPostFX = true;
/**
* If this post-pipeline belongs to a Game Object or Camera, this contains a reference to it.
*
* @name Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#gameObject
* @type {Phaser.GameObjects.GameObject}
* @since 3.50.0
*/
this.gameObject;
/**
* A Color Matrix instance belonging to this pipeline.
*
* Used during calls to the `drawFrame` method.
*
* @name Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#colorMatrix
* @type {Phaser.Display.ColorMatrix}
* @since 3.50.0
*/
this.colorMatrix = new ColorMatrix();
/**
* A reference to the Full Frame 1 Render Target that belongs to the
* Utility Pipeline. This property is set during the `boot` method.
*
* This Render Target is the full size of the renderer.
*
* You can use this directly in Post FX Pipelines for multi-target effects.
* However, be aware that these targets are shared between all post fx pipelines.
*
* @name Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#fullFrame1
* @type {Phaser.Renderer.WebGL.RenderTarget}
* @default null
* @since 3.50.0
*/
this.fullFrame1;
/**
* A reference to the Full Frame 2 Render Target that belongs to the
* Utility Pipeline. This property is set during the `boot` method.
*
* This Render Target is the full size of the renderer.
*
* You can use this directly in Post FX Pipelines for multi-target effects.
* However, be aware that these targets are shared between all post fx pipelines.
*
* @name Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#fullFrame2
* @type {Phaser.Renderer.WebGL.RenderTarget}
* @default null
* @since 3.50.0
*/
this.fullFrame2;
/**
* A reference to the Half Frame 1 Render Target that belongs to the
* Utility Pipeline. This property is set during the `boot` method.
*
* This Render Target is half the size of the renderer.
*
* You can use this directly in Post FX Pipelines for multi-target effects.
* However, be aware that these targets are shared between all post fx pipelines.
*
* @name Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#halfFrame1
* @type {Phaser.Renderer.WebGL.RenderTarget}
* @default null
* @since 3.50.0
*/
this.halfFrame1;
/**
* A reference to the Half Frame 2 Render Target that belongs to the
* Utility Pipeline. This property is set during the `boot` method.
*
* This Render Target is half the size of the renderer.
*
* You can use this directly in Post FX Pipelines for multi-target effects.
* However, be aware that these targets are shared between all post fx pipelines.
*
* @name Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#halfFrame2
* @type {Phaser.Renderer.WebGL.RenderTarget}
* @default null
* @since 3.50.0
*/
this.halfFrame2;
if (this.renderer.isBooted)
{
this.manager = this.renderer.pipelines;
this.boot();
}
},
boot: function ()
{
WebGLPipeline.prototype.boot.call(this);
var utility = this.manager.UTILITY_PIPELINE;
this.fullFrame1 = utility.fullFrame1;
this.fullFrame2 = utility.fullFrame2;
this.halfFrame1 = utility.halfFrame1;
this.halfFrame2 = utility.halfFrame2;
this.set1i('uMainSampler', 0);
},
onDraw: function (renderTarget)
{
this.bindAndDraw(renderTarget);
},
/**
* Copy the `source` Render Target to the `target` Render Target.
*
* You can optionally set the brightness factor of the copy.
*
* The difference between this method and `drawFrame` is that this method
* uses a faster copy shader, where only the brightness can be modified.
* If you need color level manipulation, see `drawFrame` instead.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#copyFrame
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target.
* @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target.
* @param {number} [brightness=1] - The brightness value applied to the frame copy.
* @param {boolean} [clear=true] - Clear the target before copying?
* @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
*/
copyFrame: function (source, target, brightness, clear, clearAlpha)
{
this.manager.copyFrame(source, target, brightness, clear, clearAlpha);
},
/**
* Pops the framebuffer from the renderers FBO stack and sets that as the active target,
* then draws the `source` Render Target to it. It then resets the renderer textures.
*
* This should be done when you need to draw the _final_ results of a pipeline to the game
* canvas, or the next framebuffer in line on the FBO stack. You should only call this once
* in the `onDraw` handler and it should be the final thing called. Be careful not to call
* this if you need to actually use the pipeline shader, instead of the copy shader. In
* those cases, use the `bindAndDraw` method.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#copyToGame
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} source - The Render Target to draw from.
*/
copyToGame: function (source)
{
this.manager.copyToGame(source);
},
/**
* Copy the `source` Render Target to the `target` Render Target, using the
* given Color Matrix.
*
* The difference between this method and `copyFrame` is that this method
* uses a color matrix shader, where you have full control over the luminance
* values used during the copy. If you don't need this, you can use the faster
* `copyFrame` method instead.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#drawFrame
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target.
* @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target.
* @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
*/
drawFrame: function (source, target, clearAlpha)
{
this.manager.drawFrame(source, target, clearAlpha, this.colorMatrix);
},
/**
* Draws the `source1` and `source2` Render Targets to the `target` Render Target
* using a linear blend effect, which is controlled by the `strength` parameter.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#blendFrames
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} source1 - The first source Render Target.
* @param {Phaser.Renderer.WebGL.RenderTarget} source2 - The second source Render Target.
* @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target.
* @param {number} [strength=1] - The strength of the blend.
* @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
*/
blendFrames: function (source1, source2, target, strength, clearAlpha)
{
this.manager.blendFrames(source1, source2, target, strength, clearAlpha);
},
/**
* Draws the `source1` and `source2` Render Targets to the `target` Render Target
* using an additive blend effect, which is controlled by the `strength` parameter.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#blendFramesAdditive
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} source1 - The first source Render Target.
* @param {Phaser.Renderer.WebGL.RenderTarget} source2 - The second source Render Target.
* @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The target Render Target.
* @param {number} [strength=1] - The strength of the blend.
* @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
*/
blendFramesAdditive: function (source1, source2, target, strength, clearAlpha)
{
this.manager.blendFramesAdditive(source1, source2, target, strength, clearAlpha);
},
/**
* Clears the given Render Target.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#clearFrame
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} target - The Render Target to clear.
* @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
*/
clearFrame: function (target, clearAlpha)
{
this.manager.clearFrame(target, clearAlpha);
},
/**
* Copy the `source` Render Target to the `target` Render Target.
*
* The difference with this copy is that no resizing takes place. If the `source`
* Render Target is larger than the `target` then only a portion the same size as
* the `target` dimensions is copied across.
*
* You can optionally set the brightness factor of the copy.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#blitFrame
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target.
* @param {Phaser.Renderer.WebGL.RenderTarget} target - The target Render Target.
* @param {number} [brightness=1] - The brightness value applied to the frame copy.
* @param {boolean} [clear=true] - Clear the target before copying?
* @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
* @param {boolean} [eraseMode=false] - Erase source from target using ERASE Blend Mode?
*/
blitFrame: function (source, target, brightness, clear, clearAlpha, eraseMode)
{
this.manager.blitFrame(source, target, brightness, clear, clearAlpha, eraseMode);
},
/**
* Binds the `source` Render Target and then copies a section of it to the `target` Render Target.
*
* This method is extremely fast because it uses `gl.copyTexSubImage2D` and doesn't
* require the use of any shaders. Remember the coordinates are given in standard WebGL format,
* where x and y specify the lower-left corner of the section, not the top-left. Also, the
* copy entirely replaces the contents of the target texture, no 'merging' or 'blending' takes
* place.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#copyFrameRect
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} source - The source Render Target.
* @param {Phaser.Renderer.WebGL.RenderTarget} target - The target Render Target.
* @param {number} x - The x coordinate of the lower left corner where to start copying.
* @param {number} y - The y coordinate of the lower left corner where to start copying.
* @param {number} width - The width of the texture.
* @param {number} height - The height of the texture.
* @param {boolean} [clear=true] - Clear the target before copying?
* @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
*/
copyFrameRect: function (source, target, x, y, width, height, clear, clearAlpha)
{
this.manager.copyFrameRect(source, target, x, y, width, height, clear, clearAlpha);
},
/**
* Binds this pipeline and draws the `source` Render Target to the `target` Render Target.
*
* If no `target` is specified, it will pop the framebuffer from the Renderers FBO stack
* and use that instead, which should be done when you need to draw the final results of
* this pipeline to the game canvas.
*
* You can optionally set the shader to be used for the draw here, if this is a multi-shader
* pipeline. By default `currentShader` will be used. If you need to set a shader but not
* a target, just pass `null` as the `target` parameter.
*
* @method Phaser.Renderer.WebGL.Pipelines.PostFXPipeline#bindAndDraw
* @since 3.50.0
*
* @param {Phaser.Renderer.WebGL.RenderTarget} source - The Render Target to draw from.
* @param {Phaser.Renderer.WebGL.RenderTarget} [target] - The Render Target to draw to. If not set, it will pop the fbo from the stack.
* @param {boolean} [clear=true] - Clear the target before copying? Only used if `target` parameter is set.
* @param {boolean} [clearAlpha=true] - Clear the alpha channel when running `gl.clear` on the target?
* @param {Phaser.Renderer.WebGL.WebGLShader} [currentShader] - The shader to use during the draw.
*/
bindAndDraw: function (source, target, clear, clearAlpha, currentShader)
{
if (clear === undefined) { clear = true; }
if (clearAlpha === undefined) { clearAlpha = true; }
var gl = this.gl;
var renderer = this.renderer;
this.bind(currentShader);
this.set1i('uMainSampler', 0);
if (target)
{
gl.viewport(0, 0, target.width, target.height);
gl.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target.texture, 0);
if (clear)
{
if (clearAlpha)
{
gl.clearColor(0, 0, 0, 0);
}
else
{
gl.clearColor(0, 0, 0, 1);
}
gl.clear(gl.COLOR_BUFFER_BIT);
}
}
else
{
renderer.popFramebuffer(false, false, false);
if (!renderer.currentFramebuffer)
{
gl.viewport(0, 0, renderer.width, renderer.height);
}
}
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, source.texture);
gl.bufferData(gl.ARRAY_BUFFER, this.vertexData, gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLES, 0, 6);
if (!target)
{
renderer.resetTextures();
}
else
{
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
}
});
module.exports = PostFXPipeline;