Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smaller PostEffect refactor and simplification, WebGPU support #4996

Merged
merged 1 commit into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
86 changes: 36 additions & 50 deletions scripts/posteffects/posteffect-outline.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,55 +13,41 @@
function OutlineEffect(graphicsDevice, thickness) {
pc.PostEffect.call(this, graphicsDevice);

this.shader = new pc.Shader(graphicsDevice, {
attributes: {
aPosition: pc.SEMANTIC_POSITION
},
vshader: [
"attribute vec2 aPosition;",
"",
"varying vec2 vUv0;",
"",
"void main(void)",
"{",
" gl_Position = vec4(aPosition, 0.0, 1.0);",
" vUv0 = (aPosition.xy + 1.0) * 0.5;",
"}"
].join("\n"),
fshader: [
"precision " + graphicsDevice.precision + " float;",
"",
"#define THICKNESS " + (thickness ? thickness.toFixed(0) : 1),
"uniform float uWidth;",
"uniform float uHeight;",
"uniform vec4 uOutlineCol;",
"uniform sampler2D uColorBuffer;",
"uniform sampler2D uOutlineTex;",
"",
"varying vec2 vUv0;",
"",
"void main(void)",
"{",
" vec4 texel1 = texture2D(uColorBuffer, vUv0);",
" float sample0 = texture2D(uOutlineTex, vUv0).a;",
" float outline = 0.0;",
" if (sample0==0.0)",
" {",
" for (int x=-THICKNESS;x<=THICKNESS;x++)",
" {",
" for (int y=-THICKNESS;y<=THICKNESS;y++)",
" { ",
" float sample=texture2D(uOutlineTex, vUv0+vec2(float(x)/uWidth, float(y)/uHeight)).a;",
" if (sample>0.0)",
" {",
" outline=1.0;",
" }",
" }",
" } ",
" }",
" gl_FragColor = mix(texel1, uOutlineCol, outline * uOutlineCol.a);",
"}"
].join("\n")
var fshader = [
"#define THICKNESS " + (thickness ? thickness.toFixed(0) : 1),
"uniform float uWidth;",
"uniform float uHeight;",
"uniform vec4 uOutlineCol;",
"uniform sampler2D uColorBuffer;",
"uniform sampler2D uOutlineTex;",
"",
"varying vec2 vUv0;",
"",
"void main(void)",
"{",
" vec4 texel1 = texture2D(uColorBuffer, vUv0);",
" float sample0 = texture2D(uOutlineTex, vUv0).a;",
" float outline = 0.0;",
" if (sample0==0.0)",
" {",
" for (int x=-THICKNESS;x<=THICKNESS;x++)",
" {",
" for (int y=-THICKNESS;y<=THICKNESS;y++)",
" { ",
" float tex=texture2D(uOutlineTex, vUv0+vec2(float(x)/uWidth, float(y)/uHeight)).a;",
" if (tex>0.0)",
" {",
" outline=1.0;",
" }",
" }",
" } ",
" }",
" gl_FragColor = mix(texel1, uOutlineCol, outline * uOutlineCol.a);",
"}"
].join("\n");

this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'OutlineShader', {
aPosition: pc.SEMANTIC_POSITION
});

// Uniforms
Expand Down Expand Up @@ -89,7 +75,7 @@ Object.assign(OutlineEffect.prototype, {
scope.resolve("uOutlineCol").setValue(this._colorData);
scope.resolve("uColorBuffer").setValue(inputTarget.colorBuffer);
scope.resolve("uOutlineTex").setValue(this.texture);
pc.drawFullscreenQuad(device, outputTarget, this.vertexBuffer, this.shader, rect);
this.drawQuad(outputTarget, this.shader, rect);
}
});

Expand Down
19 changes: 18 additions & 1 deletion src/deprecated/deprecated.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { drawQuadWithShader } from '../scene/graphics/quad-render-utils.js';
import { shaderChunks } from '../scene/shader-lib/chunks/chunks.js';
import { GraphicsDevice } from '../platform/graphics/graphics-device.js';
import { IndexBuffer } from '../platform/graphics/index-buffer.js';
import { drawFullscreenQuad, PostEffect } from '../scene/graphics/post-effect.js';
import { PostEffect } from '../scene/graphics/post-effect.js';
import { PostEffectQueue } from '../framework/components/camera/post-effect-queue.js';
import { ProgramLibrary } from '../scene/shader-lib/program-library.js';
import { getProgramLibrary, setProgramLibrary } from '../scene/shader-lib/get-program-library.js';
Expand Down Expand Up @@ -436,6 +436,23 @@ export const gfx = {
VertexIterator: VertexIterator
};

const _viewport = new Vec4();

export function drawFullscreenQuad(device, target, vertexBuffer, shader, rect) {

Debug.deprecated(`pc.drawFullscreenQuad is deprecated. When used as part of PostEffect, use PostEffect#drawQuad instead.`);

// convert rect in normalized space to viewport in pixel space
let viewport;
if (rect) {
const w = target ? target.width : device.width;
const h = target ? target.height : device.height;
viewport = _viewport.set(rect.x * w, rect.y * h, rect.z * w, rect.w * h);
}

drawQuadWithShader(device, target, shader, viewport);
}

export const posteffect = {
createFullscreenQuad: (device) => {
return device.quadVertexBuffer;
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export { Skeleton } from './scene/animation/skeleton.js';

// SCENE / GRAPHICS
export { EnvLighting } from './scene/graphics/env-lighting.js';
export { PostEffect, drawFullscreenQuad } from './scene/graphics/post-effect.js';
export { PostEffect } from './scene/graphics/post-effect.js';
export { shFromCubemap } from './scene/graphics/prefilter-cubemap.js';
export { reprojectTexture } from './scene/graphics/reproject-texture.js';

Expand Down
138 changes: 38 additions & 100 deletions src/scene/graphics/post-effect.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { CULLFACE_NONE, PRIMITIVE_TRISTRIP } from '../../platform/graphics/constants.js';
import { Vec4 } from '../../core/math/vec4.js';
import { drawQuadWithShader } from './quad-render-utils.js';

// Primitive for drawFullscreenQuad
const primitive = {
type: PRIMITIVE_TRISTRIP,
base: 0,
count: 4,
indexed: false
};
const _viewport = new Vec4();

/**
* Base class for all post effects. Post effects take a a render target as input apply effects to
Expand All @@ -28,21 +23,6 @@ class PostEffect {
*/
this.device = graphicsDevice;

/**
* The shader definition for the fullscreen quad. Needs to be set by the custom post effect
* (default is null). Used when calling {@link drawFullscreenQuad}.
*
* @type {import('../../platform/graphics/shader.js').Shader|null}
*/
this.shader = null;

/**
* The vertex buffer for the fullscreen quad. Used when calling {@link drawFullscreenQuad}.
*
* @type {import('../../platform/graphics/vertex-buffer.js').VertexBuffer}
*/
this.vertexBuffer = graphicsDevice.quadVertexBuffer;

/**
* The property that should to be set to `true` (by the custom post effect) if a depth map
* is necessary (default is false).
Expand All @@ -52,6 +32,20 @@ class PostEffect {
this.needsDepthBuffer = false;
}

/**
* A simple vertx shader used to render a quad, which requires 'vec2 aPosition' in the vertex
* buffer, and generates uv coordinates vUv0 for use in the fragment shader.
*/
static quadVertexShader = `
attribute vec2 aPosition;
varying vec2 vUv0;
void main(void)
{
gl_Position = vec4(aPosition, 0.0, 1.0);
vUv0 = getImageEffectUV((aPosition.xy + 1.0) * 0.5);
}
`;

/**
* Render the post effect using the specified inputTarget to the specified outputTarget.
*
Expand All @@ -64,84 +58,28 @@ class PostEffect {
*/
render(inputTarget, outputTarget, rect) {
}
}

/**
* Draw a screen-space rectangle in a render target. Primarily meant to be used in custom post
* effects based on {@link PostEffect}.
*
* @param {import('../../platform/graphics/graphics-device.js').GraphicsDevice} device - The
* graphics device of the application.
* @param {import('../../platform/graphics/render-target.js').RenderTarget} target - The output
* render target.
* @param {import('../../platform/graphics/vertex-buffer.js').VertexBuffer} vertexBuffer - The vertex buffer for the rectangle mesh. When calling from
* a custom post effect, pass the field {@link PostEffect#vertexBuffer}.
* @param {import('../../platform/graphics/shader.js').Shader} shader - The shader to be used for
* drawing the rectangle. When calling from a custom post effect, pass the field
* {@link PostEffect#shader}.
* @param {import('../../core/math/vec4.js').Vec4} [rect] - The normalized screen-space position
* (rect.x, rect.y) and size (rect.z, rect.w) of the rectangle. Default is [0, 0, 1, 1].
*/
function drawFullscreenQuad(device, target, vertexBuffer, shader, rect) {
const oldRt = device.getRenderTarget();
device.setRenderTarget(target);
device.updateBegin();

let w = target ? target.width : device.width;
let h = target ? target.height : device.height;
let x = 0;
let y = 0;

if (rect) {
x = rect.x * w;
y = rect.y * h;
w *= rect.z;
h *= rect.w;
/**
* Draw a screen-space rectangle in a render target, using a specified shader.
*
* @param {import('../../platform/graphics/render-target.js').RenderTarget} target - The output
* render target.
* @param {import('../../platform/graphics/shader.js').Shader} shader - The shader to be used for
* drawing the rectangle.
* @param {import('../../core/math/vec4.js').Vec4} [rect] - The normalized screen-space position
* (rect.x, rect.y) and size (rect.z, rect.w) of the rectangle. Default is [0, 0, 1, 1].
*/
drawQuad(target, shader, rect) {
let viewport;
if (rect) {
// convert rect in normalized space to viewport in pixel space
const w = target ? target.width : this.device.width;
const h = target ? target.height : this.device.height;
viewport = _viewport.set(rect.x * w, rect.y * h, rect.z * w, rect.w * h);
}

drawQuadWithShader(this.device, target, shader, viewport);
}

const oldVx = device.vx;
const oldVy = device.vy;
const oldVw = device.vw;
const oldVh = device.vh;
device.setViewport(x, y, w, h);
const oldSx = device.sx;
const oldSy = device.sy;
const oldSw = device.sw;
const oldSh = device.sh;
device.setScissor(x, y, w, h);

const oldBlending = device.getBlending();
const oldDepthTest = device.getDepthTest();
const oldDepthWrite = device.getDepthWrite();
const oldCullMode = device.getCullMode();
const oldWR = device.writeRed;
const oldWG = device.writeGreen;
const oldWB = device.writeBlue;
const oldWA = device.writeAlpha;
device.setBlending(false);
device.setDepthTest(false);
device.setDepthWrite(false);
device.setCullMode(CULLFACE_NONE);
device.setColorWrite(true, true, true, true);

device.setVertexBuffer(vertexBuffer, 0);
device.setShader(shader);

device.draw(primitive);

device.setBlending(oldBlending);
device.setDepthTest(oldDepthTest);
device.setDepthWrite(oldDepthWrite);
device.setCullMode(oldCullMode);
device.setColorWrite(oldWR, oldWG, oldWB, oldWA);

device.updateEnd();

device.setRenderTarget(oldRt);
device.updateBegin();

device.setViewport(oldVx, oldVy, oldVw, oldVh);
device.setScissor(oldSx, oldSy, oldSw, oldSh);
}

export { drawFullscreenQuad, PostEffect };
export { PostEffect };