UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

132 lines (131 loc) 5.13 kB
import { BlueNoise } from "../../core/math/blue-noise.js"; import { Color } from "../../core/math/color.js"; import { math } from "../../core/math/math.js"; import { PIXELFORMAT_R8, SEMANTIC_POSITION, SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from "../../platform/graphics/constants.js"; import { RenderTarget } from "../../platform/graphics/render-target.js"; import { Texture } from "../../platform/graphics/texture.js"; import { RenderPassShaderQuad } from "../../scene/graphics/render-pass-shader-quad.js"; import { RenderPassDepthAwareBlur } from "./render-pass-depth-aware-blur.js"; import { ShaderChunks } from "../../scene/shader-lib/shader-chunks.js"; import { ShaderUtils } from "../../scene/shader-lib/shader-utils.js"; import glslSsaoPS from "../../scene/shader-lib/glsl/chunks/render-pass/frag/ssao.js"; import wgslSsaoPS from "../../scene/shader-lib/wgsl/chunks/render-pass/frag/ssao.js"; class RenderPassSsao extends RenderPassShaderQuad { radius = 5; intensity = 1; power = 1; sampleCount = 10; minAngle = 5; randomize = false; ssaoTexture; _scale = 1; _blueNoise = new BlueNoise(19); constructor(device, sourceTexture, cameraComponent, blurEnabled) { super(device); this.sourceTexture = sourceTexture; this.cameraComponent = cameraComponent; ShaderChunks.get(device, SHADERLANGUAGE_GLSL).set("ssaoPS", glslSsaoPS); ShaderChunks.get(device, SHADERLANGUAGE_WGSL).set("ssaoPS", wgslSsaoPS); const defines = /* @__PURE__ */ new Map(); ShaderUtils.addScreenDepthChunkDefines(device, cameraComponent.shaderParams, defines); this.shader = ShaderUtils.createShader(device, { uniqueName: "SsaoShader", attributes: { aPosition: SEMANTIC_POSITION }, vertexChunk: "quadVS", fragmentChunk: "ssaoPS", fragmentDefines: defines }); const rt = this.createRenderTarget("SsaoFinalTexture"); this.ssaoTexture = rt.colorBuffer; this.init(rt, { resizeSource: this.sourceTexture }); const clearColor = new Color(0, 0, 0, 0); this.setClearColor(clearColor); if (blurEnabled) { const blurRT = this.createRenderTarget("SsaoTempTexture"); const blurPassHorizontal = new RenderPassDepthAwareBlur(device, rt.colorBuffer, cameraComponent, true); blurPassHorizontal.init(blurRT, { resizeSource: rt.colorBuffer }); blurPassHorizontal.setClearColor(clearColor); const blurPassVertical = new RenderPassDepthAwareBlur(device, blurRT.colorBuffer, cameraComponent, false); blurPassVertical.init(rt, { resizeSource: rt.colorBuffer }); blurPassVertical.setClearColor(clearColor); this.afterPasses.push(blurPassHorizontal); this.afterPasses.push(blurPassVertical); } this.ssaoTextureId = device.scope.resolve("ssaoTexture"); this.ssaoTextureSizeInvId = device.scope.resolve("ssaoTextureSizeInv"); } destroy() { this.renderTarget?.destroyTextureBuffers(); this.renderTarget?.destroy(); this.renderTarget = null; if (this.afterPasses.length > 0) { const blurRt = this.afterPasses[0].renderTarget; blurRt?.destroyTextureBuffers(); blurRt?.destroy(); } this.afterPasses.forEach((pass) => pass.destroy()); this.afterPasses.length = 0; super.destroy(); } set scale(value) { this._scale = value; this.scaleX = value; this.scaleY = value; } get scale() { return this._scale; } createRenderTarget(name) { return new RenderTarget({ depth: false, colorBuffer: Texture.createDataTexture2D(this.device, name, 1, 1, PIXELFORMAT_R8) }); } execute() { const { device, sourceTexture, sampleCount, minAngle, scale } = this; const { width, height } = this.renderTarget.colorBuffer; const scope = device.scope; scope.resolve("uAspect").setValue(width / height); scope.resolve("uInvResolution").setValue([1 / width, 1 / height]); scope.resolve("uSampleCount").setValue([sampleCount, 1 / sampleCount]); const minAngleSin = Math.sin(minAngle * math.DEG_TO_RAD); scope.resolve("uMinHorizonAngleSineSquared").setValue(minAngleSin * minAngleSin); const spiralTurns = 10; const step = 1 / (sampleCount - 0.5) * spiralTurns * 2 * 3.141; const radius = this.radius / scale; const bias = 1e-3; const peak = 0.1 * radius; const intensity = 2 * (peak * 2 * 3.141) * this.intensity / sampleCount; const projectionScale = 0.5 * sourceTexture.height; scope.resolve("uSpiralTurns").setValue(spiralTurns); scope.resolve("uAngleIncCosSin").setValue([Math.cos(step), Math.sin(step)]); scope.resolve("uMaxLevel").setValue(0); scope.resolve("uInvRadiusSquared").setValue(1 / (radius * radius)); scope.resolve("uBias").setValue(bias); scope.resolve("uPeak2").setValue(peak * peak); scope.resolve("uIntensity").setValue(intensity); scope.resolve("uPower").setValue(this.power); scope.resolve("uProjectionScaleRadius").setValue(projectionScale * radius); scope.resolve("uRandomize").setValue(this.randomize ? this._blueNoise.value() : 0); super.execute(); } after() { this.ssaoTextureId.setValue(this.ssaoTexture); const srcTexture = this.sourceTexture; this.ssaoTextureSizeInvId.setValue([1 / srcTexture.width, 1 / srcTexture.height]); } } export { RenderPassSsao };