From 0c54b0dd6e5da1f415cb01f2963c4d9faaae44b6 Mon Sep 17 00:00:00 2001 From: Alice Russell <arussell@exscientia.co.uk> Date: Wed, 7 Dec 2022 12:21:31 +0000 Subject: [PATCH] Add option to toogle transparent outline --- CHANGELOG.md | 2 +- src/apps/docking-viewer/viewport.tsx | 1 + src/examples/lighting/index.ts | 4 +-- src/extensions/cellpack/model.ts | 1 + src/mol-canvas3d/passes/draw.ts | 3 -- src/mol-canvas3d/passes/postprocessing.ts | 35 ++++++++++++++------ src/mol-gl/renderer.ts | 8 +---- src/mol-gl/shader/outlines.frag.ts | 6 +++- src/mol-gl/shader/postprocessing.frag.ts | 6 +++- src/mol-plugin-ui/structure/quick-styles.tsx | 4 +-- 10 files changed, 42 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93992814f..d53ee9910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Note that since we don't clearly distinguish between a public and private interf ## [Unreleased] -- Add option `minimumOpacity` parameter to outlines to hide any outlines of components with a lower alpha value lower +- Add an `includeTransparent` parameter to hide/show outlines of components that are transparent ## [v3.25.1] - 2022-11-20 diff --git a/src/apps/docking-viewer/viewport.tsx b/src/apps/docking-viewer/viewport.tsx index 67976ac1e..94beb975d 100644 --- a/src/apps/docking-viewer/viewport.tsx +++ b/src/apps/docking-viewer/viewport.tsx @@ -54,6 +54,7 @@ function occlusionStyle(plugin: PluginContext) { scale: 1.0, threshold: 0.33, color: Color(0x0000), + includeTransparent: true, } } } } }); diff --git a/src/examples/lighting/index.ts b/src/examples/lighting/index.ts index 23b254b5e..47018a13b 100644 --- a/src/examples/lighting/index.ts +++ b/src/examples/lighting/index.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -25,7 +25,7 @@ const Canvas3DPresets = { canvas3d: <Preset>{ postprocessing: { occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } }, - outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000) } } + outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000), includeTransparent: true, } } }, renderer: { ambientIntensity: 1.0, diff --git a/src/extensions/cellpack/model.ts b/src/extensions/cellpack/model.ts index 80fb0b3d9..38a74bbd0 100644 --- a/src/extensions/cellpack/model.ts +++ b/src/extensions/cellpack/model.ts @@ -612,6 +612,7 @@ export const LoadCellPackModel = StateAction.build({ scale: 1, threshold: 0.33, color: ColorNames.black, + includeTransparent: true, } } } diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts index 00dd3b4a7..34282e840 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -145,7 +145,6 @@ export class DrawPass { if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { this.depthTargetTransparent.bind(); renderer.clearDepth(true); - renderer.setOutlineAlphaThreshold(PostprocessingPass.getOutlineMinimumOpacity(postprocessingProps)); if (scene.opacityAverage < 1) { renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque); } @@ -200,7 +199,6 @@ export class DrawPass { if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { this.depthTargetTransparent.bind(); renderer.clearDepth(true); - renderer.setOutlineAlphaThreshold(PostprocessingPass.getOutlineMinimumOpacity(postprocessingProps)); if (scene.opacityAverage < 1) { renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque); } @@ -265,7 +263,6 @@ export class DrawPass { if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { this.depthTargetTransparent.bind(); renderer.clearDepth(true); - renderer.setOutlineAlphaThreshold(PostprocessingPass.getOutlineMinimumOpacity(postprocessingProps)); if (scene.opacityAverage < 1) { renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque); } diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index eaed90a38..5a113fe76 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/src/mol-canvas3d/passes/postprocessing.ts @@ -42,10 +42,12 @@ const OutlinesSchema = { uFar: UniformSpec('f'), uMaxPossibleViewZDiff: UniformSpec('f'), + + dTransparentOutline: DefineSpec('boolean'), }; type OutlinesRenderable = ComputeRenderable<Values<typeof OutlinesSchema>> -function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, depthTextureTransparent: Texture): OutlinesRenderable { +function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, depthTextureTransparent: Texture, transparentOutline: boolean): OutlinesRenderable { const width = depthTextureOpaque.getWidth(); const height = depthTextureOpaque.getHeight(); @@ -60,6 +62,8 @@ function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, d uFar: ValueCell.create(10000), uMaxPossibleViewZDiff: ValueCell.create(0.5), + + dTransparentOutline: ValueCell.create(transparentOutline), }; const schema = { ...OutlinesSchema }; @@ -224,10 +228,12 @@ const PostprocessingSchema = { dOutlineEnable: DefineSpec('boolean'), dOutlineScale: DefineSpec('number'), uOutlineThreshold: UniformSpec('f'), + + dTransparentOutline: DefineSpec('boolean'), }; type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>> -function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable { +function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture, transparentOutline: boolean): PostprocessingRenderable { const values: Values<typeof PostprocessingSchema> = { ...QuadValues, tSsaoDepth: ValueCell.create(ssaoDepthTexture), @@ -254,6 +260,8 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d dOutlineEnable: ValueCell.create(false), dOutlineScale: ValueCell.create(1), uOutlineThreshold: ValueCell.create(0.33), + + dTransparentOutline: ValueCell.create(transparentOutline), }; const schema = { ...PostprocessingSchema }; @@ -279,7 +287,7 @@ export const PostprocessingParams = { scale: PD.Numeric(1, { min: 1, max: 5, step: 1 }), threshold: PD.Numeric(0.33, { min: 0.01, max: 1, step: 0.01 }), color: PD.Color(Color(0x000000)), - minimumOpacity: PD.Optional(PD.Numeric(0.0, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be have an outline' })), + includeTransparent: PD.Boolean(true, { description: 'Whether to show outline for objects with an opacity value less than 1' }), }), off: PD.Group({}) }, { cycle: true, description: 'Draw outline around 3D objects' }), @@ -301,10 +309,6 @@ export class PostprocessingPass { return props.outline.name === 'on'; } - static getOutlineMinimumOpacity(props: PostprocessingProps) { - return props.outline.name === 'on' ? props.outline.params.minimumOpacity ?? 0.0 : 0.0; - } - readonly target: RenderTarget; private readonly outlinesTarget: RenderTarget; @@ -353,7 +357,7 @@ export class PostprocessingPass { this.target = webgl.createRenderTarget(width, height, false, 'uint8', 'linear'); this.outlinesTarget = webgl.createRenderTarget(width, height, false); - this.outlinesRenderable = getOutlinesRenderable(webgl, depthTextureOpaque, depthTextureTransparent); + this.outlinesRenderable = getOutlinesRenderable(webgl, depthTextureOpaque, depthTextureTransparent, true); this.ssaoFramebuffer = webgl.resources.framebuffer(); this.ssaoBlurFirstPassFramebuffer = webgl.resources.framebuffer(); @@ -378,7 +382,7 @@ export class PostprocessingPass { this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTextureOpaque : this.downsampledDepthTarget.texture); this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal'); this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical'); - this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.outlinesTarget.texture, this.ssaoDepthTexture); + this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.outlinesTarget.texture, this.ssaoDepthTexture, true); this.background = new BackgroundPass(webgl, assetManager, width, height); } @@ -413,6 +417,7 @@ export class PostprocessingPass { let needsUpdateMain = false; let needsUpdateSsao = false; let needsUpdateSsaoBlur = false; + let needsUpdateOutline = false; const orthographic = camera.state.mode === 'orthographic' ? 1 : 0; const outlinesEnabled = props.outline.name === 'on'; @@ -500,23 +505,27 @@ export class PostprocessingPass { } if (props.outline.name === 'on') { - let { threshold } = props.outline.params; + let { threshold, includeTransparent } = props.outline.params; + const transparentOutline = includeTransparent ?? true; // orthographic needs lower threshold if (camera.state.mode === 'orthographic') threshold /= 5; const factor = Math.pow(1000, threshold) / 1000; // use radiusMax for stable outlines when zooming const maxPossibleViewZDiff = factor * camera.state.radiusMax; const outlineScale = props.outline.params.scale - 1; - ValueCell.updateIfChanged(this.outlinesRenderable.values.uNear, camera.near); ValueCell.updateIfChanged(this.outlinesRenderable.values.uFar, camera.far); ValueCell.updateIfChanged(this.outlinesRenderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff); + if (this.renderable.values.dTransparentOutline.ref.value !== transparentOutline) { needsUpdateOutline = true; } + ValueCell.updateIfChanged(this.outlinesRenderable.values.dTransparentOutline, transparentOutline); ValueCell.update(this.renderable.values.uOutlineColor, Color.toVec3Normalized(this.renderable.values.uOutlineColor.ref.value, props.outline.params.color)); ValueCell.updateIfChanged(this.renderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff); if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) { needsUpdateMain = true; } ValueCell.updateIfChanged(this.renderable.values.dOutlineScale, outlineScale); + if (this.renderable.values.dTransparentOutline.ref.value !== transparentOutline) { needsUpdateMain = true; } + ValueCell.updateIfChanged(this.renderable.values.dTransparentOutline, transparentOutline); } ValueCell.updateIfChanged(this.renderable.values.uFar, camera.far); @@ -532,6 +541,10 @@ export class PostprocessingPass { if (this.renderable.values.dOcclusionEnable.ref.value !== occlusionEnabled) { needsUpdateMain = true; } ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, occlusionEnabled); + if (needsUpdateOutline) { + this.outlinesRenderable.update(); + } + if (needsUpdateSsao) { this.ssaoRenderable.update(); } diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index ab6548600..086f6db40 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -80,7 +80,6 @@ interface Renderer { setTransparentBackground: (value: boolean) => void setDrawingBufferSize: (width: number, height: number) => void setPixelRatio: (value: number) => void - setOutlineAlphaThreshold: (value: number) => void dispose: () => void } @@ -187,8 +186,6 @@ namespace Renderer { const ambientColor = Vec3(); Vec3.scale(ambientColor, Color.toArrayNormalized(p.ambientColor, ambientColor, 0), p.ambientIntensity); - let outlineAlphaThreshold = 0.0; - const globalUniforms: GlobalUniformValues = { uModel: ValueCell.create(Mat4.identity()), uView: ValueCell.create(view), @@ -435,7 +432,7 @@ namespace Renderer { const { renderables } = group; for (let i = 0, il = renderables.length; i < il; ++i) { const r = renderables[i]; - if ((!r.state.opaque && r.values.alpha.ref.value > outlineAlphaThreshold) || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) { + if (!r.state.opaque || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) { renderObject(r, 'depth', Flag.None); } } @@ -830,9 +827,6 @@ namespace Renderer { instancedDrawCount: stats.instancedDrawCount, }; }, - setOutlineAlphaThreshold: (value: number) => { - outlineAlphaThreshold = value; - }, dispose: () => { // TODO } diff --git a/src/mol-gl/shader/outlines.frag.ts b/src/mol-gl/shader/outlines.frag.ts index e2cb6ec22..6c55edc06 100644 --- a/src/mol-gl/shader/outlines.frag.ts +++ b/src/mol-gl/shader/outlines.frag.ts @@ -38,7 +38,11 @@ float getDepthOpaque(const in vec2 coords) { } float getDepthTransparent(const in vec2 coords) { - return unpackRGBAToDepth(texture2D(tDepthTransparent, coords)); + #ifdef dTransparentOutline + return unpackRGBAToDepth(texture2D(tDepthTransparent, coords)); + #else + return 1.0; + #endif } bool isBackground(const in float depth) { diff --git a/src/mol-gl/shader/postprocessing.frag.ts b/src/mol-gl/shader/postprocessing.frag.ts index df1acfad5..db622f416 100644 --- a/src/mol-gl/shader/postprocessing.frag.ts +++ b/src/mol-gl/shader/postprocessing.frag.ts @@ -50,7 +50,11 @@ float getDepthOpaque(const in vec2 coords) { } float getDepthTransparent(const in vec2 coords) { - return unpackRGBAToDepth(texture2D(tDepthTransparent, coords)); + #ifdef dTransparentOutline + return unpackRGBAToDepth(texture2D(tDepthTransparent, coords)); + #else + return 1.0; + #endif } bool isBackground(const in float depth) { diff --git a/src/mol-plugin-ui/structure/quick-styles.tsx b/src/mol-plugin-ui/structure/quick-styles.tsx index a0c535acf..49bbb19a1 100644 --- a/src/mol-plugin-ui/structure/quick-styles.tsx +++ b/src/mol-plugin-ui/structure/quick-styles.tsx @@ -56,7 +56,7 @@ export class QuickStyles extends PurePluginUIComponent { postprocessing: { outline: { name: 'on', - params: { scale: 1, color: Color(0x000000), threshold: 0.25 } + params: { scale: 1, color: Color(0x000000), threshold: 0.25, includeTransparent: true } }, occlusion: { name: 'on', @@ -78,7 +78,7 @@ export class QuickStyles extends PurePluginUIComponent { name: 'on', params: pp.outline.name === 'on' ? pp.outline.params - : { scale: 1, color: Color(0x000000), threshold: 0.33 } + : { scale: 1, color: Color(0x000000), threshold: 0.33, includeTransparent: true } }, occlusion: { name: 'on', -- GitLab