UNPKG

molstar

Version:

A comprehensive macromolecular library.

273 lines (272 loc) 15.4 kB
"use strict"; /** * Copyright (c) 2024 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * * Partially adapted from three.js, The MIT License, Copyright © 2010-2024 three.js authors */ Object.defineProperty(exports, "__esModule", { value: true }); exports.BloomPass = exports.BloomParams = void 0; const util_1 = require("../../mol-gl/compute/util"); const renderable_1 = require("../../mol-gl/renderable"); const schema_1 = require("../../mol-gl/renderable/schema"); const shader_code_1 = require("../../mol-gl/shader-code"); const render_item_1 = require("../../mol-gl/webgl/render-item"); const texture_1 = require("../../mol-gl/webgl/texture"); const linear_algebra_1 = require("../../mol-math/linear-algebra"); const mol_util_1 = require("../../mol-util"); const param_definition_1 = require("../../mol-util/param-definition"); const quad_vert_1 = require("../../mol-gl/shader/quad.vert"); const debug_1 = require("../../mol-util/debug"); const composite_frag_1 = require("../../mol-gl/shader/bloom/composite.frag"); const luminosity_frag_1 = require("../../mol-gl/shader/bloom/luminosity.frag"); const blur_frag_1 = require("../../mol-gl/shader/bloom/blur.frag"); const memoize_1 = require("../../mol-util/memoize"); const MipCount = 5; exports.BloomParams = { strength: param_definition_1.ParamDefinition.Numeric(1, { min: 0, max: 3, step: 0.1 }), radius: param_definition_1.ParamDefinition.Numeric(0, { min: 0, max: 1, step: 0.01 }), threshold: param_definition_1.ParamDefinition.Numeric(0, { min: 0, max: 1, step: 0.01 }, { description: 'Luminosity threshold', hideIf: p => p.mode === 'emissive' }), mode: param_definition_1.ParamDefinition.Select('emissive', [['luminosity', 'Luminosity'], ['emissive', 'Emissive']]), }; class BloomPass { static isEnabled(props) { return props.bloom.name === 'on'; } constructor(webgl, width, height) { this.webgl = webgl; this.horizontalBlurTargets = []; this.verticalBlurTargets = []; this.emissiveTarget = webgl.createRenderTarget(width, height, true, 'uint8', 'linear', 'rgba'); this.luminosityTarget = webgl.createRenderTarget(width, height, false, 'uint8', 'linear'); this.compositeTarget = webgl.createRenderTarget(width, height, false, 'uint8', 'linear'); let blurWidth = Math.round(width / 2); let blurHeight = Math.round(height / 2); for (let i = 0; i < MipCount; ++i) { this.horizontalBlurTargets[i] = webgl.createRenderTarget(blurWidth, blurHeight, false, 'uint8', 'linear'); this.verticalBlurTargets[i] = webgl.createRenderTarget(blurWidth, blurHeight, false, 'uint8', 'linear'); blurWidth = Math.round(blurWidth / 2); blurHeight = Math.round(blurHeight / 2); } const nullTexture = (0, texture_1.createNullTexture)(); this.luminosityRenderable = getLuminosityRenderable(webgl, nullTexture, nullTexture, nullTexture); this.blurRenderable = getBlurRenderable(webgl, nullTexture); this.compositeRenderable = getCompositeRenderable(webgl, width, height, this.verticalBlurTargets[0].texture, this.verticalBlurTargets[1].texture, this.verticalBlurTargets[2].texture, this.verticalBlurTargets[3].texture, this.verticalBlurTargets[4].texture); this.copyRenderable = (0, util_1.createCopyRenderable)(webgl, this.compositeTarget.texture); } setSize(width, height) { const w = this.luminosityTarget.getWidth(); const h = this.luminosityTarget.getHeight(); if (width !== w || height !== h) { this.emissiveTarget.setSize(width, height); this.luminosityTarget.setSize(width, height); this.compositeTarget.setSize(width, height); let blurWidth = Math.round(width / 2); let blurHeight = Math.round(height / 2); for (let i = 0; i < MipCount; ++i) { this.horizontalBlurTargets[i].setSize(blurWidth, blurHeight); this.verticalBlurTargets[i].setSize(blurWidth, blurHeight); blurWidth = Math.round(blurWidth / 2); blurHeight = Math.round(blurHeight / 2); } mol_util_1.ValueCell.update(this.luminosityRenderable.values.uTexSizeInv, linear_algebra_1.Vec2.set(this.compositeRenderable.values.uTexSizeInv.ref.value, 1 / width, 1 / height)); mol_util_1.ValueCell.update(this.compositeRenderable.values.uTexSizeInv, linear_algebra_1.Vec2.set(this.compositeRenderable.values.uTexSizeInv.ref.value, 1 / width, 1 / height)); mol_util_1.ValueCell.update(this.copyRenderable.values.uTexSize, linear_algebra_1.Vec2.set(this.copyRenderable.values.uTexSize.ref.value, width, height)); } } update(input, emissive, depth, props) { let luminosityNeedsUpdate = false; if (this.luminosityRenderable.values.tColor.ref.value !== input) { mol_util_1.ValueCell.update(this.luminosityRenderable.values.tColor, input); luminosityNeedsUpdate = true; } if (this.luminosityRenderable.values.tEmissive.ref.value !== emissive) { mol_util_1.ValueCell.update(this.luminosityRenderable.values.tEmissive, emissive); luminosityNeedsUpdate = true; } if (this.luminosityRenderable.values.tDepth.ref.value !== depth) { mol_util_1.ValueCell.update(this.luminosityRenderable.values.tDepth, depth); luminosityNeedsUpdate = true; } if (this.luminosityRenderable.values.dMode.ref.value !== props.mode) { mol_util_1.ValueCell.update(this.luminosityRenderable.values.dMode, props.mode); luminosityNeedsUpdate = true; } mol_util_1.ValueCell.updateIfChanged(this.luminosityRenderable.values.uLuminosityThreshold, props.threshold); if (luminosityNeedsUpdate) { this.luminosityRenderable.update(); } // let blurNeedsUpdate = false; if (this.blurRenderable.values.tInput.ref.value !== input) { mol_util_1.ValueCell.update(this.blurRenderable.values.tInput, input); blurNeedsUpdate = true; } if (blurNeedsUpdate) { this.blurRenderable.update(); } // mol_util_1.ValueCell.update(this.compositeRenderable.values.uBloomRadius, props.radius); mol_util_1.ValueCell.update(this.compositeRenderable.values.uBloomStrength, props.strength); } render(viewport, target) { if (debug_1.isTimingMode) this.webgl.timer.mark('BloomPass.render'); const { gl, state } = this.webgl; const { x, y, width, height } = viewport; state.viewport(x, y, width, height); state.scissor(x, y, width, height); // printTextureImage(readTexture(this.webgl, this.luminosityRenderable.values.tEmissive.ref.value, new Uint8Array(width * height * 4)), { scale: 0.25, id: 'emissive' }); state.enable(gl.SCISSOR_TEST); state.disable(gl.BLEND); state.disable(gl.DEPTH_TEST); state.depthMask(false); // Extract Bright Areas this.luminosityTarget.bind(); this.luminosityRenderable.render(); // printTextureImage(readTexture(this.webgl, this.luminosityTarget.texture, new Uint8Array(width * height * 4)), { scale: 0.25, id: 'luminosity' }); // Blur All the mips progressively for (let i = 0; i < MipCount; ++i) { const blurWidth = this.horizontalBlurTargets[i].getWidth(); const blurHeight = this.horizontalBlurTargets[i].getHeight(); state.viewport(0, 0, blurWidth, blurHeight); state.scissor(0, 0, blurWidth, blurHeight); mol_util_1.ValueCell.update(this.blurRenderable.values.dKernelRadius, BlurKernelSizes[i]); mol_util_1.ValueCell.update(this.blurRenderable.values.uGaussianCoefficients, getBlurCoefficients(BlurKernelSizes[i])); mol_util_1.ValueCell.update(this.blurRenderable.values.uTexSizeInv, linear_algebra_1.Vec2.set(this.blurRenderable.values.uTexSizeInv.ref.value, 1 / blurWidth, 1 / blurHeight)); this.horizontalBlurTargets[i].bind(); mol_util_1.ValueCell.update(this.blurRenderable.values.tInput, i === 0 ? this.luminosityTarget.texture : this.verticalBlurTargets[i - 1].texture); mol_util_1.ValueCell.update(this.blurRenderable.values.uDirection, BlurDirectionX); this.blurRenderable.update(); this.blurRenderable.render(); this.verticalBlurTargets[i].bind(); mol_util_1.ValueCell.update(this.blurRenderable.values.tInput, this.horizontalBlurTargets[i].texture); mol_util_1.ValueCell.update(this.blurRenderable.values.uDirection, BlurDirectionY); this.blurRenderable.update(); this.blurRenderable.render(); // printTextureImage(readTexture(this.webgl, this.verticalBlurTargets[i].texture, new Uint8Array(blurWidth * blurHeight * 4)), { scale: 0.25, id: `blur-${i}` }); } state.viewport(x, y, width, height); state.scissor(x, y, width, height); // Composite All the mips this.compositeTarget.bind(); this.compositeRenderable.update(); this.compositeRenderable.render(); // printTextureImage(readTexture(this.webgl, this.compositeTarget.texture, new Uint8Array(width * height * 4)), { scale: 0.25, id: 'composite' }); if (target) { target.bind(); } else { this.webgl.unbindFramebuffer(); } state.enable(gl.BLEND); state.blendFunc(gl.ONE, gl.ONE); this.copyRenderable.render(); if (debug_1.isTimingMode) this.webgl.timer.markEnd('BloomPass.render'); } } exports.BloomPass = BloomPass; // const LuminositySchema = { ...util_1.QuadSchema, tColor: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tEmissive: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tDepth: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), uTexSizeInv: (0, schema_1.UniformSpec)('v2'), uDefaultColor: (0, schema_1.UniformSpec)('v3'), uDefaultOpacity: (0, schema_1.UniformSpec)('f'), uLuminosityThreshold: (0, schema_1.UniformSpec)('f'), uSmoothWidth: (0, schema_1.UniformSpec)('f'), dMode: (0, schema_1.DefineSpec)('string', ['luminosity', 'emissive']), }; const LuminosityShaderCode = (0, shader_code_1.ShaderCode)('Bloom Luminosity', quad_vert_1.quad_vert, luminosity_frag_1.luminosity_frag); function getLuminosityRenderable(ctx, colorTexture, emissiveTexture, depthTexture) { const width = colorTexture.getWidth(); const height = colorTexture.getHeight(); const values = { ...util_1.QuadValues, tColor: mol_util_1.ValueCell.create(colorTexture), tEmissive: mol_util_1.ValueCell.create(emissiveTexture), tDepth: mol_util_1.ValueCell.create(depthTexture), uTexSizeInv: mol_util_1.ValueCell.create(linear_algebra_1.Vec2.create(1 / width, 1 / height)), uDefaultColor: mol_util_1.ValueCell.create((0, linear_algebra_1.Vec3)()), uDefaultOpacity: mol_util_1.ValueCell.create(0), uLuminosityThreshold: mol_util_1.ValueCell.create(0), uSmoothWidth: mol_util_1.ValueCell.create(1), dMode: mol_util_1.ValueCell.create('emissive'), }; const schema = { ...LuminositySchema }; const renderItem = (0, render_item_1.createComputeRenderItem)(ctx, 'triangles', LuminosityShaderCode, schema, values); return (0, renderable_1.createComputeRenderable)(renderItem, values); } // function _getBlurCoefficients(kernelRadius) { const coefficients = []; for (let i = 0; i < kernelRadius; ++i) { coefficients.push(0.39894 * Math.exp(-0.5 * i * i / (kernelRadius * kernelRadius)) / kernelRadius); } return coefficients; } const getBlurCoefficients = (0, memoize_1.memoize1)(_getBlurCoefficients); const BlurKernelSizes = [3, 5, 7, 9, 11]; const BlurDirectionX = linear_algebra_1.Vec2.create(1, 0); const BlurDirectionY = linear_algebra_1.Vec2.create(0, 1); const BlurSchema = { ...util_1.QuadSchema, tInput: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), uTexSizeInv: (0, schema_1.UniformSpec)('v2'), uDirection: (0, schema_1.UniformSpec)('v2'), uGaussianCoefficients: (0, schema_1.UniformSpec)('f[]'), dKernelRadius: (0, schema_1.DefineSpec)('number'), }; const BlurShaderCode = (0, shader_code_1.ShaderCode)('Bloom Blur', quad_vert_1.quad_vert, blur_frag_1.blur_frag); function getBlurRenderable(ctx, inputTexture) { const width = inputTexture.getWidth(); const height = inputTexture.getHeight(); const values = { ...util_1.QuadValues, tInput: mol_util_1.ValueCell.create(inputTexture), uTexSizeInv: mol_util_1.ValueCell.create(linear_algebra_1.Vec2.create(1 / width, 1 / height)), uDirection: mol_util_1.ValueCell.create((0, linear_algebra_1.Vec2)()), uGaussianCoefficients: mol_util_1.ValueCell.create([]), dKernelRadius: mol_util_1.ValueCell.create(BlurKernelSizes[0]), }; const schema = { ...BlurSchema }; const renderItem = (0, render_item_1.createComputeRenderItem)(ctx, 'triangles', BlurShaderCode, schema, values); return (0, renderable_1.createComputeRenderable)(renderItem, values); } // const CompositeSchema = { ...util_1.QuadSchema, tBlur1: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tBlur2: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tBlur3: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tBlur4: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), tBlur5: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'linear'), uTexSizeInv: (0, schema_1.UniformSpec)('v2'), uBloomStrength: (0, schema_1.UniformSpec)('f'), uBloomRadius: (0, schema_1.UniformSpec)('f'), uBloomFactors: (0, schema_1.UniformSpec)('f[]'), uBloomTints: (0, schema_1.UniformSpec)('v3[]'), }; const CompositeShaderCode = (0, shader_code_1.ShaderCode)('Bloom Composite', quad_vert_1.quad_vert, composite_frag_1.composite_frag); function getCompositeRenderable(ctx, width, height, blurTexture1, blurTexture2, blurTexture3, blurTexture4, blurTexture5) { const values = { ...util_1.QuadValues, uTexSizeInv: mol_util_1.ValueCell.create(linear_algebra_1.Vec2.create(width, height)), tBlur1: mol_util_1.ValueCell.create(blurTexture1), tBlur2: mol_util_1.ValueCell.create(blurTexture2), tBlur3: mol_util_1.ValueCell.create(blurTexture3), tBlur4: mol_util_1.ValueCell.create(blurTexture4), tBlur5: mol_util_1.ValueCell.create(blurTexture5), uBloomStrength: mol_util_1.ValueCell.create(1), uBloomRadius: mol_util_1.ValueCell.create(0), uBloomFactors: mol_util_1.ValueCell.create([1.0, 0.8, 0.6, 0.4, 0.2]), uBloomTints: mol_util_1.ValueCell.create(new Array(5 * 3).fill(1)), }; const schema = { ...CompositeSchema }; const renderItem = (0, render_item_1.createComputeRenderItem)(ctx, 'triangles', CompositeShaderCode, schema, values); return (0, renderable_1.createComputeRenderable)(renderItem, values); }