UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

141 lines (138 loc) 5.67 kB
import { BlueNoise } from '../../core/math/blue-noise.js'; import { Color } from '../../core/math/color.js'; import { SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL, SEMANTIC_POSITION, ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_R8 } 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 { constructor(device, sourceTexture, cameraComponent, blurEnabled){ super(device), this.radius = 5, this.intensity = 1, this.power = 1, this.sampleCount = 10, this.minAngle = 5, this.randomize = false, this._scale = 1, this._blueNoise = new BlueNoise(19); this.sourceTexture = sourceTexture; this.cameraComponent = cameraComponent; ShaderChunks.get(device, SHADERLANGUAGE_GLSL).set('ssaoPS', glslSsaoPS); ShaderChunks.get(device, SHADERLANGUAGE_WGSL).set('ssaoPS', wgslSsaoPS); const defines = 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: new Texture(this.device, { name: name, width: 1, height: 1, format: PIXELFORMAT_R8, mipmaps: false, minFilter: FILTER_NEAREST, magFilter: FILTER_NEAREST, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE }) }); } 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.0 / width, 1.0 / height ]); scope.resolve('uSampleCount').setValue([ sampleCount, 1.0 / sampleCount ]); const minAngleSin = Math.sin(minAngle * Math.PI / 180.0); scope.resolve('uMinHorizonAngleSineSquared').setValue(minAngleSin * minAngleSin); const spiralTurns = 10.0; const step = 1.0 / (sampleCount - 0.5) * spiralTurns * 2.0 * 3.141; const radius = this.radius / scale; const bias = 0.001; const peak = 0.1 * radius; const intensity = 2 * (peak * 2.0 * 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.0); scope.resolve('uInvRadiusSquared').setValue(1.0 / (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.0 / srcTexture.width, 1.0 / srcTexture.height ]); } } export { RenderPassSsao };