Skip to content

Instantly share code, notes, and snippets.

@ssube
Last active June 25, 2020 05:06
Show Gist options
  • Select an option

  • Save ssube/96e9db52bbb6e310fc4096872ed027bb to your computer and use it in GitHub Desktop.

Select an option

Save ssube/96e9db52bbb6e310fc4096872ed027bb to your computer and use it in GitHub Desktop.
phaser-post
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;
}
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