Last active
June 25, 2020 05:06
-
-
Save ssube/96e9db52bbb6e310fc4096872ed027bb to your computer and use it in GitHub Desktop.
phaser-post
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 characters
| 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 characters
| 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); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment