diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index 4535331fef525a8c9734e257d997796ec1034097..e1c9ce4f85af804226c8f8336c6358169460e8f8 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/src/mol-canvas3d/passes/postprocessing.ts @@ -43,9 +43,9 @@ const OutlinesSchema = { dOrthographic: DefineSpec('number'), uNear: UniformSpec('f'), uFar: UniformSpec('f'), + uInvProjection: UniformSpec('m4'), - uMaxPossibleViewZDiff: UniformSpec('f'), - + uOutlineThreshold: UniformSpec('f'), dTransparentOutline: DefineSpec('boolean'), }; type OutlinesRenderable = ComputeRenderable<Values<typeof OutlinesSchema>> @@ -63,9 +63,9 @@ function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, d dOrthographic: ValueCell.create(0), uNear: ValueCell.create(1), uFar: ValueCell.create(10000), + uInvProjection: ValueCell.create(Mat4.identity()), - uMaxPossibleViewZDiff: ValueCell.create(0.5), - + uOutlineThreshold: ValueCell.create(0.33), dTransparentOutline: ValueCell.create(transparentOutline), }; @@ -189,8 +189,7 @@ const SsaoBlurSchema = { uBlurDirectionX: UniformSpec('f'), uBlurDirectionY: UniformSpec('f'), - uMaxPossibleViewZDiff: UniformSpec('f'), - + uInvProjection: UniformSpec('m4'), uNear: UniformSpec('f'), uFar: UniformSpec('f'), uBounds: UniformSpec('v4'), @@ -211,8 +210,7 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0), uBlurDirectionY: ValueCell.create(direction === 'vertical' ? 1 : 0), - uMaxPossibleViewZDiff: ValueCell.create(0.5), - + uInvProjection: ValueCell.create(Mat4.identity()), uNear: ValueCell.create(0.0), uFar: ValueCell.create(10000.0), uBounds: ValueCell.create(Vec4()), @@ -283,9 +281,6 @@ const PostprocessingSchema = { uOcclusionColor: UniformSpec('v3'), uTransparentBackground: UniformSpec('b'), - uMaxPossibleViewZDiff: UniformSpec('f'), - uInvProjection: UniformSpec('m4'), - dOcclusionEnable: DefineSpec('boolean'), uOcclusionOffset: UniformSpec('v2'), @@ -293,8 +288,6 @@ const PostprocessingSchema = { dOutlineEnable: DefineSpec('boolean'), dOutlineScale: DefineSpec('number'), - uOutlineThreshold: UniformSpec('f'), - dTransparentOutline: DefineSpec('boolean'), }; type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>> @@ -321,9 +314,6 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d uOcclusionColor: ValueCell.create(Vec3.create(0, 0, 0)), uTransparentBackground: ValueCell.create(false), - uMaxPossibleViewZDiff: ValueCell.create(0.5), - uInvProjection: ValueCell.create(Mat4.identity()), - dOcclusionEnable: ValueCell.create(true), uOcclusionOffset: ValueCell.create(Vec2.create(0, 0)), @@ -331,8 +321,6 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d dOutlineEnable: ValueCell.create(false), dOutlineScale: ValueCell.create(1), - uOutlineThreshold: ValueCell.create(0.33), - dTransparentOutline: ValueCell.create(transparentOutline), }; @@ -546,6 +534,9 @@ export class PostprocessingPass { ValueCell.updateIfChanged(this.ssaoBlurFirstPassRenderable.values.uFar, camera.far); ValueCell.updateIfChanged(this.ssaoBlurSecondPassRenderable.values.uFar, camera.far); + ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uInvProjection, invProjection); + ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uInvProjection, invProjection); + if (this.ssaoBlurFirstPassRenderable.values.dOrthographic.ref.value !== orthographic) { needsUpdateSsaoBlur = true; ValueCell.update(this.ssaoBlurFirstPassRenderable.values.dOrthographic, orthographic); @@ -638,17 +629,13 @@ export class PostprocessingPass { } if (props.outline.name === 'on') { - 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 / 10) / 1000; - // use radiusMax for stable outlines when zooming - const maxPossibleViewZDiff = factor * camera.state.radiusMax; + const transparentOutline = props.outline.params.includeTransparent ?? true; const outlineScale = props.outline.params.scale - 1; + const outlineThreshold = 50 * props.outline.params.threshold; ValueCell.updateIfChanged(this.outlinesRenderable.values.uNear, camera.near); ValueCell.updateIfChanged(this.outlinesRenderable.values.uFar, camera.far); + ValueCell.update(this.outlinesRenderable.values.uInvProjection, invProjection); if (this.outlinesRenderable.values.dTransparentOutline.ref.value !== transparentOutline) { needsUpdateOutlines = true; ValueCell.update(this.outlinesRenderable.values.dTransparentOutline, transparentOutline); @@ -657,12 +644,10 @@ export class PostprocessingPass { needsUpdateOutlines = true; ValueCell.update(this.outlinesRenderable.values.dOrthographic, orthographic); } + ValueCell.updateIfChanged(this.outlinesRenderable.values.uOutlineThreshold, outlineThreshold); 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); - ValueCell.update(this.renderable.values.uInvProjection, invProjection); - if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) { needsUpdateMain = true; ValueCell.update(this.renderable.values.dOutlineScale, outlineScale); diff --git a/src/mol-gl/shader/outlines.frag.ts b/src/mol-gl/shader/outlines.frag.ts index 443843945e03497943f3712e4bda90540ee3726e..55134888c0f59b15f38354ea1f2baca390c42ef4 100644 --- a/src/mol-gl/shader/outlines.frag.ts +++ b/src/mol-gl/shader/outlines.frag.ts @@ -16,8 +16,9 @@ uniform vec2 uTexSize; uniform float uNear; uniform float uFar; +uniform mat4 uInvProjection; -uniform float uMaxPossibleViewZDiff; +uniform float uOutlineThreshold; #include common @@ -49,17 +50,25 @@ bool isBackground(const in float depth) { return depth == 1.0; } +float getPixelSize(const in vec2 coords, const in float depth) { + vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection); + vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection); + return distance(viewPos0, viewPos1); +} + void main(void) { - float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff; + float backgroundViewZ = 2.0 * uFar; vec2 coords = gl_FragCoord.xy / uTexSize; vec2 invTexSize = 1.0 / uTexSize; float selfDepthOpaque = getDepthOpaque(coords); float selfViewZOpaque = isBackground(selfDepthOpaque) ? backgroundViewZ : getViewZ(selfDepthOpaque); + float pixelSizeOpaque = getPixelSize(coords, selfDepthOpaque) * uOutlineThreshold; float selfDepthTransparent = getDepthTransparent(coords); float selfViewZTransparent = isBackground(selfDepthTransparent) ? backgroundViewZ : getViewZ(selfDepthTransparent); + float pixelSizeTransparent = getPixelSize(coords, selfDepthTransparent) * uOutlineThreshold; float outline = 1.0; float bestDepth = 1.0; @@ -73,14 +82,14 @@ void main(void) { float sampleDepthTransparent = getDepthTransparent(sampleCoords); float sampleViewZOpaque = isBackground(sampleDepthOpaque) ? backgroundViewZ : getViewZ(sampleDepthOpaque); - if (abs(selfViewZOpaque - sampleViewZOpaque) > uMaxPossibleViewZDiff && selfDepthOpaque > sampleDepthOpaque && sampleDepthOpaque <= bestDepth) { + if (abs(selfViewZOpaque - sampleViewZOpaque) > pixelSizeOpaque && selfDepthOpaque > sampleDepthOpaque && sampleDepthOpaque <= bestDepth) { outline = 0.0; bestDepth = sampleDepthOpaque; } if (sampleDepthTransparent < sampleDepthOpaque) { float sampleViewZTransparent = isBackground(sampleDepthTransparent) ? backgroundViewZ : getViewZ(sampleDepthTransparent); - if (abs(selfViewZTransparent - sampleViewZTransparent) > uMaxPossibleViewZDiff && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestDepth) { + if (abs(selfViewZTransparent - sampleViewZTransparent) > pixelSizeTransparent && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestDepth) { outline = 0.0; bestDepth = sampleDepthTransparent; transparentFlag = 1.0; diff --git a/src/mol-gl/shader/postprocessing.frag.ts b/src/mol-gl/shader/postprocessing.frag.ts index 2226522c95444c353a9e10ab11972cd20a19bbc1..22d4a364217e3b762df94f85a3c75e635a249a2d 100644 --- a/src/mol-gl/shader/postprocessing.frag.ts +++ b/src/mol-gl/shader/postprocessing.frag.ts @@ -26,13 +26,8 @@ uniform vec3 uFogColor; uniform vec3 uOutlineColor; uniform vec3 uOcclusionColor; uniform bool uTransparentBackground; - uniform vec2 uOcclusionOffset; -uniform float uMaxPossibleViewZDiff; -uniform mat4 uInvProjection; - -const float outlineDistanceFactor = 5.0; #include common float getViewZ(const in float depth) { @@ -63,21 +58,14 @@ bool isBackground(const in float depth) { return depth == 1.0; } -float getPixelSize(const in vec2 coords, const in float depth) { - vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection); - vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection); - return distance(viewPos0, viewPos1); -} - float getOutline(const in vec2 coords, const in float opaqueDepth, out float closestTexel) { - float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff; + float backgroundViewZ = 2.0 * uFar; vec2 invTexSize = 1.0 / uTexSize; float transparentDepth = getDepthTransparent(coords); float opaqueSelfViewZ = isBackground(opaqueDepth) ? backgroundViewZ : getViewZ(opaqueDepth); float transparentSelfViewZ = isBackground(transparentDepth) ? backgroundViewZ : getViewZ(transparentDepth); float selfDepth = min(opaqueDepth, transparentDepth); - float pixelSize = getPixelSize(coords, selfDepth); float outline = 1.0; closestTexel = 1.0; @@ -95,7 +83,7 @@ float getOutline(const in vec2 coords, const in float opaqueDepth, out float clo float sampleOutlineViewZ = isBackground(sampleOutlineDepth) ? backgroundViewZ : getViewZ(sampleOutlineDepth); float selfViewZ = sampleOutlineCombined.a == 0.0 ? opaqueSelfViewZ : transparentSelfViewZ; - if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineViewZ) > uMaxPossibleViewZDiff + (pixelSize * outlineDistanceFactor)) { + if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel) { outline = 0.0; closestTexel = sampleOutlineDepth; } diff --git a/src/mol-gl/shader/ssao-blur.frag.ts b/src/mol-gl/shader/ssao-blur.frag.ts index efa894dc21f8d9e4f490424f1bb5afd6e9864a57..11429476796ef2f0064b498c72e92c02475049eb 100644 --- a/src/mol-gl/shader/ssao-blur.frag.ts +++ b/src/mol-gl/shader/ssao-blur.frag.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Ăron Samuel Kovács <aron.kovacs@mail.muni.cz> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -19,8 +19,7 @@ uniform float uKernel[dOcclusionKernelSize]; uniform float uBlurDirectionX; uniform float uBlurDirectionY; -uniform float uMaxPossibleViewZDiff; - +uniform mat4 uInvProjection; uniform float uNear; uniform float uFar; @@ -42,6 +41,12 @@ bool outsideBounds(const in vec2 p) { return p.x < uBounds.x || p.y < uBounds.y || p.x > uBounds.z || p.y > uBounds.w; } +float getPixelSize(const in vec2 coords, const in float depth) { + vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection); + vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection); + return distance(viewPos0, viewPos1); +} + void main(void) { vec2 coords = gl_FragCoord.xy / uTexSize; @@ -60,6 +65,8 @@ void main(void) { } float selfViewZ = getViewZ(selfDepth); + float pixelSize = getPixelSize(coords, selfDepth); + float maxDiffViewZ = pixelSize * 5.0; vec2 offset = vec2(uBlurDirectionX, uBlurDirectionY) / uTexSize; @@ -81,7 +88,7 @@ void main(void) { if (abs(float(i)) > 1.0) { // abs is not defined for int in webgl1 float sampleViewZ = getViewZ(sampleDepth); - if (abs(selfViewZ - sampleViewZ) > uMaxPossibleViewZDiff) { + if (abs(selfViewZ - sampleViewZ) > maxDiffViewZ) { continue; } }