UNPKG

molstar

Version:

A comprehensive macromolecular library.

364 lines (363 loc) 20.2 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.AntialiasingPass = exports.PostprocessingPass = exports.PostprocessingParams = 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 postprocessing_frag_1 = require("../../mol-gl/shader/postprocessing.frag"); const color_1 = require("../../mol-util/color"); const fxaa_1 = require("./fxaa"); const smaa_1 = require("./smaa"); const debug_1 = require("../../mol-util/debug"); const background_1 = require("./background"); const cas_1 = require("./cas"); const dof_1 = require("./dof"); const bloom_1 = require("./bloom"); const outline_1 = require("./outline"); const shadow_1 = require("./shadow"); const ssao_1 = require("./ssao"); const PostprocessingSchema = { ...util_1.QuadSchema, tSsaoDepth: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), tSsaoDepthTransparent: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), tColor: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), tTransparentColor: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), dBlendTransparency: (0, schema_1.DefineSpec)('boolean'), tDepthOpaque: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), tDepthTransparent: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), tShadows: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), tOutlines: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), uTexSize: (0, schema_1.UniformSpec)('v2'), dOrthographic: (0, schema_1.DefineSpec)('number'), uNear: (0, schema_1.UniformSpec)('f'), uFar: (0, schema_1.UniformSpec)('f'), uFogNear: (0, schema_1.UniformSpec)('f'), uFogFar: (0, schema_1.UniformSpec)('f'), uFogColor: (0, schema_1.UniformSpec)('v3'), uOutlineColor: (0, schema_1.UniformSpec)('v3'), uOcclusionColor: (0, schema_1.UniformSpec)('v3'), uTransparentBackground: (0, schema_1.UniformSpec)('b'), dOcclusionEnable: (0, schema_1.DefineSpec)('boolean'), dOcclusionSingleDepth: (0, schema_1.DefineSpec)('boolean'), dOcclusionIncludeOpacity: (0, schema_1.DefineSpec)('boolean'), dOcclusionIncludeTransparency: (0, schema_1.DefineSpec)('boolean'), uOcclusionOffset: (0, schema_1.UniformSpec)('v2'), dShadowEnable: (0, schema_1.DefineSpec)('boolean'), dOutlineEnable: (0, schema_1.DefineSpec)('boolean'), dOutlineScale: (0, schema_1.DefineSpec)('number'), dTransparentOutline: (0, schema_1.DefineSpec)('boolean'), }; function getPostprocessingRenderable(ctx, colorTexture, transparentColorTexture, depthTextureOpaque, depthTextureTransparent, shadowsTexture, outlinesTexture, ssaoDepthTexture, ssaoDepthTransparentTexture, transparentOutline) { const values = { ...util_1.QuadValues, tSsaoDepth: mol_util_1.ValueCell.create(ssaoDepthTexture), tSsaoDepthTransparent: mol_util_1.ValueCell.create(ssaoDepthTransparentTexture), tColor: mol_util_1.ValueCell.create(colorTexture), tTransparentColor: mol_util_1.ValueCell.create(transparentColorTexture), dBlendTransparency: mol_util_1.ValueCell.create(true), tDepthOpaque: mol_util_1.ValueCell.create(depthTextureOpaque), tDepthTransparent: mol_util_1.ValueCell.create(depthTextureTransparent), tShadows: mol_util_1.ValueCell.create(shadowsTexture), tOutlines: mol_util_1.ValueCell.create(outlinesTexture), uTexSize: mol_util_1.ValueCell.create(linear_algebra_1.Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())), dOrthographic: mol_util_1.ValueCell.create(0), uNear: mol_util_1.ValueCell.create(1), uFar: mol_util_1.ValueCell.create(10000), uFogNear: mol_util_1.ValueCell.create(10000), uFogFar: mol_util_1.ValueCell.create(10000), uFogColor: mol_util_1.ValueCell.create(linear_algebra_1.Vec3.create(1, 1, 1)), uOutlineColor: mol_util_1.ValueCell.create(linear_algebra_1.Vec3.create(0, 0, 0)), uOcclusionColor: mol_util_1.ValueCell.create(linear_algebra_1.Vec3.create(0, 0, 0)), uTransparentBackground: mol_util_1.ValueCell.create(false), dOcclusionEnable: mol_util_1.ValueCell.create(true), dOcclusionSingleDepth: mol_util_1.ValueCell.create(false), dOcclusionIncludeOpacity: mol_util_1.ValueCell.create(true), dOcclusionIncludeTransparency: mol_util_1.ValueCell.create(false), uOcclusionOffset: mol_util_1.ValueCell.create(linear_algebra_1.Vec2.create(0, 0)), dShadowEnable: mol_util_1.ValueCell.create(false), dOutlineEnable: mol_util_1.ValueCell.create(false), dOutlineScale: mol_util_1.ValueCell.create(1), dTransparentOutline: mol_util_1.ValueCell.create(transparentOutline), }; const schema = { ...PostprocessingSchema }; const shaderCode = (0, shader_code_1.ShaderCode)('postprocessing', quad_vert_1.quad_vert, postprocessing_frag_1.postprocessing_frag); const renderItem = (0, render_item_1.createComputeRenderItem)(ctx, 'triangles', shaderCode, schema, values); return (0, renderable_1.createComputeRenderable)(renderItem, values); } exports.PostprocessingParams = { occlusion: param_definition_1.ParamDefinition.MappedStatic('on', { on: param_definition_1.ParamDefinition.Group(ssao_1.SsaoParams), off: param_definition_1.ParamDefinition.Group({}) }, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }), shadow: param_definition_1.ParamDefinition.MappedStatic('off', { on: param_definition_1.ParamDefinition.Group(shadow_1.ShadowParams), off: param_definition_1.ParamDefinition.Group({}) }, { cycle: true, description: 'Simplistic shadows' }), outline: param_definition_1.ParamDefinition.MappedStatic('off', { on: param_definition_1.ParamDefinition.Group(outline_1.OutlineParams), off: param_definition_1.ParamDefinition.Group({}) }, { cycle: true, description: 'Draw outline around 3D objects' }), dof: param_definition_1.ParamDefinition.MappedStatic('off', { on: param_definition_1.ParamDefinition.Group(dof_1.DofParams), off: param_definition_1.ParamDefinition.Group({}) }, { cycle: true, description: 'DOF' }), antialiasing: param_definition_1.ParamDefinition.MappedStatic('smaa', { fxaa: param_definition_1.ParamDefinition.Group(fxaa_1.FxaaParams), smaa: param_definition_1.ParamDefinition.Group(smaa_1.SmaaParams), off: param_definition_1.ParamDefinition.Group({}) }, { options: [['fxaa', 'FXAA'], ['smaa', 'SMAA'], ['off', 'Off']], description: 'Smooth pixel edges' }), sharpening: param_definition_1.ParamDefinition.MappedStatic('off', { on: param_definition_1.ParamDefinition.Group(cas_1.CasParams), off: param_definition_1.ParamDefinition.Group({}) }, { cycle: true, description: 'Contrast Adaptive Sharpening' }), background: param_definition_1.ParamDefinition.Group(background_1.BackgroundParams, { isFlat: true }), bloom: param_definition_1.ParamDefinition.MappedStatic('on', { on: param_definition_1.ParamDefinition.Group(bloom_1.BloomParams), off: param_definition_1.ParamDefinition.Group({}) }, { cycle: true, description: 'Bloom' }), }; class PostprocessingPass { static isEnabled(props) { return ssao_1.SsaoPass.isEnabled(props) || shadow_1.ShadowPass.isEnabled(props) || outline_1.OutlinePass.isEnabled(props) || props.background.variant.name !== 'off'; } static isTransparentDepthRequired(scene, props) { return dof_1.DofPass.isEnabled(props) || outline_1.OutlinePass.isEnabled(props) && PostprocessingPass.isTransparentOutlineEnabled(props) || ssao_1.SsaoPass.isEnabled(props) && PostprocessingPass.isTransparentSsaoEnabled(scene, props); } static isTransparentOutlineEnabled(props) { var _a; return outline_1.OutlinePass.isEnabled(props) && ((_a = props.outline.params.includeTransparent) !== null && _a !== void 0 ? _a : true); } static isTransparentSsaoEnabled(scene, props) { return ssao_1.SsaoPass.isEnabled(props) && ssao_1.SsaoPass.isTransparentEnabled(scene, props.occlusion.params); } static isSsaoEnabled(props) { return ssao_1.SsaoPass.isEnabled(props); } constructor(webgl, assetManager, drawPass) { this.webgl = webgl; this.drawPass = drawPass; this.occlusionOffset = [0, 0]; this.transparentBackground = false; const { colorTarget, transparentColorTarget, depthTextureOpaque, depthTextureTransparent, packedDepth } = drawPass; const width = colorTarget.getWidth(); const height = colorTarget.getHeight(); // needs to be linear for anti-aliasing pass this.target = webgl.createRenderTarget(width, height, false, 'uint8', 'linear'); this.ssao = new ssao_1.SsaoPass(webgl, width, height, packedDepth, depthTextureOpaque, depthTextureTransparent); this.shadow = new shadow_1.ShadowPass(webgl, width, height, depthTextureOpaque); this.outline = new outline_1.OutlinePass(webgl, width, height, depthTextureTransparent, depthTextureOpaque); this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, transparentColorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.shadow.target.texture, this.outline.target.texture, this.ssao.ssaoDepthTexture, this.ssao.ssaoDepthTransparentTexture, true); this.background = new background_1.BackgroundPass(webgl, assetManager, width, height); } setSize(width, height) { const [w, h] = this.renderable.values.uTexSize.ref.value; if (width !== w || height !== h) { this.target.setSize(width, height); mol_util_1.ValueCell.update(this.renderable.values.uTexSize, linear_algebra_1.Vec2.set(this.renderable.values.uTexSize.ref.value, width, height)); } this.ssao.setSize(width, height); this.shadow.setSize(width, height); this.outline.setSize(width, height); this.background.setSize(width, height); } reset() { this.ssao.reset(); } updateState(camera, scene, transparentBackground, backgroundColor, props, light, ambientColor) { let needsUpdateMain = false; const orthographic = camera.state.mode === 'orthographic' ? 1 : 0; const outlinesEnabled = outline_1.OutlinePass.isEnabled(props); const shadowsEnabled = shadow_1.ShadowPass.isEnabled(props); const occlusionEnabled = ssao_1.SsaoPass.isEnabled(props); if (occlusionEnabled) { const params = props.occlusion.params; this.ssao.update(camera, scene, params); const includeTransparency = ssao_1.SsaoPass.isTransparentEnabled(scene, params); if (this.renderable.values.dOcclusionIncludeTransparency.ref.value !== includeTransparency) { needsUpdateMain = true; mol_util_1.ValueCell.update(this.renderable.values.dOcclusionIncludeTransparency, includeTransparency); } mol_util_1.ValueCell.update(this.renderable.values.uOcclusionColor, color_1.Color.toVec3Normalized(this.renderable.values.uOcclusionColor.ref.value, params.color)); } if (shadowsEnabled) { this.shadow.update(camera, light, ambientColor, props.shadow.params); } if (outlinesEnabled) { const outlineProps = props.outline.params; const { transparentOutline, outlineScale } = this.outline.update(camera, outlineProps, this.drawPass.depthTextureTransparent, this.drawPass.depthTextureOpaque); mol_util_1.ValueCell.update(this.renderable.values.uOutlineColor, color_1.Color.toVec3Normalized(this.renderable.values.uOutlineColor.ref.value, outlineProps.color)); if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) { needsUpdateMain = true; mol_util_1.ValueCell.update(this.renderable.values.dOutlineScale, outlineScale); } if (this.renderable.values.dTransparentOutline.ref.value !== transparentOutline) { needsUpdateMain = true; mol_util_1.ValueCell.update(this.renderable.values.dTransparentOutline, transparentOutline); } } mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uFar, camera.far); mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uNear, camera.near); mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uFogFar, camera.fogFar); mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uFogNear, camera.fogNear); mol_util_1.ValueCell.update(this.renderable.values.uFogColor, color_1.Color.toVec3Normalized(this.renderable.values.uFogColor.ref.value, backgroundColor)); mol_util_1.ValueCell.updateIfChanged(this.renderable.values.uTransparentBackground, transparentBackground); if (this.renderable.values.dOrthographic.ref.value !== orthographic) { needsUpdateMain = true; mol_util_1.ValueCell.update(this.renderable.values.dOrthographic, orthographic); } if (this.renderable.values.dOutlineEnable.ref.value !== outlinesEnabled) { needsUpdateMain = true; mol_util_1.ValueCell.update(this.renderable.values.dOutlineEnable, outlinesEnabled); } if (this.renderable.values.dShadowEnable.ref.value !== shadowsEnabled) { needsUpdateMain = true; mol_util_1.ValueCell.update(this.renderable.values.dShadowEnable, shadowsEnabled); } if (this.renderable.values.dOcclusionEnable.ref.value !== occlusionEnabled) { needsUpdateMain = true; mol_util_1.ValueCell.update(this.renderable.values.dOcclusionEnable, occlusionEnabled); } const blendTransparency = scene.opacityAverage < 1; if (this.renderable.values.dBlendTransparency.ref.value !== blendTransparency) { needsUpdateMain = true; mol_util_1.ValueCell.update(this.renderable.values.dBlendTransparency, blendTransparency); } if (needsUpdateMain) { this.renderable.update(); } const { gl, state } = this.webgl; state.enable(gl.SCISSOR_TEST); state.disable(gl.BLEND); state.disable(gl.DEPTH_TEST); state.depthMask(false); } setOcclusionOffset(x, y) { this.occlusionOffset[0] = x; this.occlusionOffset[1] = y; mol_util_1.ValueCell.update(this.renderable.values.uOcclusionOffset, linear_algebra_1.Vec2.set(this.renderable.values.uOcclusionOffset.ref.value, x, y)); } setTransparentBackground(value) { this.transparentBackground = value; } render(camera, scene, toDrawingBuffer, transparentBackground, backgroundColor, props, light, ambientColor) { if (debug_1.isTimingMode) this.webgl.timer.mark('PostprocessingPass.render'); this.updateState(camera, scene, transparentBackground, backgroundColor, props, light, ambientColor); const { state } = this.webgl; const { x, y, width, height } = camera.viewport; // don't render occlusion if offset is given, // which will reuse the existing occlusion if (props.occlusion.name === 'on' && this.occlusionOffset[0] === 0 && this.occlusionOffset[1] === 0) { this.ssao.render(camera); } state.viewport(x, y, width, height); state.scissor(x, y, width, height); if (props.outline.name === 'on') { this.outline.render(); } if (props.shadow.name === 'on') { this.shadow.render(); } if (toDrawingBuffer) { this.webgl.unbindFramebuffer(); } else { this.target.bind(); } this.background.update(camera, props.background); this.background.clear(props.background, this.transparentBackground, backgroundColor); this.background.render(props.background); this.renderable.render(); if (debug_1.isTimingMode) this.webgl.timer.markEnd('PostprocessingPass.render'); } } exports.PostprocessingPass = PostprocessingPass; class AntialiasingPass { static isEnabled(props) { return props.antialiasing.name !== 'off'; } constructor(webgl, width, height) { this.target = webgl.createRenderTarget(width, height, false); this.internalTarget = webgl.createRenderTarget(width, height, false); this.fxaa = new fxaa_1.FxaaPass(webgl, this.target.texture); this.smaa = new smaa_1.SmaaPass(webgl, this.target.texture); this.cas = new cas_1.CasPass(webgl, this.target.texture); } setSize(width, height) { const w = this.target.texture.getWidth(); const h = this.target.texture.getHeight(); if (width !== w || height !== h) { this.target.setSize(width, height); this.internalTarget.setSize(width, height); this.fxaa.setSize(width, height); if (this.smaa.supported) this.smaa.setSize(width, height); this.cas.setSize(width, height); } } _renderFxaa(camera, input, target, props) { if (props.antialiasing.name !== 'fxaa') return; this.fxaa.update(input, props.antialiasing.params); this.fxaa.render(camera.viewport, target); } _renderSmaa(camera, input, target, props) { if (props.antialiasing.name !== 'smaa') return; this.smaa.update(input, props.antialiasing.params); this.smaa.render(camera.viewport, target); } _renderAntialiasing(camera, input, target, props) { if (props.antialiasing.name === 'fxaa') { this._renderFxaa(camera, input, target, props); } else if (props.antialiasing.name === 'smaa') { this._renderSmaa(camera, input, target, props); } } _renderCas(camera, input, target, props) { if (props.sharpening.name !== 'on') return; if (props.antialiasing.name !== 'off') input = this.internalTarget.texture; this.cas.update(input, props.sharpening.params); this.cas.render(camera.viewport, target); } render(camera, input, toDrawingBuffer, props) { if (props.antialiasing.name === 'off' && props.sharpening.name === 'off') return; if (props.antialiasing.name === 'smaa' && !this.smaa.supported) { console.error('SMAA not supported, missing "HTMLImageElement"'); return; } const target = toDrawingBuffer === true ? undefined : toDrawingBuffer === false ? this.target : toDrawingBuffer; if (props.sharpening.name === 'off') { this._renderAntialiasing(camera, input, target, props); } else if (props.antialiasing.name === 'off') { this._renderCas(camera, input, target, props); } else { this._renderAntialiasing(camera, input, this.internalTarget, props); this._renderCas(camera, input, target, props); } } } exports.AntialiasingPass = AntialiasingPass;