UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

208 lines (207 loc) 8.11 kB
import { EffectWrapper } from "../Materials/effectRenderer.js"; import { Vector2, Vector3 } from "../Maths/math.vector.js"; import { Camera } from "../Cameras/camera.js"; import { RawTexture } from "../Materials/Textures/rawTexture.js"; import { RandomRange } from "../Maths/math.scalar.functions.js"; import { Texture } from "../Materials/Textures/texture.js"; /** * @internal */ export class ThinSSAO2PostProcess extends EffectWrapper { _gatherImports(useWebGPU, list) { if (useWebGPU) { this._webGPUReady = true; list.push(import("../ShadersWGSL/ssao2.fragment.js")); } else { list.push(import("../Shaders/ssao2.fragment.js")); } } get textureWidth() { return this._textureWidth; } set textureWidth(width) { if (this._textureWidth === width) { return; } this._textureWidth = width; } get textureHeight() { return this._textureHeight; } set textureHeight(height) { if (this._textureHeight === height) { return; } this._textureHeight = height; } set samples(n) { this._samples = n; this.updateEffect(this._getDefinesForSSAO()); this._sampleSphere = this._generateHemisphere(); } get samples() { return this._samples; } set epsilon(n) { this._epsilon = n; this.updateEffect(this._getDefinesForSSAO()); } get epsilon() { return this._epsilon; } constructor(name, scene, options) { super({ ...options, name, engine: scene.getEngine(), useShaderStore: true, useAsPostProcess: true, fragmentShader: ThinSSAO2PostProcess.FragmentUrl, uniforms: ThinSSAO2PostProcess.Uniforms, samplers: ThinSSAO2PostProcess.Samplers, defines: `#define SSAO\n#define SAMPLES 8\n#define EPSILON 0.0001`, shaderLanguage: scene.getEngine().isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, }); this.camera = null; this._textureWidth = 0; this._textureHeight = 0; this._samples = 8; this.totalStrength = 1.0; this.radius = 2.0; this.maxZ = 100.0; this.minZAspect = 0.2; this.base = 0; this._epsilon = 0.02; this._bits = new Uint32Array(1); this._scene = scene; this._createRandomTexture(); this.updateEffect(this._getDefinesForSSAO()); this._sampleSphere = this._generateHemisphere(); } bind(noDefaultBindings = false) { super.bind(noDefaultBindings); const effect = this._drawWrapper.effect; const camera = this.camera; if (!camera) { return; } const projectionMatrix = camera.getProjectionMatrix(); effect.setArray3("sampleSphere", this._sampleSphere); effect.setFloat("randTextureTiles", 32.0); effect.setFloat("samplesFactor", 1 / this.samples); effect.setFloat("totalStrength", this.totalStrength); effect.setFloat2("texelSize", 1 / this.textureWidth, 1 / this.textureHeight); effect.setFloat("radius", this.radius); effect.setFloat("maxZ", this.maxZ); effect.setFloat("minZAspect", this.minZAspect); effect.setFloat("base", this.base); effect.setFloat("near", camera.minZ); if (camera.mode === Camera.PERSPECTIVE_CAMERA) { effect.setMatrix3x3("depthProjection", ThinSSAO2PostProcess.PERSPECTIVE_DEPTH_PROJECTION); effect.setFloat("xViewport", Math.tan(camera.fov / 2) * this._scene.getEngine().getAspectRatio(camera, true)); effect.setFloat("yViewport", Math.tan(camera.fov / 2)); } else { const halfWidth = this._scene.getEngine().getRenderWidth() / 2.0; const halfHeight = this._scene.getEngine().getRenderHeight() / 2.0; const orthoLeft = camera.orthoLeft ?? -halfWidth; const orthoRight = camera.orthoRight ?? halfWidth; const orthoBottom = camera.orthoBottom ?? -halfHeight; const orthoTop = camera.orthoTop ?? halfHeight; effect.setMatrix3x3("depthProjection", ThinSSAO2PostProcess.ORTHO_DEPTH_PROJECTION); effect.setFloat4("viewport", orthoLeft, orthoRight, orthoBottom, orthoTop); } effect.setMatrix("projection", projectionMatrix); effect.setTexture("randomSampler", this._randomTexture); } dispose() { this._randomTexture.dispose(); super.dispose(); } _createRandomTexture() { const size = 128; const data = new Uint8Array(size * size * 4); const randVector = Vector2.Zero(); for (let index = 0; index < data.length;) { randVector.set(RandomRange(0, 1), RandomRange(0, 1)).normalize().scaleInPlace(255); data[index++] = Math.floor(randVector.x); data[index++] = Math.floor(randVector.y); data[index++] = 0; data[index++] = 255; } const texture = RawTexture.CreateRGBATexture(data, size, size, this._scene, false, false, 2); texture.name = "SSAORandomTexture"; texture.wrapU = Texture.WRAP_ADDRESSMODE; texture.wrapV = Texture.WRAP_ADDRESSMODE; this._randomTexture = texture; } //Van der Corput radical inverse _radicalInverseVdC(i) { this._bits[0] = i; this._bits[0] = ((this._bits[0] << 16) | (this._bits[0] >> 16)) >>> 0; this._bits[0] = ((this._bits[0] & 0x55555555) << 1) | (((this._bits[0] & 0xaaaaaaaa) >>> 1) >>> 0); this._bits[0] = ((this._bits[0] & 0x33333333) << 2) | (((this._bits[0] & 0xcccccccc) >>> 2) >>> 0); this._bits[0] = ((this._bits[0] & 0x0f0f0f0f) << 4) | (((this._bits[0] & 0xf0f0f0f0) >>> 4) >>> 0); this._bits[0] = ((this._bits[0] & 0x00ff00ff) << 8) | (((this._bits[0] & 0xff00ff00) >>> 8) >>> 0); return this._bits[0] * 2.3283064365386963e-10; // / 0x100000000 or / 4294967296 } _hammersley(i, n) { return [i / n, this._radicalInverseVdC(i)]; } _hemisphereSampleUniform(u, v) { const phi = v * 2.0 * Math.PI; // rejecting samples that are close to tangent plane to avoid z-fighting artifacts const cosTheta = 1.0 - u * 0.85; const sinTheta = Math.sqrt(1.0 - cosTheta * cosTheta); return new Vector3(Math.cos(phi) * sinTheta, Math.sin(phi) * sinTheta, cosTheta); } _generateHemisphere() { const numSamples = this.samples; const result = []; let vector; let i = 0; while (i < numSamples) { if (numSamples < 16) { vector = this._hemisphereSampleUniform(Math.random(), Math.random()); } else { const rand = this._hammersley(i, numSamples); vector = this._hemisphereSampleUniform(rand[0], rand[1]); } result.push(vector.x, vector.y, vector.z); i++; } return result; } _getDefinesForSSAO() { let defines = `#define SSAO\n#define SAMPLES ${this.samples}\n#define EPSILON ${this.epsilon.toFixed(4)}`; if (this.camera?.mode === Camera.ORTHOGRAPHIC_CAMERA) { defines += `\n#define ORTHOGRAPHIC_CAMERA`; } return defines; } } ThinSSAO2PostProcess.ORTHO_DEPTH_PROJECTION = [1, 0, 0, 0, 1, 0, 0, 0, 1]; ThinSSAO2PostProcess.PERSPECTIVE_DEPTH_PROJECTION = [0, 0, 0, 0, 0, 0, 1, 1, 1]; ThinSSAO2PostProcess.FragmentUrl = "ssao2"; ThinSSAO2PostProcess.Uniforms = [ "sampleSphere", "samplesFactor", "randTextureTiles", "totalStrength", "radius", "base", "range", "projection", "near", "texelSize", "xViewport", "yViewport", "viewport", "maxZ", "minZAspect", "depthProjection", ]; ThinSSAO2PostProcess.Samplers = ["randomSampler", "depthSampler", "normalSampler"]; //# sourceMappingURL=thinSSAO2PostProcess.js.map