UNPKG

molstar

Version:

A comprehensive macromolecular library.

524 lines (523 loc) 32.8 kB
"use strict"; /** * Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz> * @author Ludovic Autin <ludovic.autin@gmail.com> * @author Gianluca Tomasello <giagitom@gmail.com> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SsaoPass = exports.SsaoParams = void 0; const util_1 = require("../../mol-gl/compute/util"); const schema_1 = require("../../mol-gl/renderable/schema"); const shader_code_1 = require("../../mol-gl/shader-code"); const mol_util_1 = require("../../mol-util"); const render_item_1 = require("../../mol-gl/webgl/render-item"); const renderable_1 = require("../../mol-gl/renderable"); const linear_algebra_1 = require("../../mol-math/linear-algebra"); const param_definition_1 = require("../../mol-util/param-definition"); const quad_vert_1 = require("../../mol-gl/shader/quad.vert"); const ssao_frag_1 = require("../../mol-gl/shader/ssao.frag"); const ssao_blur_frag_1 = require("../../mol-gl/shader/ssao-blur.frag"); const color_1 = require("../../mol-util/color"); const debug_1 = require("../../mol-util/debug"); exports.SsaoParams = { samples: param_definition_1.ParamDefinition.Numeric(32, { min: 1, max: 256, step: 1 }), multiScale: param_definition_1.ParamDefinition.MappedStatic('off', { on: param_definition_1.ParamDefinition.Group({ levels: param_definition_1.ParamDefinition.ObjectList({ radius: param_definition_1.ParamDefinition.Numeric(5, { min: 0, max: 20, step: 0.1 }, { description: 'Final occlusion radius is 2^x' }), bias: param_definition_1.ParamDefinition.Numeric(1, { min: 0, max: 3, step: 0.1 }), }, o => `${o.radius}, ${o.bias}`, { defaultValue: [ { radius: 2, bias: 1 }, { radius: 5, bias: 1 }, { radius: 8, bias: 1 }, { radius: 11, bias: 1 }, ] }), nearThreshold: param_definition_1.ParamDefinition.Numeric(10, { min: 0, max: 50, step: 1 }), farThreshold: param_definition_1.ParamDefinition.Numeric(1500, { min: 0, max: 10000, step: 100 }), }), off: param_definition_1.ParamDefinition.Group({}) }, { cycle: true }), radius: param_definition_1.ParamDefinition.Numeric(5, { min: 0, max: 20, step: 0.1 }, { description: 'Final occlusion radius is 2^x', hideIf: p => (p === null || p === void 0 ? void 0 : p.multiScale.name) === 'on' }), bias: param_definition_1.ParamDefinition.Numeric(0.8, { min: 0, max: 3, step: 0.1 }), blurKernelSize: param_definition_1.ParamDefinition.Numeric(15, { min: 1, max: 25, step: 2 }), blurDepthBias: param_definition_1.ParamDefinition.Numeric(0.5, { min: 0, max: 1, step: 0.01 }), resolutionScale: param_definition_1.ParamDefinition.Numeric(1, { min: 0.1, max: 1, step: 0.05 }, { description: 'Adjust resolution of occlusion calculation' }), color: param_definition_1.ParamDefinition.Color((0, color_1.Color)(0x000000)), transparentThreshold: param_definition_1.ParamDefinition.Numeric(0.4, { min: 0, max: 1, step: 0.05 }), }; function getLevels(props, levels) { const count = props.length; const { radius, bias } = levels || { radius: (new Array(count * 3)).fill(0), bias: (new Array(count * 3)).fill(0), }; props = props.slice().sort((a, b) => a.radius - b.radius); for (let i = 0; i < count; ++i) { const p = props[i]; radius[i] = Math.pow(2, p.radius); bias[i] = p.bias; } return { count, radius, bias }; } class SsaoPass { static isEnabled(props) { return props.occlusion.name !== 'off'; } static isTransparentEnabled(scene, props) { return scene.opacityAverage < 1 && scene.transparencyMin < props.transparentThreshold; } calcSsaoScale(resolutionScale) { // downscale ssao for high pixel-ratios return Math.min(1, 1 / this.webgl.pixelRatio) * resolutionScale; } getDepthTexture() { return this.ssaoScale === 1 ? this.depthTextureOpaque : this.downsampledDepthTargetOpaque.texture; } getTransparentDepthTexture() { return this.ssaoScale === 1 ? this.depthTextureTransparent : this.downsampledDepthTargetTransparent.texture; } constructor(webgl, width, height, packedDepth, depthTextureOpaque, depthTextureTransparent) { this.webgl = webgl; const { textureFloatLinear } = webgl.extensions; this.depthTextureOpaque = depthTextureOpaque; this.depthTextureTransparent = depthTextureTransparent; this.nSamples = 1; this.blurKernelSize = 1; this.ssaoScale = this.calcSsaoScale(1); this.texSize = [width, height]; this.levels = []; this.framebuffer = webgl.resources.framebuffer(); this.blurFirstPassFramebuffer = webgl.resources.framebuffer(); this.blurSecondPassFramebuffer = webgl.resources.framebuffer(); const sw = Math.floor(width * this.ssaoScale); const sh = Math.floor(height * this.ssaoScale); const hw = Math.max(1, Math.floor(sw * 0.5)); const hh = Math.max(1, Math.floor(sh * 0.5)); const qw = Math.max(1, Math.floor(sw * 0.25)); const qh = Math.max(1, Math.floor(sh * 0.25)); const filter = textureFloatLinear ? 'linear' : 'nearest'; this.downsampledDepthTargetOpaque = packedDepth ? webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear', 'rgba') : webgl.createRenderTarget(sw, sh, false, 'float32', filter, webgl.isWebGL2 ? 'alpha' : 'rgba'); this.downsampleDepthRenderableOpaque = (0, util_1.createCopyRenderable)(webgl, depthTextureOpaque); const depthTexture = this.getDepthTexture(); this.depthHalfTargetOpaque = packedDepth ? webgl.createRenderTarget(hw, hh, false, 'uint8', 'linear', 'rgba') : webgl.createRenderTarget(hw, hh, false, 'float32', filter, webgl.isWebGL2 ? 'alpha' : 'rgba'); this.depthHalfRenderableOpaque = (0, util_1.createCopyRenderable)(webgl, depthTexture); this.depthQuarterTargetOpaque = packedDepth ? webgl.createRenderTarget(qw, qh, false, 'uint8', 'linear', 'rgba') : webgl.createRenderTarget(qw, qh, false, 'float32', filter, webgl.isWebGL2 ? 'alpha' : 'rgba'); this.depthQuarterRenderableOpaque = (0, util_1.createCopyRenderable)(webgl, this.depthHalfTargetOpaque.texture); this.downsampledDepthTargetTransparent = webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear', 'rgba'); this.downsampleDepthRenderableTransparent = (0, util_1.createCopyRenderable)(webgl, depthTextureTransparent); const transparentDepthTexture = this.getTransparentDepthTexture(); this.depthHalfTargetTransparent = webgl.createRenderTarget(hw, hh, false, 'uint8', 'linear', 'rgba'); this.depthHalfRenderableTransparent = (0, util_1.createCopyRenderable)(webgl, transparentDepthTexture); this.depthQuarterTargetTransparent = webgl.createRenderTarget(qw, qh, false, 'uint8', 'linear', 'rgba'); this.depthQuarterRenderableTransparent = (0, util_1.createCopyRenderable)(webgl, this.depthHalfTargetTransparent.texture); this.ssaoDepthTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear'); this.ssaoDepthTexture.define(sw, sh); this.ssaoDepthTexture.attachFramebuffer(this.framebuffer, 'color0'); this.ssaoDepthTransparentTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear'); this.ssaoDepthTransparentTexture.define(sw, sh); this.depthBlurProxyTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear'); this.depthBlurProxyTexture.define(sw, sh); this.depthBlurProxyTexture.attachFramebuffer(this.blurFirstPassFramebuffer, 'color0'); this.renderable = getSsaoRenderable(webgl, depthTexture, this.depthHalfTargetOpaque.texture, this.depthQuarterTargetOpaque.texture, transparentDepthTexture, this.depthHalfTargetTransparent.texture, this.depthQuarterTargetTransparent.texture); this.blurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTransparentTexture, 'horizontal'); this.blurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.depthBlurProxyTexture, 'vertical'); } setSize(width, height) { const [w, h] = this.texSize; const ssaoScale = this.calcSsaoScale(1); if (width !== w || height !== h || this.ssaoScale !== ssaoScale) { this.texSize.splice(0, 2, width, height); const sw = Math.floor(width * this.ssaoScale); const sh = Math.floor(height * this.ssaoScale); this.ssaoDepthTexture.define(sw, sh); this.ssaoDepthTransparentTexture.define(sw, sh); this.depthBlurProxyTexture.define(sw, sh); const hw = Math.max(1, Math.floor(sw * 0.5)); const hh = Math.max(1, Math.floor(sh * 0.5)); const qw = Math.max(1, Math.floor(sw * 0.25)); const qh = Math.max(1, Math.floor(sh * 0.25)); this.downsampledDepthTargetOpaque.setSize(sw, sh); this.depthHalfTargetOpaque.setSize(hw, hh); this.depthQuarterTargetOpaque.setSize(qw, qh); mol_util_1.ValueCell.update(this.downsampleDepthRenderableOpaque.values.uTexSize, linear_algebra_1.Vec2.set(this.downsampleDepthRenderableOpaque.values.uTexSize.ref.value, sw, sh)); mol_util_1.ValueCell.update(this.depthHalfRenderableOpaque.values.uTexSize, linear_algebra_1.Vec2.set(this.depthHalfRenderableOpaque.values.uTexSize.ref.value, hw, hh)); mol_util_1.ValueCell.update(this.depthQuarterRenderableOpaque.values.uTexSize, linear_algebra_1.Vec2.set(this.depthQuarterRenderableOpaque.values.uTexSize.ref.value, qw, qh)); this.downsampledDepthTargetTransparent.setSize(sw, sh); this.depthHalfTargetTransparent.setSize(hw, hh); this.depthQuarterTargetTransparent.setSize(qw, qh); mol_util_1.ValueCell.update(this.downsampleDepthRenderableTransparent.values.uTexSize, linear_algebra_1.Vec2.set(this.downsampleDepthRenderableTransparent.values.uTexSize.ref.value, sw, sh)); mol_util_1.ValueCell.update(this.depthHalfRenderableTransparent.values.uTexSize, linear_algebra_1.Vec2.set(this.depthHalfRenderableTransparent.values.uTexSize.ref.value, hw, hh)); mol_util_1.ValueCell.update(this.depthQuarterRenderableTransparent.values.uTexSize, linear_algebra_1.Vec2.set(this.depthQuarterRenderableTransparent.values.uTexSize.ref.value, qw, qh)); mol_util_1.ValueCell.update(this.renderable.values.uTexSize, linear_algebra_1.Vec2.set(this.renderable.values.uTexSize.ref.value, sw, sh)); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.uTexSize, linear_algebra_1.Vec2.set(this.blurFirstPassRenderable.values.uTexSize.ref.value, sw, sh)); mol_util_1.ValueCell.update(this.blurSecondPassRenderable.values.uTexSize, linear_algebra_1.Vec2.set(this.blurSecondPassRenderable.values.uTexSize.ref.value, sw, sh)); const depthTexture = this.getDepthTexture(); const transparentDepthTexture = this.getTransparentDepthTexture(); mol_util_1.ValueCell.update(this.depthHalfRenderableOpaque.values.tColor, depthTexture); mol_util_1.ValueCell.update(this.depthHalfRenderableTransparent.values.tColor, transparentDepthTexture); mol_util_1.ValueCell.update(this.renderable.values.tDepth, depthTexture); mol_util_1.ValueCell.update(this.renderable.values.tDepthTransparent, transparentDepthTexture); this.depthHalfRenderableOpaque.update(); this.depthHalfRenderableTransparent.update(); this.renderable.update(); } } reset() { this.ssaoDepthTexture.attachFramebuffer(this.framebuffer, 'color0'); this.depthBlurProxyTexture.attachFramebuffer(this.blurFirstPassFramebuffer, 'color0'); } update(camera, scene, props, illuminationMode = false) { let needsUpdateSsao = false; let needsUpdateSsaoBlur = false; let needsUpdateDepthHalf = false; const orthographic = camera.state.mode === 'orthographic' ? 1 : 0; const invProjection = linear_algebra_1.Mat4.identity(); linear_algebra_1.Mat4.invert(invProjection, camera.projection); const [w, h] = this.texSize; const v = camera.viewport; mol_util_1.ValueCell.update(this.renderable.values.uProjection, camera.projection); mol_util_1.ValueCell.update(this.renderable.values.uInvProjection, invProjection); const b = this.renderable.values.uBounds; const s = this.ssaoScale; linear_algebra_1.Vec4.set(b.ref.value, Math.floor(v.x * s) / (w * s), Math.floor(v.y * s) / (h * s), Math.ceil((v.x + v.width) * s) / (w * s), Math.ceil((v.y + v.height) * s) / (h * s)); mol_util_1.ValueCell.update(b, b.ref.value); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.uBounds, b.ref.value); mol_util_1.ValueCell.update(this.blurSecondPassRenderable.values.uBounds, b.ref.value); mol_util_1.ValueCell.updateIfChanged(this.blurFirstPassRenderable.values.uNear, camera.near); mol_util_1.ValueCell.updateIfChanged(this.blurSecondPassRenderable.values.uNear, camera.near); mol_util_1.ValueCell.updateIfChanged(this.blurFirstPassRenderable.values.uFar, camera.far); mol_util_1.ValueCell.updateIfChanged(this.blurSecondPassRenderable.values.uFar, camera.far); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.uInvProjection, invProjection); mol_util_1.ValueCell.update(this.blurSecondPassRenderable.values.uInvProjection, invProjection); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.uBlurDepthBias, props.blurDepthBias); mol_util_1.ValueCell.update(this.blurSecondPassRenderable.values.uBlurDepthBias, props.blurDepthBias); if (this.blurFirstPassRenderable.values.dOrthographic.ref.value !== orthographic) { needsUpdateSsaoBlur = true; mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.dOrthographic, orthographic); mol_util_1.ValueCell.update(this.blurSecondPassRenderable.values.dOrthographic, orthographic); } const includeTransparent = SsaoPass.isTransparentEnabled(scene, props); if (this.renderable.values.dIncludeTransparent.ref.value !== includeTransparent) { needsUpdateSsao = true; mol_util_1.ValueCell.update(this.renderable.values.dIncludeTransparent, includeTransparent); } if (this.renderable.values.dIllumination.ref.value !== illuminationMode) { needsUpdateSsao = true; mol_util_1.ValueCell.update(this.renderable.values.dIllumination, illuminationMode); } if (this.nSamples !== props.samples) { needsUpdateSsao = true; this.nSamples = props.samples; mol_util_1.ValueCell.update(this.renderable.values.uSamples, getSamples(this.nSamples)); mol_util_1.ValueCell.updateIfChanged(this.renderable.values.dNSamples, this.nSamples); } const multiScale = props.multiScale.name === 'on'; if (this.renderable.values.dMultiScale.ref.value !== multiScale) { needsUpdateSsao = true; mol_util_1.ValueCell.update(this.renderable.values.dMultiScale, multiScale); } if (props.multiScale.name === 'on') { const mp = props.multiScale.params; if (!(0, mol_util_1.deepEqual)(this.levels, mp.levels)) { needsUpdateSsao = true; this.levels = mp.levels; const levels = getLevels(mp.levels); mol_util_1.ValueCell.updateIfChanged(this.renderable.values.dLevels, levels.count); mol_util_1.ValueCell.update(this.renderable.values.uLevelRadius, levels.radius); mol_util_1.ValueCell.update(this.renderable.values.uLevelBias, levels.bias); } mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uNearThreshold, mp.nearThreshold); mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uFarThreshold, mp.farThreshold); } else { mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uRadius, Math.pow(2, props.radius)); } mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uBias, props.bias); if (this.blurKernelSize !== props.blurKernelSize) { needsUpdateSsaoBlur = true; this.blurKernelSize = props.blurKernelSize; const kernel = getBlurKernel(this.blurKernelSize); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.uKernel, kernel); mol_util_1.ValueCell.update(this.blurSecondPassRenderable.values.uKernel, kernel); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize); mol_util_1.ValueCell.update(this.blurSecondPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize); } const ssaoScale = this.calcSsaoScale(props.resolutionScale); if (this.ssaoScale !== ssaoScale) { needsUpdateSsao = true; needsUpdateDepthHalf = true; this.ssaoScale = ssaoScale; const sw = Math.floor(w * this.ssaoScale); const sh = Math.floor(h * this.ssaoScale); this.ssaoDepthTexture.define(sw, sh); this.ssaoDepthTransparentTexture.define(sw, sh); this.depthBlurProxyTexture.define(sw, sh); const hw = Math.floor(sw * 0.5); const hh = Math.floor(sh * 0.5); const qw = Math.floor(sw * 0.25); const qh = Math.floor(sh * 0.25); this.downsampledDepthTargetOpaque.setSize(sw, sh); this.depthHalfTargetOpaque.setSize(hw, hh); this.depthQuarterTargetOpaque.setSize(qw, qh); const depthTexture = this.getDepthTexture(); mol_util_1.ValueCell.update(this.depthHalfRenderableOpaque.values.tColor, depthTexture); mol_util_1.ValueCell.update(this.renderable.values.tDepth, depthTexture); mol_util_1.ValueCell.update(this.renderable.values.tDepthHalf, this.depthHalfTargetOpaque.texture); mol_util_1.ValueCell.update(this.renderable.values.tDepthQuarter, this.depthQuarterTargetOpaque.texture); mol_util_1.ValueCell.update(this.downsampleDepthRenderableOpaque.values.uTexSize, linear_algebra_1.Vec2.set(this.downsampleDepthRenderableOpaque.values.uTexSize.ref.value, sw, sh)); mol_util_1.ValueCell.update(this.depthHalfRenderableOpaque.values.uTexSize, linear_algebra_1.Vec2.set(this.depthHalfRenderableOpaque.values.uTexSize.ref.value, hw, hh)); mol_util_1.ValueCell.update(this.depthQuarterRenderableOpaque.values.uTexSize, linear_algebra_1.Vec2.set(this.depthQuarterRenderableOpaque.values.uTexSize.ref.value, qw, qh)); this.downsampledDepthTargetTransparent.setSize(sw, sh); this.depthHalfTargetTransparent.setSize(hw, hh); this.depthQuarterTargetTransparent.setSize(qw, qh); const transparentDepthTexture = this.getTransparentDepthTexture(); mol_util_1.ValueCell.update(this.depthHalfRenderableTransparent.values.tColor, transparentDepthTexture); mol_util_1.ValueCell.update(this.renderable.values.tDepthTransparent, transparentDepthTexture); mol_util_1.ValueCell.update(this.renderable.values.tDepthHalfTransparent, this.depthHalfTargetTransparent.texture); mol_util_1.ValueCell.update(this.renderable.values.tDepthQuarterTransparent, this.depthQuarterTargetTransparent.texture); mol_util_1.ValueCell.update(this.downsampleDepthRenderableTransparent.values.uTexSize, linear_algebra_1.Vec2.set(this.downsampleDepthRenderableTransparent.values.uTexSize.ref.value, sw, sh)); mol_util_1.ValueCell.update(this.depthHalfRenderableTransparent.values.uTexSize, linear_algebra_1.Vec2.set(this.depthHalfRenderableTransparent.values.uTexSize.ref.value, hw, hh)); mol_util_1.ValueCell.update(this.depthQuarterRenderableTransparent.values.uTexSize, linear_algebra_1.Vec2.set(this.depthQuarterRenderableTransparent.values.uTexSize.ref.value, qw, qh)); mol_util_1.ValueCell.update(this.renderable.values.uTexSize, linear_algebra_1.Vec2.set(this.renderable.values.uTexSize.ref.value, sw, sh)); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.uTexSize, linear_algebra_1.Vec2.set(this.blurFirstPassRenderable.values.uTexSize.ref.value, sw, sh)); mol_util_1.ValueCell.update(this.blurSecondPassRenderable.values.uTexSize, linear_algebra_1.Vec2.set(this.blurSecondPassRenderable.values.uTexSize.ref.value, sw, sh)); } if (needsUpdateSsao) { this.renderable.update(); } if (needsUpdateSsaoBlur) { this.blurFirstPassRenderable.update(); this.blurSecondPassRenderable.update(); } if (needsUpdateDepthHalf) { this.depthHalfRenderableOpaque.update(); this.depthHalfRenderableTransparent.update(); } } render(camera) { if (debug_1.isTimingMode) this.webgl.timer.mark('SSAO.render'); const { state } = this.webgl; const { x, y, width, height } = camera.viewport; const includeTransparent = this.renderable.values.dIncludeTransparent.ref.value; const multiScale = this.renderable.values.dMultiScale.ref.value; const sx = Math.floor(x * this.ssaoScale); const sy = Math.floor(y * this.ssaoScale); const sw = Math.ceil(width * this.ssaoScale); const sh = Math.ceil(height * this.ssaoScale); state.viewport(sx, sy, sw, sh); state.scissor(sx, sy, sw, sh); if (this.ssaoScale < 1) { if (debug_1.isTimingMode) this.webgl.timer.mark('SSAO.downsample'); this.downsampledDepthTargetOpaque.bind(); this.downsampleDepthRenderableOpaque.render(); if (includeTransparent) { this.downsampledDepthTargetTransparent.bind(); this.downsampleDepthRenderableTransparent.render(); } if (debug_1.isTimingMode) this.webgl.timer.markEnd('SSAO.downsample'); } if (debug_1.isTimingMode) this.webgl.timer.mark('SSAO.half'); if (multiScale) { this.depthHalfTargetOpaque.bind(); this.depthHalfRenderableOpaque.render(); } if (multiScale && includeTransparent) { this.depthHalfTargetTransparent.bind(); this.depthHalfRenderableTransparent.render(); } if (debug_1.isTimingMode) this.webgl.timer.markEnd('SSAO.half'); if (debug_1.isTimingMode) this.webgl.timer.mark('SSAO.quarter'); if (multiScale) { this.depthQuarterTargetOpaque.bind(); this.depthQuarterRenderableOpaque.render(); } if (multiScale && includeTransparent) { this.depthQuarterTargetTransparent.bind(); this.depthQuarterRenderableTransparent.render(); } if (debug_1.isTimingMode) this.webgl.timer.markEnd('SSAO.quarter'); if (debug_1.isTimingMode) this.webgl.timer.mark('SSAO.opaque'); this.ssaoDepthTexture.attachFramebuffer(this.framebuffer, 'color0'); mol_util_1.ValueCell.update(this.renderable.values.uTransparencyFlag, 0); this.framebuffer.bind(); this.renderable.render(); if (debug_1.isTimingMode) this.webgl.timer.markEnd('SSAO.opaque'); if (debug_1.isTimingMode) this.webgl.timer.mark('SSAO.blurOpaque'); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.tSsaoDepth, this.ssaoDepthTexture); this.blurFirstPassRenderable.update(); this.blurFirstPassFramebuffer.bind(); this.blurFirstPassRenderable.render(); this.ssaoDepthTexture.attachFramebuffer(this.blurSecondPassFramebuffer, 'color0'); this.blurSecondPassFramebuffer.bind(); this.blurSecondPassRenderable.render(); if (debug_1.isTimingMode) this.webgl.timer.markEnd('SSAO.blurOpaque'); if (includeTransparent) { if (debug_1.isTimingMode) this.webgl.timer.mark('SSAO.transparent '); this.ssaoDepthTransparentTexture.attachFramebuffer(this.framebuffer, 'color0'); mol_util_1.ValueCell.update(this.renderable.values.uTransparencyFlag, 1); this.framebuffer.bind(); this.renderable.render(); if (debug_1.isTimingMode) this.webgl.timer.markEnd('SSAO.transparent '); if (debug_1.isTimingMode) this.webgl.timer.mark('SSAO.blurTransparent '); mol_util_1.ValueCell.update(this.blurFirstPassRenderable.values.tSsaoDepth, this.ssaoDepthTransparentTexture); this.blurFirstPassRenderable.update(); this.blurFirstPassFramebuffer.bind(); this.blurFirstPassRenderable.render(); this.ssaoDepthTransparentTexture.attachFramebuffer(this.blurSecondPassFramebuffer, 'color0'); this.blurSecondPassFramebuffer.bind(); this.blurSecondPassRenderable.render(); if (debug_1.isTimingMode) this.webgl.timer.markEnd('SSAO.blurTransparent '); } if (debug_1.isTimingMode) this.webgl.timer.markEnd('SSAO.render'); } } exports.SsaoPass = SsaoPass; const SsaoSchema = { ...util_1.QuadSchema, tDepth: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tDepthHalf: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tDepthQuarter: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), dIllumination: (0, schema_1.DefineSpec)('boolean'), uTransparencyFlag: (0, schema_1.UniformSpec)('i'), dIncludeTransparent: (0, schema_1.DefineSpec)('boolean'), tDepthTransparent: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tDepthHalfTransparent: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tDepthQuarterTransparent: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), uSamples: (0, schema_1.UniformSpec)('v3[]'), dNSamples: (0, schema_1.DefineSpec)('number'), uProjection: (0, schema_1.UniformSpec)('m4'), uInvProjection: (0, schema_1.UniformSpec)('m4'), uBounds: (0, schema_1.UniformSpec)('v4'), uTexSize: (0, schema_1.UniformSpec)('v2'), uRadius: (0, schema_1.UniformSpec)('f'), uBias: (0, schema_1.UniformSpec)('f'), dMultiScale: (0, schema_1.DefineSpec)('boolean'), dLevels: (0, schema_1.DefineSpec)('number'), uLevelRadius: (0, schema_1.UniformSpec)('f[]'), uLevelBias: (0, schema_1.UniformSpec)('f[]'), uNearThreshold: (0, schema_1.UniformSpec)('f'), uFarThreshold: (0, schema_1.UniformSpec)('f'), }; function getSsaoRenderable(ctx, depthTexture, depthHalfTexture, depthQuarterTexture, transparentDepthTexture, transparentDepthHalfTexture, transparentDepthQuarterTexture) { const values = { ...util_1.QuadValues, tDepth: mol_util_1.ValueCell.create(depthTexture), tDepthHalf: mol_util_1.ValueCell.create(depthHalfTexture), tDepthQuarter: mol_util_1.ValueCell.create(depthQuarterTexture), dIllumination: mol_util_1.ValueCell.create(false), dIncludeTransparent: mol_util_1.ValueCell.create(true), uTransparencyFlag: mol_util_1.ValueCell.create(0), tDepthTransparent: mol_util_1.ValueCell.create(transparentDepthTexture), tDepthHalfTransparent: mol_util_1.ValueCell.create(transparentDepthHalfTexture), tDepthQuarterTransparent: mol_util_1.ValueCell.create(transparentDepthQuarterTexture), uSamples: mol_util_1.ValueCell.create(getSamples(32)), dNSamples: mol_util_1.ValueCell.create(32), uProjection: mol_util_1.ValueCell.create(linear_algebra_1.Mat4.identity()), uInvProjection: mol_util_1.ValueCell.create(linear_algebra_1.Mat4.identity()), uBounds: mol_util_1.ValueCell.create((0, linear_algebra_1.Vec4)()), uTexSize: mol_util_1.ValueCell.create(linear_algebra_1.Vec2.create(ctx.gl.drawingBufferWidth, ctx.gl.drawingBufferHeight)), uRadius: mol_util_1.ValueCell.create(Math.pow(2, 5)), uBias: mol_util_1.ValueCell.create(0.8), dMultiScale: mol_util_1.ValueCell.create(false), dLevels: mol_util_1.ValueCell.create(3), uLevelRadius: mol_util_1.ValueCell.create([Math.pow(2, 2), Math.pow(2, 5), Math.pow(2, 8)]), uLevelBias: mol_util_1.ValueCell.create([0.8, 0.8, 0.8]), uNearThreshold: mol_util_1.ValueCell.create(10.0), uFarThreshold: mol_util_1.ValueCell.create(1500.0), }; const schema = { ...SsaoSchema }; const shaderCode = (0, shader_code_1.ShaderCode)('ssao', quad_vert_1.quad_vert, ssao_frag_1.ssao_frag); const renderItem = (0, render_item_1.createComputeRenderItem)(ctx, 'triangles', shaderCode, schema, values); return (0, renderable_1.createComputeRenderable)(renderItem, values); } const SsaoBlurSchema = { ...util_1.QuadSchema, tSsaoDepth: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), uTexSize: (0, schema_1.UniformSpec)('v2'), uKernel: (0, schema_1.UniformSpec)('f[]'), dOcclusionKernelSize: (0, schema_1.DefineSpec)('number'), uBlurDepthBias: (0, schema_1.UniformSpec)('f'), uBlurDirectionX: (0, schema_1.UniformSpec)('f'), uBlurDirectionY: (0, schema_1.UniformSpec)('f'), uInvProjection: (0, schema_1.UniformSpec)('m4'), uNear: (0, schema_1.UniformSpec)('f'), uFar: (0, schema_1.UniformSpec)('f'), uBounds: (0, schema_1.UniformSpec)('v4'), dOrthographic: (0, schema_1.DefineSpec)('number'), }; function getSsaoBlurRenderable(ctx, ssaoDepthTexture, direction) { const values = { ...util_1.QuadValues, tSsaoDepth: mol_util_1.ValueCell.create(ssaoDepthTexture), uTexSize: mol_util_1.ValueCell.create(linear_algebra_1.Vec2.create(ssaoDepthTexture.getWidth(), ssaoDepthTexture.getHeight())), uKernel: mol_util_1.ValueCell.create(getBlurKernel(15)), dOcclusionKernelSize: mol_util_1.ValueCell.create(15), uBlurDepthBias: mol_util_1.ValueCell.create(0.5), uBlurDirectionX: mol_util_1.ValueCell.create(direction === 'horizontal' ? 1 : 0), uBlurDirectionY: mol_util_1.ValueCell.create(direction === 'vertical' ? 1 : 0), uInvProjection: mol_util_1.ValueCell.create(linear_algebra_1.Mat4.identity()), uNear: mol_util_1.ValueCell.create(0.0), uFar: mol_util_1.ValueCell.create(10000.0), uBounds: mol_util_1.ValueCell.create((0, linear_algebra_1.Vec4)()), dOrthographic: mol_util_1.ValueCell.create(0), }; const schema = { ...SsaoBlurSchema }; const shaderCode = (0, shader_code_1.ShaderCode)('ssao_blur', quad_vert_1.quad_vert, ssao_blur_frag_1.ssaoBlur_frag); const renderItem = (0, render_item_1.createComputeRenderItem)(ctx, 'triangles', shaderCode, schema, values); return (0, renderable_1.createComputeRenderable)(renderItem, values); } function getBlurKernel(kernelSize) { const sigma = kernelSize / 3.0; const halfKernelSize = Math.floor((kernelSize + 1) / 2); const kernel = []; for (let x = 0; x < halfKernelSize; x++) { kernel.push((1.0 / ((Math.sqrt(2 * Math.PI)) * sigma)) * Math.exp(-x * x / (2 * sigma * sigma))); } return kernel; } const RandomHemisphereVector = []; for (let i = 0; i < 256; i++) { const v = (0, linear_algebra_1.Vec3)(); v[0] = Math.random() * 2.0 - 1.0; v[1] = Math.random() * 2.0 - 1.0; v[2] = Math.random(); linear_algebra_1.Vec3.normalize(v, v); linear_algebra_1.Vec3.scale(v, v, Math.random()); RandomHemisphereVector.push(v); } function getSamples(nSamples) { const samples = []; for (let i = 0; i < nSamples; i++) { let scale = (i * i + 2.0 * i + 1) / (nSamples * nSamples); scale = 0.1 + scale * (1.0 - 0.1); samples.push(RandomHemisphereVector[i][0] * scale); samples.push(RandomHemisphereVector[i][1] * scale); samples.push(RandomHemisphereVector[i][2] * scale); } return samples; }