Last active
June 25, 2020 05:06
-
-
Save ssube/96e9db52bbb6e310fc4096872ed027bb to your computer and use it in GitHub Desktop.
Revisions
-
ssube revised this gist
Jun 25, 2020 . 3 changed files with 218 additions and 51 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,6 +18,7 @@ A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shad - [`PostProcessor.update`](#postprocessorupdate) - [`PostProcessor.stop`](#postprocessorstop) - [Data](#data) - [Data `buffers`](#data-buffers) - [Data `effects`](#data-effects) - [Data `layers`](#data-layers) - [Data `mipmaps`](#data-mipmaps) @@ -48,6 +49,12 @@ A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shad ## Features - multiple input and output textures - offscreen buffers - access to textures loaded by Phaser - reusable `effects` composed into `layers` - set uniforms per layer ### Caveats - creates N+2 screen-sized textures for N effects @@ -63,7 +70,6 @@ A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shad - more effects - support binding sampler to TEXTURE0 other than `stage` buffer (bound by draw call) ## API @@ -102,11 +108,21 @@ The data structure used to initialize a `PostProcessor` instance describes the l example is attached. - `name` - `buffers` - `effects` - `layers` - `mipmaps` - `screen` ### Data `buffers` Additional offscreen buffers to create. The `scene` and `stage` buffers will always be created. - `name` - `size` - `x` - `y` ### Data `effects` The effect pipelines with shader source. @@ -146,10 +162,14 @@ The screen size. #### Dither TODO Reduce color space to pseudo 8-bit and scatter pixels. #### Edge Detect TODO Laplacian edge detection. #### Invert @@ -172,7 +192,7 @@ Gradiated desaturation with circular black edge. #### Madd (Mult, Sum) Add the previous `stage` to the original `scene`. #### Diff @@ -190,7 +210,7 @@ Offset pixels from the previous `stage` using a normal map from another texture. #### Gaussian Separable blur of 7 pixels using the Gaussian kernel: `[0.00038771, 0.01330373, 0.11098164, 0.22508352, 0.00038771, 0.01330373, 0.11098164]`. ### Tone Mapping This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,24 +1,41 @@ pipelines: - name: basic buffers: - name: offscreen layers: - name: clamp targets: - name: stage - name: offscreen uniforms: - name: uMin type: float value: 0.5 - name: uMax type: float value: 0.9 - name: blurX - name: blurY - name: blurX - name: blurY - name: madd - name: exposure uniforms: - name: uTarget type: float value: 0.5 - name: sepia - name: vignette - name: color-curve uniforms: - name: anchor1 type: vec2 value: [1.0, 0.0] - name: anchor2 type: vec2 value: [0.0, 1.0] - name: distort - name: recombine mipmaps: true screen: x: 1024 @@ -245,11 +262,14 @@ pipelines: { vec2 uv = outTexCoord.xy; vec4 texel = texture2D(foo, uv); vec4 faded = texel; /* kernel from https://gist.github.com/rasteron/2019a4890e0d6311297f */ faded.x = dot(texel.xyz, vec3(0.393, 0.769, 0.189)); faded.y = dot(texel.xyz, vec3(0.349, 0.686, 0.168)); faded.z = dot(texel.xyz, vec3(0.272, 0.534, 0.131)); gl_FragColor.xyz = mix(texel.xyz, faded.xyz, step(0.5, uv.x)); gl_FragColor.w = 1.0; } - name: vignette @@ -300,11 +320,11 @@ pipelines: vec2 normal = texture2D(normal, uv).xy; normal = normalize(normal * 2.0 - 1.0); vec2 offset = normal * pixel * 8.0; vec4 texel = texture2D(stage, uv + offset); vec4 original = texture2D(stage, uv); gl_FragColor.xyz = (original.xyz + mix(texel.xyz, original.xyz, step(0.5, uv.x))) / 2.0; gl_FragColor.w = 1.0; } - name: color-curve @@ -316,10 +336,11 @@ pipelines: const vec3 lumF = vec3(0.2125, 0.7145, 0.0721); const vec2 anchor0 = vec2(0.0, 0.0); const vec2 anchor3 = vec2(1.0, 1.0); uniform vec2 anchor1; uniform vec2 anchor2; uniform sampler2D stage; varying vec2 outTexCoord; @@ -343,3 +364,83 @@ pipelines: /* gl_FragColor.xyz = vec3(dist); */ gl_FragColor.w = 1.0; } - name: gaussianX samplers: - name: foo source: stage fragment: precision mediump float; uniform sampler2D foo; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 pixel = vec2(1.0) / vec2(1024.0, 768.0); vec2 uv = outTexCoord.xy; vec4 texN3 = texture2D(foo, vec2(uv.x - (pixel.x * 3.0), uv.y)) * 0.00038771; vec4 texN2 = texture2D(foo, vec2(uv.x - (pixel.x * 2.0), uv.y)) * 0.01330373; vec4 texN1 = texture2D(foo, vec2(uv.x - (pixel.x * 1.0), uv.y)) * 0.11098164; vec4 tex00 = texture2D(foo, uv) * 0.22508352; vec4 texP1 = texture2D(foo, vec2(uv.x + (pixel.x * 1.0), uv.y)) * 0.11098164; vec4 texP2 = texture2D(foo, vec2(uv.x + (pixel.x * 2.0), uv.y)) * 0.01330373; vec4 texP3 = texture2D(foo, vec2(uv.x + (pixel.x * 3.0), uv.y)) * 0.00038771; gl_FragColor = (texN3 + texN2 + texN1 + tex00 + texP1 + texP2 + texP3); gl_FragColor.w = 1.0; } - name: gaussianY samplers: - name: foo source: stage fragment: precision mediump float; uniform sampler2D foo; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 pixel = vec2(1.0) / vec2(1024.0, 768.0); vec2 uv = outTexCoord.xy; vec4 texN3 = texture2D(foo, vec2(uv.x, uv.y - (pixel.y * 3.0))) * 0.00038771; vec4 texN2 = texture2D(foo, vec2(uv.x, uv.y - (pixel.y * 2.0))) * 0.01330373; vec4 texN1 = texture2D(foo, vec2(uv.x, uv.y - (pixel.y * 1.0))) * 0.11098164; vec4 tex00 = texture2D(foo, uv) * 0.22508352; vec4 texP1 = texture2D(foo, vec2(uv.x, uv.y + (pixel.y * 1.0))) * 0.11098164; vec4 texP2 = texture2D(foo, vec2(uv.x, uv.y + (pixel.y * 2.0))) * 0.01330373; vec4 texP3 = texture2D(foo, vec2(uv.x, uv.y + (pixel.y * 3.0))) * 0.00038771; gl_FragColor = (texN3 + texN2 + texN1 + tex00 + texP1 + texP2 + texP3); gl_FragColor.w = 1.0; } - name: recombine samplers: - name: stage source: stage - name: offscreen source: offscreen fragment: precision mediump float; uniform sampler2D stage; uniform sampler2D offscreen; varying vec2 outTexCoord; void main() { vec2 uv = outTexCoord.xy; uv.y = 1.0 - uv.y; vec4 texel = texture2D(stage, outTexCoord); vec4 glow = texture2D(offscreen, uv); gl_FragColor.xyz = texel.xyz + glow.xyz; gl_FragColor.w = 1.0; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -10,6 +10,10 @@ export interface EffectSampler { source: string; } export interface EffectTarget { name: string; } export interface EffectUniformSingle { name: string; type: 'float'; @@ -24,23 +28,31 @@ export interface EffectUniformVector { export type EffectUniform = EffectUniformSingle | EffectUniformVector; export interface PipelineBuffer { mipmaps: boolean; name: string; size: Point; } export interface PipelineEffect { fragment: string; name: string; samplers: Array<EffectSampler>; vertex: string; } export interface PipelineLayer { name: string; targets: Array<EffectTarget>; uniforms: Array<EffectUniform>; } export interface PipelineData { buffers: Array<PipelineBuffer>; effects: Array<PipelineEffect>; layers: Array<PipelineLayer>; mipmaps: boolean; name: string; screen: Point; } @@ -105,25 +117,9 @@ class EffectPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline return this; } public cacheTextures(samplers: Array<CachedSampler>) { this.samplers.splice(0); this.samplers.push(...samplers); } } @@ -170,40 +166,73 @@ export class PostProcessor { this.buffers.set(buffer, rt); } for (const buffer of this.data.buffers) { const rt = this.createBuffer(buffer.name); this.buffers.set(buffer.name, rt); } for (const effect of this.data.effects) { const game = this.scene.game; const pipeline = new EffectPipeline(game, effect); this.effects.set(effect.name, pipeline); const qn = this.qualifyName(effect.name); this.renderer.addPipeline(qn, pipeline); const rt = this.createBuffer(effect.name); rt.setPipeline(qn); this.buffers.set(effect.name, rt); } this.cacheTextures(); } public cacheTextures() { /* this should happen after all effects (and their buffers) have been created and registered */ for (const data of this.data.effects) { const effect = mustGet(this.effects, data.name); const cache: Array<CachedSampler> = []; for (const { name, source } of data.samplers) { if (this.buffers.has(source)) { const texture = mustGet(this.buffers, source); cache.push({ name, texture, type: 'buffer', }); } else { // source is not a name and should not be qualified const texture = this.scene.textures.get(source); cache.push({ frame: texture.firstFrame, name, texture, type: 'texture', }); } } effect.cacheTextures(cache); } this.running = true; } public createBuffer(name: string) { const qn = this.qualifyName(name); const rt = this.scene.make.renderTexture({ height: this.data.screen.y, width: this.data.screen.x, x: 0, y: 0, }); rt.setName(qn); rt.setScrollFactor(0, 0); rt.setVisible(false); this.scene.textures.addRenderTexture(qn, rt); return rt; } @@ -230,21 +259,33 @@ export class PostProcessor { stageBuffer.draw(sceneBuffer); for (const layer of layers) { this.updateLayer(layer, stageBuffer); } } public updateLayer(layer: PipelineLayer, stageBuffer: Phaser.GameObjects.RenderTexture) { const buffer = mustGet(this.buffers, layer.name); const effect = mustGet(this.effects, layer.name); if (doesExist(layer.uniforms)) { for (const uniform of layer.uniforms) { this.updateUniform(effect, uniform); } } buffer.draw(stageBuffer); if (doesExist(layer.targets)) { for (const data of layer.targets) { const target = mustGet(this.buffers, data.name); target.draw(buffer); } } else { stageBuffer.draw(buffer); } if (this.data.mipmaps) { this.updateMips(stageBuffer); } } @@ -302,8 +343,13 @@ export class PostProcessor { this.scene.children.remove(fillRect); for (const name of this.buffers.keys()) { const qn = this.qualifyName(name); this.renderer.removePipeline(qn); this.scene.textures.remove(qn); } } protected qualifyName(name: string): string { return `postfx-${this.data.name}-${name}`; } } -
ssube revised this gist
Jun 25, 2020 . 1 changed file with 18 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -24,6 +24,8 @@ A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shad - [Data `screen`](#data-screen) - [Effects](#effects) - [Basics](#basics) - [Dither](#dither) - [Edge Detect](#edge-detect) - [Invert](#invert) - [Monochrome](#monochrome) - [Sepia](#sepia) @@ -51,15 +53,17 @@ A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shad - creates N+2 screen-sized textures for N effects - this can potentially be reduced to 2 or 3 (no N) by calling `buffer.setPipeline()` before `stage.draw(buffer)` - generating mipmaps for NP2 textures require a WebGL2 context - the `exposure` effect requires WebGL2 - the Phaser game must be created with the same WebGL2 context - `textureLod` GLSL function requires `#version 300 es` shaders - the fragment and vertex program version must match within the same layer (a `300 es` vertex shader is provided) - mixing shader versions on different layers is allowed ### TODO - more effects - support binding sampler to TEXTURE0 other than `stage` buffer (bound by draw call) - support writing to any target buffer, not just `stage` ## API @@ -140,6 +144,14 @@ The screen size. ### Basics #### Dither Reduce color space to pseudo 8-bit. #### Edge Detect Laplacian edge detection. #### Invert Invert colors per-channel. @@ -197,9 +209,7 @@ This effect can act as a low-pass/high-cut or high-pass/low-cut filter by settin #### Curve Adjust scene luminance using a 4-point curve. #### Color -
ssube revised this gist
Jun 25, 2020 . 1 changed file with 15 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,5 @@ import { doesExist, mustExist, mustGet } from '@apextoaster/js-utils'; import { defaultTo } from 'lodash'; import * as Phaser from 'phaser'; import { CENTER_SPLIT } from '../constants'; @@ -51,8 +52,13 @@ export type CachedSampler = { name: string; type: 'texture'; texture: Phaser.Textures.Texture; frame: string; }; export interface FrameDict { [key: string]: Phaser.Textures.Frame; } const REQUIRED_BUFFERS = [ 'scene', 'stage', @@ -87,13 +93,13 @@ class EffectPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline const sampler = this.samplers[i]; if (sampler.type === 'buffer') { renderer.setTexture2D(sampler.texture.glTexture, i); } else { const frameName = defaultTo(sampler.frame, sampler.texture.firstFrame); const frame = (sampler.texture.frames as FrameDict)[frameName]; renderer.setTexture2D(frame.glTexture, i); } renderer.setInt1(program, sampler.name, i); } return this; @@ -111,6 +117,7 @@ class EffectPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline } else { const texture = textures.get(source); this.samplers.push({ frame: texture.firstFrame, name, texture, type: 'texture', @@ -148,10 +155,10 @@ export class PostProcessor { } public getContext(): WebGL2RenderingContext { return this.renderer.gl as WebGL2RenderingContext; } public get renderer(): Phaser.Renderer.WebGL.WebGLRenderer { return this.scene.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer; } @@ -168,7 +175,7 @@ export class PostProcessor { const pipeline = new EffectPipeline(game, effect); this.effects.set(effect.name, pipeline); this.renderer.addPipeline(effect.name, pipeline); const rt = this.createBuffer(effect.name); rt.setPipeline(effect.name); @@ -294,9 +301,8 @@ export class PostProcessor { const fillRect = mustExist(this.fillRect); this.scene.children.remove(fillRect); for (const name of this.buffers.keys()) { this.renderer.removePipeline(name); this.scene.textures.remove(name); } } -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 37 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -9,11 +9,20 @@ export interface EffectSampler { source: string; } export interface EffectUniformSingle { name: string; type: 'float'; value: number; } export interface EffectUniformVector { name: string; type: 'float1' | 'float2' | 'float3' | 'float4' | 'vec1' | 'vec2' | 'vec3' | 'vec4'; value: Array<number>; } export type EffectUniform = EffectUniformSingle | EffectUniformVector; export interface PipelineEffect { name: string; fragment: string; @@ -219,7 +228,7 @@ export class PostProcessor { if (doesExist(layer.uniforms)) { for (const uniform of layer.uniforms) { this.updateUniform(effect, uniform); } } @@ -254,14 +263,39 @@ export class PostProcessor { gl.activeTexture(active); } public updateUniform(effect: EffectPipeline, uniform: EffectUniform) { switch (uniform.type) { case 'float1': case 'vec1': effect.setFloat1v(uniform.name, Float32Array.from(uniform.value)); break; case 'float2': case 'vec2': effect.setFloat2v(uniform.name, Float32Array.from(uniform.value)); break; case 'float3': case 'vec3': effect.setFloat3v(uniform.name, Float32Array.from(uniform.value)); break; case 'float4': case 'vec4': effect.setFloat4v(uniform.name, Float32Array.from(uniform.value)); break; case 'float': default: effect.setFloat1(uniform.name, uniform.value); break; } } public stop() { this.running = false; const fillRect = mustExist(this.fillRect); this.scene.children.remove(fillRect); const renderer = this.getRenderer(); for (const name of this.buffers.keys()) { renderer.removePipeline(name); this.scene.textures.remove(name); } -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 37 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -306,4 +306,40 @@ pipelines: gl_FragColor.xyz = mix(texel.xyz, original.xyz, step(0.5, uv.x)); gl_FragColor.w = 1.0; } - name: color-curve samplers: - name: stage source: stage fragment: precision mediump float; const vec3 lumF = vec3(0.2125, 0.7145, 0.0721); const vec2 anchor0 = vec2(0.0, 0.0); const vec2 anchor1 = vec2(0.0, 1.0); const vec2 anchor2 = vec2(1.0, 0.0); const vec2 anchor3 = vec2(1.0, 1.0); uniform sampler2D stage; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 uv = outTexCoord.xy; vec4 texel = texture2D(stage, uv); float lum = dot(texel.xyz, lumF); vec2 curve = /* 4-point bezier */ (pow(1.0 - lum, 3.0) * anchor0) + (pow(1.0 - lum, 2.0) * anchor1 * 3.0 * lum) + ((1.0 - lum) * anchor2 * 3.0 * pow(lum, 2.0)) + (pow(lum, 3.0) * anchor3); float dist = 0.5 + pow(curve.y - lum, 0.5); gl_FragColor.xyz = texel.xyz * dist; /* gl_FragColor.xyz = vec3(dist); */ gl_FragColor.w = 1.0; } -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 18 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -138,6 +138,14 @@ export class PostProcessor { return mustGet(this.buffers, key); } public getContext(): WebGL2RenderingContext { return this.getRenderer().gl as WebGL2RenderingContext; } public getRenderer(): Phaser.Renderer.WebGL.WebGLRenderer { return this.scene.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer; } public create() { this.fillRect = this.scene.add.rectangle(0, 0, this.data.screen.x, this.data.screen.y, 0x00, 1.0); @@ -151,14 +159,14 @@ export class PostProcessor { const pipeline = new EffectPipeline(game, effect); this.effects.set(effect.name, pipeline); this.getRenderer().addPipeline(effect.name, pipeline); const rt = this.createBuffer(effect.name); rt.setPipeline(effect.name); this.buffers.set(effect.name, rt); } /* this should happen after all effects (and their buffers) have been created and registered */ for (const data of this.data.effects) { const effect = mustGet(this.effects, data.name); effect.cacheTextures(data.samplers, this.buffers, this.scene.textures); @@ -184,7 +192,7 @@ export class PostProcessor { return rt; } public update(objects: Array<RenderObject>, layers: Array<PipelineLayer>, camera: Phaser.Cameras.Scene2D.Camera) { if (!this.running) { return; } @@ -194,12 +202,13 @@ export class PostProcessor { const fillRect = mustExist(this.fillRect); sceneBuffer.draw(fillRect, fillRect.displayWidth / CENTER_SPLIT, fillRect.displayHeight / CENTER_SPLIT); for (const obj of objects) { sceneBuffer.draw(obj, Math.floor(-camera.scrollX + obj.x), Math.floor(-camera.scrollY + obj.y)); } if (this.data.mipmaps) { this.updateMips(sceneBuffer); } stageBuffer.draw(sceneBuffer); @@ -218,12 +227,15 @@ export class PostProcessor { stageBuffer.draw(buffer); if (this.data.mipmaps) { this.updateMips(stageBuffer); } } } public updateMips(buffer: Phaser.GameObjects.RenderTexture) { // use the RenderTexture's context in case it is different const gl = (buffer.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl as WebGL2RenderingContext; const active = gl.getParameter(gl.ACTIVE_TEXTURE); const levels = Math.ceil(Math.log2(Math.max(buffer.height, buffer.width))); -
ssube revised this gist
Jun 24, 2020 . 2 changed files with 19 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -42,6 +42,7 @@ A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shad - [Exposure](#exposure) - [Compound Effects](#compound-effects) - [Bloom](#bloom) - [License](#license) ## Features @@ -226,3 +227,10 @@ much more complex effects. - X - Y - Sum ## License This is not really much code, needs plenty of cleanup, and none of the shaders are original. The `postprocessor.ts` and `data.yml` files are public domain, excluding the `exposure` effect's `vertex` shader, which was copied from Phaser's source. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -34,6 +34,16 @@ export interface PipelineData { screen: Point; } export type CachedSampler = { name: string; type: 'buffer'; texture: Phaser.GameObjects.RenderTexture; } | { name: string; type: 'texture'; texture: Phaser.Textures.Texture; }; const REQUIRED_BUFFERS = [ 'scene', 'stage', @@ -44,15 +54,7 @@ const MAX_SAMPLERS = 8; export type RenderObject = Phaser.GameObjects.GameObject & Phaser.GameObjects.Components.Transform; class EffectPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline { protected samplers: Array<CachedSampler>; constructor(game: Phaser.Game, effect: PipelineEffect) { super({ -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 60 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,6 +17,11 @@ A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shad - [`PostProcessor.create`](#postprocessorcreate) - [`PostProcessor.update`](#postprocessorupdate) - [`PostProcessor.stop`](#postprocessorstop) - [Data](#data) - [Data `effects`](#data-effects) - [Data `layers`](#data-layers) - [Data `mipmaps`](#data-mipmaps) - [Data `screen`](#data-screen) - [Effects](#effects) - [Basics](#basics) - [Invert](#invert) @@ -27,8 +32,9 @@ A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shad - [Madd (Mult, Sum)](#madd-mult-sum) - [Diff](#diff) - [Blur](#blur) - [Basic](#basic) - [Distort](#distort) - [Gaussian](#gaussian) - [Tone Mapping](#tone-mapping) - [Clamp](#clamp) - [Curve](#curve) @@ -85,6 +91,50 @@ Render a list of `objects` using the post-processing `layers` positioned for the Shut down the post-processing pipelines and clean up cached textures and WebGL resources. ## Data The data structure used to initialize a `PostProcessor` instance describes the layers and effects they use. An example is attached. - `name` - `effects` - `layers` - `mipmaps` - `screen` ### Data `effects` The effect pipelines with shader source. - `name` - `fragment`: fragment program source - `samplers`: - `name`: uniform name as it appears in the fragment program - `source`: buffer (`scene`, `stage`) or texture name - `vertex`: vertex program source ### Data `layers` A list of effects to be applied in order, with optional uniforms for each. - `name`: the effect `name` - `uniforms`: - `name`: the uniform name within the shader program - `value`: a float1 value ### Data `mipmaps` Update mipmaps for the `scene` and `stage` buffers each frame. *Note:* This requires WebGL2 for NP2 framebuffers. ### Data `screen` The screen size. - `x` - `y` ## Effects ### Basics @@ -117,13 +167,17 @@ TODO ### Blur #### Basic Separable blur of 7 pixels using the kernel `[0.125, 0.25, 0.5, 1.0, 0.5, 0.25, 0.125]`. #### Distort Offset pixels from the previous `stage` using a normal map from another texture. #### Gaussian TODO ### Tone Mapping -
ssube revised this gist
Jun 24, 2020 . 2 changed files with 4 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ # Phaser PostFX A flexible, layered post-processing system for PhaserJS 3. Apply fullscreen shaders to select scene elements. ## Contents @@ -51,8 +51,7 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade ### TODO - more effects - support binding sampler to TEXTURE0 other than stage (forced by draw call) - set uniforms other than float1 ## API This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -302,7 +302,8 @@ pipelines: vec2 offset = normal * pixel * 10.0; vec4 texel = texture2D(stage, uv + offset); vec4 original = texture2D(stage, uv); gl_FragColor.xyz = mix(texel.xyz, original.xyz, step(0.5, uv.x)); gl_FragColor.w = 1.0; } -
ssube revised this gist
Jun 24, 2020 . 3 changed files with 80 additions and 28 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -23,12 +23,12 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade - [Monochrome](#monochrome) - [Sepia](#sepia) - [Vignette](#vignette) - [Blend](#blend) - [Madd (Mult, Sum)](#madd-mult-sum) - [Diff](#diff) - [Blur](#blur) - [Basic X](#basic-x) - [Basic Y](#basic-y) - [Tone Mapping](#tone-mapping) - [Clamp](#clamp) - [Curve](#curve) @@ -63,7 +63,8 @@ Tint-based pipeline for shader effects with texture caching, sampler binding, an #### `EffectPipeline.cacheTextures` Look up required textures from `buffers` and the scene's texture manager, and cache them with the uniform name to which they will be bound. ### `PostProcessor` @@ -105,25 +106,25 @@ Partial desaturation with overall brown tint, like old-fashioned photographs. Gradiated desaturation with circular black edge. ### Blend #### Madd (Mult, Sum) Add the previos `stage` to the original `scene`. #### Diff TODO ### Blur #### Basic X Blurs 7 pixels in the X direction using the kernel `[0.125, 0.25, 0.5, 1.0, 0.5, 0.25, 0.125]`. #### Basic Y Blurs 7 pixels in the Y direction using the kernel `[0.125, 0.25, 0.5, 1.0, 0.5, 0.25, 0.125]`. ### Tone Mapping This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,14 +4,15 @@ pipelines: - name: clamp uniforms: - name: uMin value: 0.5 - name: uMax value: 0.9 - name: blurX - name: blurY - name: blurX - name: blurY - name: madd - name: distort - name: exposure uniforms: - name: uTarget @@ -272,7 +273,36 @@ pipelines: float dist = pow(uv.x - 0.5, 2.0) + pow(uv.y - 0.5, 2.0); dist = 1.0 - clamp(dist, 0.0, 1.0); gl_FragColor.xyz = mix(texel.xyz, vec3(lum), 1.0 - pow(dist, 9.0)); /* desat */ gl_FragColor.xyz = gl_FragColor.xyz * vec3(pow(dist, 3.0)); /* darken */ gl_FragColor.w = 1.0; } - name: distort samplers: - name: stage source: stage - name: normal source: glass-normal fragment: precision mediump float; uniform sampler2D stage; uniform sampler2D normal; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 pixel = vec2(1.0 / 1024.0, 1.0 / 768.0); vec2 uv = outTexCoord.xy; vec2 normal = texture2D(normal, uv).xy; normal = normalize(normal * 2.0 - 1.0); vec2 offset = normal * pixel * 10.0; vec4 texel = texture2D(stage, uv + offset); gl_FragColor.xyz = texel.xyz; gl_FragColor.w = 1.0; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -46,7 +46,12 @@ export type RenderObject = Phaser.GameObjects.GameObject & Phaser.GameObjects.Co class EffectPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline { protected samplers: Array<{ name: string; type: 'buffer'; texture: Phaser.GameObjects.RenderTexture; } | { name: string; type: 'texture'; texture: Phaser.Textures.Texture; }>; constructor(game: Phaser.Game, effect: PipelineEffect) { @@ -68,22 +73,38 @@ class EffectPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline const sl = Math.min(MAX_SAMPLERS, this.samplers.length); for (let i = 0; i < sl; ++i) { const sampler = this.samplers[i]; if (sampler.type === 'buffer') { renderer.setTexture2D(sampler.texture.glTexture, i); renderer.setInt1(program, sampler.name, i); } else { /* eslint-disable-next-line */ const frame = (sampler.texture.frames as any)[sampler.texture.firstFrame] as Phaser.Textures.Frame; renderer.setTexture2D(frame.glTexture, i); renderer.setInt1(program, sampler.name, i); } } return this; } public cacheTextures(samplers: Array<EffectSampler>, buffers: Map<string, Phaser.GameObjects.RenderTexture>, textures: Phaser.Textures.TextureManager) { for (const { name, source } of samplers) { if (buffers.has(source)) { const texture = mustGet(buffers, source); this.samplers.push({ name, texture, type: 'buffer', }); } else { const texture = textures.get(source); this.samplers.push({ name, texture, type: 'texture', }); } } } } @@ -138,7 +159,7 @@ export class PostProcessor { for (const data of this.data.effects) { const effect = mustGet(this.effects, data.name); effect.cacheTextures(data.samplers, this.buffers, this.scene.textures); } this.running = true; -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 10 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,6 +7,7 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade - [Phaser PostFX](#phaser-postfx) - [Contents](#contents) - [Features](#features) - [Caveats](#caveats) - [TODO](#todo) - [API](#api) - [`EffectPipeline`](#effectpipeline) @@ -38,6 +39,15 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade ## Features ### Caveats - creates N+2 screen-sized textures for N effects - this can potentially be reduced to 2 or 3 (no N) by calling `buffer.setPipeline()` before `stage.draw(buffer)` - generating mipmaps for NP2 textures require a WebGL2 context - the `textureLod` GLSL function requires a WebGL2 context and `#version 300 es` pragmas - the versions must match between fragment and vertex programs in the same layer, so this requires a `300 es` vertex shader - mixing versions on different layers is allowed ### TODO - more effects -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 0 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -89,14 +89,10 @@ Luminance-based grayscale. #### Sepia Partial desaturation with overall brown tint, like old-fashioned photographs. #### Vignette Gradiated desaturation with circular black edge. ### Blur -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 6 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -16,7 +16,7 @@ pipelines: uniforms: - name: uTarget value: 0.5 # name: sepia - name: vignette mipmaps: true screen: @@ -267,9 +267,12 @@ pipelines: { vec2 uv = outTexCoord.xy; vec4 texel = texture2D(stage, uv); float lum = (texel.x * 0.3) + (texel.y * 0.6) + (texel.z * 0.1); float dist = pow(uv.x - 0.5, 2.0) + pow(uv.y - 0.5, 2.0); dist = 1.0 - clamp(dist, 0.0, 1.0); gl_FragColor.xyz = mix(texel.xyz, vec3(lum), 1.0 - pow(dist, 12.0)); /* desat */ gl_FragColor.xyz = gl_FragColor.xyz * vec3(pow(dist, 3.0)); /* darken */ gl_FragColor.w = 1.0; } -
ssube revised this gist
Jun 24, 2020 . 2 changed files with 68 additions and 11 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -12,6 +12,7 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade - [`EffectPipeline`](#effectpipeline) - [`EffectPipeline.cacheTextures`](#effectpipelinecachetextures) - [`PostProcessor`](#postprocessor) - [`new PostProcessor`](#new-postprocessor) - [`PostProcessor.create`](#postprocessorcreate) - [`PostProcessor.update`](#postprocessorupdate) - [`PostProcessor.stop`](#postprocessorstop) @@ -41,19 +42,27 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade - more effects - bind static textures to samplers (other than buffers) - support binding sampler to TEXTURE0 other than stage (forced by draw call) - set uniforms other than float1 ## API ### `EffectPipeline` Tint-based pipeline for shader effects with texture caching, sampler binding, and other niceties. #### `EffectPipeline.cacheTextures` Look up required textures from `buffers` and cache them with the uniform name to which they will be bound. ### `PostProcessor` Manages the buffers and pipelines for post-processing a particular scene. Methods match the scene lifecycle. #### `new PostProcessor` Create a new post-processing pipeline from `data` and bound to the `scene`. #### `PostProcessor.create` Set up the post-processing pipelines and render textures, compiling shaders and caching textures. @@ -72,20 +81,24 @@ Shut down the post-processing pipelines and clean up cached textures and WebGL r #### Invert Invert colors per-channel. #### Monochrome Luminance-based grayscale. #### Sepia TODO Partial desaturation with overall brown tint, like old-fashioned photographs. #### Vignette TODO Gradiated desaturation with circular black edge. ### Blur #### Basic X This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,4 @@ pipelines: - name: basic layers: - name: clamp @@ -17,8 +16,8 @@ post: uniforms: - name: uTarget value: 0.5 - name: sepia - name: vignette mipmaps: true screen: x: 1024 @@ -43,7 +42,7 @@ post: vec4 texel = texture2D(foo, uv); vec4 invert = vec4(1.0) - texel; gl_FragColor = mix(invert, texel, step(uv.y, 0.6)); gl_FragColor.w = 1.0; } - name: monochrome @@ -66,7 +65,7 @@ post: float lum = (texel.x * 0.3) + (texel.y * 0.6) + (texel.z * 0.1); vec4 lumtex = vec4(lum, lum, lum, 1.0); gl_FragColor = mix(texel, lumtex, step(0.3, uv.y)); gl_FragColor.w = 1.0; } - name: blurX @@ -229,3 +228,48 @@ post: gl_FragColor = texel; gl_FragColor.w = 1.0; } - name: sepia samplers: - name: stage source: stage fragment: precision mediump float; uniform sampler2D foo; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 uv = outTexCoord.xy; vec4 texel = texture2D(foo, uv); /* kernel from https://gist.github.com/rasteron/2019a4890e0d6311297f */ gl_FragColor.x = dot(texel.xyz, vec3(0.393, 0.769, 0.189)); gl_FragColor.y = dot(texel.xyz, vec3(0.349, 0.686, 0.168)); gl_FragColor.z = dot(texel.xyz, vec3(0.272, 0.534, 0.131)); gl_FragColor.w = 1.0; } - name: vignette samplers: - name: stage source: stage fragment: precision mediump float; uniform sampler2D stage; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 uv = outTexCoord.xy; vec4 texel = texture2D(stage, uv); float dist = pow(uv.x - 0.5, 2.0) + pow(uv.y - 0.5, 2.0); float cd = pow(1.0 - clamp(dist, 0.0, 1.0), 6.0); gl_FragColor.xyz = texel.xyz * vec3(cd); gl_FragColor.w = 1.0; } -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 3 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -41,7 +41,8 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade - more effects - bind static textures to samplers (other than buffers) - support binding sampler to `TEXTURE0` other than `stage` (forced by draw call) - set uniforms other than `float1` ## API @@ -145,7 +146,7 @@ much more complex effects. ### Bloom - Clamp (high pass) - high: 1.0 - low: 0.8 - Blur (repeat as needed) -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 15 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,6 +6,8 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade - [Phaser PostFX](#phaser-postfx) - [Contents](#contents) - [Features](#features) - [TODO](#todo) - [API](#api) - [`EffectPipeline`](#effectpipeline) - [`EffectPipeline.cacheTextures`](#effectpipelinecachetextures) @@ -33,6 +35,14 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade - [Compound Effects](#compound-effects) - [Bloom](#bloom) ## Features ### TODO - more effects - bind static textures to samplers (other than buffers) - set uniforms other than `float` ## API ### `EffectPipeline` @@ -89,7 +99,7 @@ Blurs 7 pixels in the Y direction using the kernel `[0.125, 0.25, 0.5, 1.0, 0.5, #### Madd (Mult, Sum) Add the previos `stage` to the original `scene`. #### Diff @@ -112,10 +122,14 @@ This effect can act as a low-pass/high-cut or high-pass/low-cut filter by settin #### Curve TODO Adjust scene colors using a 2-point curve. #### Color TODO Adjust scene colors using a 2D tonemap texture, based on the biome tint/color palette effect from Crysis 1. #### Exposure -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 2 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,7 +3,6 @@ import * as Phaser from 'phaser'; import { CENTER_SPLIT } from '../constants'; import { Point } from '../entity'; export interface EffectSampler { name: string; @@ -12,8 +11,7 @@ export interface EffectSampler { export interface EffectUniform { name: string; value: number; } export interface PipelineEffect { @@ -39,7 +37,6 @@ export interface PipelineData { const REQUIRED_BUFFERS = [ 'scene', 'stage', ]; const MAX_SAMPLERS = 8; @@ -171,7 +168,6 @@ export class PostProcessor { const sceneBuffer = mustGet(this.buffers, 'scene'); const stageBuffer = mustGet(this.buffers, 'stage'); const fillRect = mustExist(this.fillRect); sceneBuffer.draw(fillRect, fillRect.displayWidth / CENTER_SPLIT, fillRect.displayHeight / CENTER_SPLIT); @@ -196,9 +192,7 @@ export class PostProcessor { } buffer.draw(stageBuffer); stageBuffer.draw(buffer); if (this.data.mipmaps) { this.updateMips(stageBuffer, gl); -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 12 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,12 +7,12 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade - [Phaser PostFX](#phaser-postfx) - [Contents](#contents) - [API](#api) - [`EffectPipeline`](#effectpipeline) - [`EffectPipeline.cacheTextures`](#effectpipelinecachetextures) - [`PostProcessor`](#postprocessor) - [`PostProcessor.create`](#postprocessorcreate) - [`PostProcessor.update`](#postprocessorupdate) - [`PostProcessor.stop`](#postprocessorstop) - [Effects](#effects) - [Basics](#basics) - [Invert](#invert) @@ -35,23 +35,23 @@ A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shade ## API ### `EffectPipeline` #### `EffectPipeline.cacheTextures` Look up required textures and cache them with the uniform name to which they will be bound. ### `PostProcessor` #### `PostProcessor.create` Set up the post-processing pipelines and render textures, compiling shaders and caching textures. #### `PostProcessor.update` Render a list of `objects` using the post-processing `layers` positioned for the `camera`. #### `PostProcessor.stop` Shut down the post-processing pipelines and clean up cached textures and WebGL resources. -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -93,15 +93,15 @@ class EffectPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline export class PostProcessor { protected readonly data: PipelineData; protected readonly scene: Phaser.Scene; protected readonly buffers: Map<string, Phaser.GameObjects.RenderTexture>; protected readonly effects: Map<string, EffectPipeline>; protected running: boolean; protected fillRect?: Phaser.GameObjects.Rectangle; constructor(data: PipelineData, scene: Phaser.Scene) { this.data = data; this.scene = scene; this.running = false; -
ssube revised this gist
Jun 24, 2020 . 1 changed file with 140 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,140 @@ # Phaser PostFX A flexible layered post-processing system for PhaserJS 3. Apply fullscreen shaders to select scene elements. ## Contents - [Phaser PostFX](#phaser-postfx) - [Contents](#contents) - [API](#api) - [`Pipeline`](#pipeline) - [`Pipeline.cacheTextures`](#pipelinecachetextures) - [`PostPipeline`](#postpipeline) - [`PostPipeline.create`](#postpipelinecreate) - [`PostPipeline.update`](#postpipelineupdate) - [`PostPipeline.stop`](#postpipelinestop) - [Effects](#effects) - [Basics](#basics) - [Invert](#invert) - [Monochrome](#monochrome) - [Sepia](#sepia) - [Vignette](#vignette) - [Blur](#blur) - [Basic X](#basic-x) - [Basic Y](#basic-y) - [Combinators](#combinators) - [Madd (Mult, Sum)](#madd-mult-sum) - [Diff](#diff) - [Tone Mapping](#tone-mapping) - [Clamp](#clamp) - [Curve](#curve) - [Color](#color) - [Exposure](#exposure) - [Compound Effects](#compound-effects) - [Bloom](#bloom) ## API ### `Pipeline` #### `Pipeline.cacheTextures` Look up required textures and cache them with the uniform name to which they will be bound. ### `PostPipeline` #### `PostPipeline.create` Set up the post-processing pipelines and render textures, compiling shaders and caching textures. #### `PostPipeline.update` Render a list of `objects` using the post-processing `layers` positioned for the `camera`. #### `PostPipeline.stop` Shut down the post-processing pipelines and clean up cached textures and WebGL resources. ## Effects ### Basics #### Invert Invert colors: replace black with white and vice versa, except for alpha channel. #### Monochrome Luminance-based grayscale with tints. #### Sepia TODO #### Vignette TODO ### Blur #### Basic X Blurs 7 pixels in the X direction using the kernel `[0.125, 0.25, 0.5, 1.0, 0.5, 0.25, 0.125]`. #### Basic Y Blurs 7 pixels in the Y direction using the kernel `[0.125, 0.25, 0.5, 1.0, 0.5, 0.25, 0.125]`. ### Combinators #### Madd (Mult, Sum) TODO #### Diff TODO ### Tone Mapping #### Clamp Clamp scene colors between the high and low points, then expand that to fill the full `[0.0, 1.0)` texture range. This effect can act as a low-pass/high-cut or high-pass/low-cut filter by setting the range: - high pass: - `uMin`: desired cutoff - `uMax`: 1.0 - low pass: - `uMin`: 0.0 - `uMax`: desired cutoff #### Curve Adjust scene colors using a 2-point curve. #### Color Adjust scene colors using a 2D tonemap texture, based on the biome tint/color palette effect from Crysis 1. #### Exposure A _very_ rudimentary and not at all production-suitable fake HDR effect. *Note:* Requires WebGL2 and GLSL version 300 for the `textureLod` function and generating mipmaps on NP2 textures. ## Compound Effects Most effects can be used on their own, but may not be useful. Including a few layers in the right order can produce much more complex effects. ### Bloom - Clamp (high threshold pass) - high: 1.0 - low: 0.8 - Blur (repeat as needed) - X - Y - Sum -
ssube created this gist
Jun 24, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,231 @@ post: - name: basic layers: - name: clamp uniforms: - name: uMin value: 0.8 - name: uMax value: 1.0 - name: blurX - name: blurY - name: blurX - name: blurY - name: madd - name: exposure uniforms: - name: uTarget value: 0.5 - name: monochrome - name: invert mipmaps: true screen: x: 1024 y: 768 effects: - name: invert samplers: - name: foo source: stage fragment: precision mediump float; uniform sampler2D foo; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 uv = outTexCoord.xy; vec4 texel = texture2D(foo, uv); vec4 invert = vec4(1.0) - texel; gl_FragColor = mix(invert, texel, step(uv.y, 0.5)); gl_FragColor.w = 1.0; } - name: monochrome samplers: - name: foo source: stage fragment: precision mediump float; uniform sampler2D foo; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 uv = outTexCoord.xy; vec4 texel = texture2D(foo, uv); float lum = (texel.x * 0.3) + (texel.y * 0.6) + (texel.z * 0.1); vec4 lumtex = vec4(lum, lum, lum, 1.0); gl_FragColor = mix(texel, lumtex, step(0.5, uv.y)); gl_FragColor.w = 1.0; } - name: blurX samplers: - name: foo source: stage fragment: precision mediump float; uniform sampler2D foo; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 pixel = vec2(1.0) / vec2(1024.0, 768.0); vec2 uv = outTexCoord.xy; vec4 texN3 = texture2D(foo, vec2(uv.x - (pixel.x * 3.0), uv.y)) * 0.125; vec4 texN2 = texture2D(foo, vec2(uv.x - (pixel.x * 2.0), uv.y)) * 0.25; vec4 texN1 = texture2D(foo, vec2(uv.x - (pixel.x * 1.0), uv.y)) * 0.5; vec4 tex00 = texture2D(foo, uv); vec4 texP1 = texture2D(foo, vec2(uv.x + (pixel.x * 1.0), uv.y)) * 0.5; vec4 texP2 = texture2D(foo, vec2(uv.x + (pixel.x * 2.0), uv.y)) * 0.25; vec4 texP3 = texture2D(foo, vec2(uv.x + (pixel.x * 3.0), uv.y)) * 0.125; gl_FragColor = (texN3 + texN2 + texN1 + tex00 + texP1 + texP2 + texP3) / 2.75; gl_FragColor.w = 1.0; } - name: blurY samplers: - name: foo source: stage fragment: precision mediump float; uniform sampler2D foo; varying vec2 outTexCoord; varying vec4 outTint; void main() { vec2 pixel = vec2(1.0) / vec2(1024.0, 768.0); vec2 uv = outTexCoord.xy; vec4 texN3 = texture2D(foo, vec2(uv.x, uv.y - (pixel.y * 3.0))) * 0.125; vec4 texN2 = texture2D(foo, vec2(uv.x, uv.y - (pixel.y * 2.0))) * 0.25; vec4 texN1 = texture2D(foo, vec2(uv.x, uv.y - (pixel.y * 1.0))) * 0.5; vec4 tex00 = texture2D(foo, uv); vec4 texP1 = texture2D(foo, vec2(uv.x, uv.y + (pixel.y * 1.0))) * 0.5; vec4 texP2 = texture2D(foo, vec2(uv.x, uv.y + (pixel.y * 2.0))) * 0.25; vec4 texP3 = texture2D(foo, vec2(uv.x, uv.y + (pixel.y * 3.0))) * 0.125; gl_FragColor = (texN3 + texN2 + texN1 + tex00 + texP1 + texP2 + texP3) / 2.75; gl_FragColor.w = 1.0; } - name: exposure samplers: - name: foo source: stage - name: bar source: scene fragment: | #version 300 es precision mediump float; uniform sampler2D foo; uniform sampler2D bar; uniform vec2 uResolution; uniform float uTime; uniform float uLum; uniform float uTarget; in vec2 outTexCoord; in vec4 outTint; out vec4 outColor; void main() { vec2 uv = outTexCoord.xy; vec2 luv = outTexCoord.xy; luv.y = 1.0 - luv.y; vec4 global_lum = textureLod(bar, luv, 7.0); vec4 local_lum = textureLod(bar, luv, 3.0); vec4 texel = texture(foo, uv); outColor = mix((local_lum - global_lum) + texel, texel, step(uTarget, uv.x)); outColor.w = 1.0f; } vertex: | #version 300 es precision mediump float; uniform mat4 uProjectionMatrix; uniform mat4 uViewMatrix; uniform mat4 uModelMatrix; in vec2 inPosition; in vec2 inTexCoord; in float inTintEffect; in vec4 inTint; out vec2 outTexCoord; out float outTintEffect; out vec4 outTint; void main () { gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(inPosition, 1.0, 1.0); outTexCoord = inTexCoord; outTint = inTint; outTintEffect = inTintEffect; } - name: clamp samplers: - name: stage source: stage fragment: | precision mediump float; uniform float uMin; uniform float uMax; uniform sampler2D stage; varying vec2 outTexCoord; void main() { float range = uMax - uMin; vec4 base = vec4(uMin); vec4 texel = texture2D(stage, outTexCoord); gl_FragColor = (clamp(texel, base, vec4(uMax)) - base) / range; gl_FragColor.w = 1.0; } - name: madd samplers: - name: stage source: stage - name: scene source: scene fragment: | precision mediump float; uniform sampler2D scene; uniform sampler2D stage; varying vec2 outTexCoord; void main() { vec2 uv = outTexCoord.xy; uv.y = 1.0 - uv.y; vec4 texel = texture2D(stage, outTexCoord) + texture2D(scene, uv); gl_FragColor = texel; gl_FragColor.w = 1.0; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,240 @@ import { doesExist, mustExist, mustGet } from '@apextoaster/js-utils'; import * as Phaser from 'phaser'; import { CENTER_SPLIT } from '../constants'; import { Point } from '../entity'; import { ControlledScene } from '../scene'; export interface EffectSampler { name: string; source: string; } export interface EffectUniform { name: string; /* eslint-disable-next-line */ value: any; } export interface PipelineEffect { name: string; fragment: string; samplers: Array<EffectSampler>; vertex: string; } export interface PipelineLayer { name: string; uniforms: Array<EffectUniform>; } export interface PipelineData { name: string; effects: Array<PipelineEffect>; layers: Array<PipelineLayer>; mipmaps: boolean; screen: Point; } const REQUIRED_BUFFERS = [ 'scene', 'stage', 'write', ]; const MAX_SAMPLERS = 8; export type RenderObject = Phaser.GameObjects.GameObject & Phaser.GameObjects.Components.Transform; class EffectPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline { protected samplers: Array<{ name: string; texture: Phaser.GameObjects.RenderTexture; }>; constructor(game: Phaser.Game, effect: PipelineEffect) { super({ fragShader: effect.fragment, game, renderer: game.renderer, vertShader: effect.vertex, }); this.samplers = []; } public onBind() { super.onBind(); const program = this.program; const renderer = this.renderer; const sl = Math.min(MAX_SAMPLERS, this.samplers.length); for (let i = 0; i < sl; ++i) { const { name, texture } = this.samplers[i]; renderer.setTexture2D(texture.glTexture, i); renderer.setInt1(program, name, i); } return this; } public cacheTextures(samplers: Array<EffectSampler>, buffers: Map<string, Phaser.GameObjects.RenderTexture>) { for (const { name, source } of samplers) { const texture = mustGet(buffers, source); this.samplers.push({ name, texture, }); } } } export class PostProcessor { protected readonly data: PipelineData; protected readonly scene: ControlledScene; protected readonly buffers: Map<string, Phaser.GameObjects.RenderTexture>; protected readonly effects: Map<string, EffectPipeline>; protected running: boolean; protected fillRect?: Phaser.GameObjects.Rectangle; constructor(data: PipelineData, scene: ControlledScene) { this.data = data; this.scene = scene; this.running = false; this.effects = new Map(); this.buffers = new Map(); } public get screenBuffer() { return this.getBuffer('stage'); } public getBuffer(key: string) { return mustGet(this.buffers, key); } public create() { this.fillRect = this.scene.add.rectangle(0, 0, this.data.screen.x, this.data.screen.y, 0x00, 1.0); for (const buffer of REQUIRED_BUFFERS) { const rt = this.createBuffer(buffer); this.buffers.set(buffer, rt); } for (const effect of this.data.effects) { const game = this.scene.game; const pipeline = new EffectPipeline(game, effect); this.effects.set(effect.name, pipeline); const renderer = game.renderer as Phaser.Renderer.WebGL.WebGLRenderer; renderer.addPipeline(effect.name, pipeline); const rt = this.createBuffer(effect.name); rt.setPipeline(effect.name); this.buffers.set(effect.name, rt); } for (const data of this.data.effects) { const effect = mustGet(this.effects, data.name); effect.cacheTextures(data.samplers, this.buffers); } this.running = true; } public createBuffer(name: string) { const rt = this.scene.make.renderTexture({ height: this.data.screen.y, width: this.data.screen.x, x: 0, y: 0, }); rt.setName(name); rt.setScrollFactor(0, 0); rt.setVisible(false); this.scene.textures.addRenderTexture(name, rt); return rt; } public update(objects: Array<RenderObject>, layers: Array<PipelineLayer>, camera: Phaser.Cameras.Scene2D.Camera, gl: WebGL2RenderingContext) { if (!this.running) { return; } const sceneBuffer = mustGet(this.buffers, 'scene'); const stageBuffer = mustGet(this.buffers, 'stage'); const writeBuffer = mustGet(this.buffers, 'write'); const fillRect = mustExist(this.fillRect); sceneBuffer.draw(fillRect, fillRect.displayWidth / CENTER_SPLIT, fillRect.displayHeight / CENTER_SPLIT); for (const obj of objects) { sceneBuffer.draw(obj, Math.floor(-camera.scrollX + obj.x), Math.floor(-camera.scrollY + obj.y)); } if (this.data.mipmaps) { this.updateMips(sceneBuffer, gl); } stageBuffer.draw(sceneBuffer); for (const layer of layers) { const buffer = mustGet(this.buffers, layer.name); const effect = mustGet(this.effects, layer.name); if (doesExist(layer.uniforms)) { for (const uniform of layer.uniforms) { effect.setFloat1(uniform.name, uniform.value); } } buffer.draw(stageBuffer); writeBuffer.draw(buffer); stageBuffer.draw(writeBuffer); if (this.data.mipmaps) { this.updateMips(stageBuffer, gl); } } } public updateMips(buffer: Phaser.GameObjects.RenderTexture, gl: WebGL2RenderingContext) { const active = gl.getParameter(gl.ACTIVE_TEXTURE); const levels = Math.ceil(Math.log2(Math.max(buffer.height, buffer.width))); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, buffer.glTexture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, levels); gl.generateMipmap(gl.TEXTURE_2D); const err = gl.getError(); if (err > 0) { /* eslint-disable-next-line */ console.warn('mipmap error', err); } gl.activeTexture(active); } public stop() { this.running = false; const fillRect = mustExist(this.fillRect); this.scene.children.remove(fillRect); for (const name of this.buffers.keys()) { const renderer = this.scene.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer; renderer.removePipeline(name); this.scene.textures.remove(name); } } }