molstar
Version:
A comprehensive macromolecular library.
524 lines (523 loc) • 32.8 kB
JavaScript
"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;
}