diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d82d55ea5a87a810b9de5e850b70cae40ab66fa..e3de459f2e6d705653c9f7b88e289565859242c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,14 @@ Note that since we don't clearly distinguish between a public and private interf ## [Unreleased] +- Add an `includeTransparent` parameter to hide/show outlines of components that are transparent - Fix 'once' for animations of systems with many frames - Better guard against issue (black fringes) with bumpiness in impostors - Improve impostor shaders - Fix sphere near-clipping with orthographic projection - Fix cylinder near-clipping - Add interior cylinder caps + - Add per-pixel object clipping - Fix `QualityAssessment` assignment bug for structures with different auth vs label sequence numbering ## [v3.26.0] - 2022-12-04 diff --git a/src/apps/docking-viewer/viewport.tsx b/src/apps/docking-viewer/viewport.tsx index ba5ce4598a9920a917064968ad97f754706cad07..d1c4ff85ab2c940349d9a7dcbec2fa3a8f0d113c 100644 --- a/src/apps/docking-viewer/viewport.tsx +++ b/src/apps/docking-viewer/viewport.tsx @@ -55,6 +55,7 @@ function occlusionStyle(plugin: PluginContext) { scale: 1.0, threshold: 0.33, color: Color(0x0000), + includeTransparent: true, } }, shadow: { name: 'off', params: {} }, } diff --git a/src/examples/lighting/index.ts b/src/examples/lighting/index.ts index a71bff56aa4c0d39b8c03f6317bd7864b7b0485a..0cc1048ef7843c78080a383bd8927ea3ba88a2a4 100644 --- a/src/examples/lighting/index.ts +++ b/src/examples/lighting/index.ts @@ -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, } }, shadow: { name: 'off', params: {} }, }, renderer: { diff --git a/src/extensions/cellpack/model.ts b/src/extensions/cellpack/model.ts index 9c62408af02efabdaaa3bf78a4b9e058892bfa9b..91ea9c33315343732112fe22b80829545b5edc90 100644 --- a/src/extensions/cellpack/model.ts +++ b/src/extensions/cellpack/model.ts @@ -621,6 +621,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 1f6c5549bc2169e74b09f6246446930781db242b..2f8942215ebd170c0ea13252f7550a79b9ba27fb 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -142,7 +142,7 @@ export class DrawPass { } if (PostprocessingPass.isEnabled(postprocessingProps)) { - if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { + if (PostprocessingPass.isTransparentOutlineEnabled(postprocessingProps)) { this.depthTargetTransparent.bind(); renderer.clearDepth(true); if (scene.opacityAverage < 1) { @@ -196,7 +196,7 @@ export class DrawPass { } if (PostprocessingPass.isEnabled(postprocessingProps)) { - if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { + if (PostprocessingPass.isTransparentOutlineEnabled(postprocessingProps)) { this.depthTargetTransparent.bind(); renderer.clearDepth(true); if (scene.opacityAverage < 1) { @@ -260,7 +260,7 @@ export class DrawPass { this.colorTarget.depthRenderbuffer?.detachFramebuffer(this.postprocessing.target.framebuffer); } - if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { + if (PostprocessingPass.isTransparentOutlineEnabled(postprocessingProps)) { this.depthTargetTransparent.bind(); renderer.clearDepth(true); if (scene.opacityAverage < 1) { diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index b963c66d44ce977a443d0b89601fcabbf26f8f63..1a1401642b2a14055e068ea86d5780d962a832ef 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/src/mol-canvas3d/passes/postprocessing.ts @@ -45,10 +45,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(); @@ -63,6 +65,8 @@ function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, d uFar: ValueCell.create(10000), uMaxPossibleViewZDiff: ValueCell.create(0.5), + + dTransparentOutline: ValueCell.create(transparentOutline), }; const schema = { ...OutlinesSchema }; @@ -288,10 +292,13 @@ 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, shadowsTexture: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable { + +function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, shadowsTexture: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture, transparentOutline: boolean): PostprocessingRenderable { const values: Values<typeof PostprocessingSchema> = { ...QuadValues, tSsaoDepth: ValueCell.create(ssaoDepthTexture), @@ -321,6 +328,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 }; @@ -355,6 +364,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)), + includeTransparent: PD.Boolean(true, { description: 'Whether to show outline for transparent objects' }), }), off: PD.Group({}) }, { cycle: true, description: 'Draw outline around 3D objects' }), @@ -373,8 +383,8 @@ export class PostprocessingPass { return props.occlusion.name === 'on' || props.shadow.name === 'on' || props.outline.name === 'on' || props.background.variant.name !== 'off'; } - static isOutlineEnabled(props: PostprocessingProps) { - return props.outline.name === 'on'; + static isTransparentOutlineEnabled(props: PostprocessingProps) { + return props.outline.name === 'on' && props.outline.params.includeTransparent; } readonly target: RenderTarget; @@ -428,7 +438,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.shadowsTarget = webgl.createRenderTarget(width, height, false); this.shadowsRenderable = getShadowsRenderable(webgl, depthTextureOpaque); @@ -456,7 +466,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.shadowsTarget.texture, this.outlinesTarget.texture, this.ssaoDepthTexture); + this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.shadowsTarget.texture, this.outlinesTarget.texture, this.ssaoDepthTexture, true); this.background = new BackgroundPass(webgl, assetManager, width, height); } @@ -494,6 +504,7 @@ export class PostprocessingPass { let needsUpdateMain = false; let needsUpdateSsao = false; let needsUpdateSsaoBlur = false; + let needsUpdateOutlines = false; const orthographic = camera.state.mode === 'orthographic' ? 1 : 0; const outlinesEnabled = props.outline.name === 'on'; @@ -615,7 +626,8 @@ 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; @@ -626,12 +638,16 @@ export class PostprocessingPass { 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) { needsUpdateOutlines = 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); @@ -650,6 +666,10 @@ export class PostprocessingPass { if (this.renderable.values.dOcclusionEnable.ref.value !== occlusionEnabled) { needsUpdateMain = true; } ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, occlusionEnabled); + if (needsUpdateOutlines) { + this.outlinesRenderable.update(); + } + if (needsUpdateShadows) { this.shadowsRenderable.update(); } diff --git a/src/mol-gl/shader/chunks/clip-instance.glsl.ts b/src/mol-gl/shader/chunks/clip-instance.glsl.ts index 8ea189e67d496c48338d12a6e61096c87ad38be5..644a5f6cc4118b35165bbf444ef14c5e1431da2f 100644 --- a/src/mol-gl/shader/chunks/clip-instance.glsl.ts +++ b/src/mol-gl/shader/chunks/clip-instance.glsl.ts @@ -1,12 +1,7 @@ export const clip_instance = ` #if defined(dClipVariant_instance) && dClipObjectCount != 0 - int flag = 0; - #if defined(dClipping) - flag = int(floor(vClipping * 255.0 + 0.5)); - #endif - vec4 mCenter = uModel * aTransform * vec4(uInvariantBoundingSphere.xyz, 1.0); - if (clipTest(vec4(mCenter.xyz, uInvariantBoundingSphere.w), flag)) + if (clipTest(vec4(mCenter.xyz, uInvariantBoundingSphere.w))) // move out of [ -w, +w ] to 'discard' in vert shader gl_Position.z = 2.0 * gl_Position.w; #endif diff --git a/src/mol-gl/shader/chunks/clip-pixel.glsl.ts b/src/mol-gl/shader/chunks/clip-pixel.glsl.ts index dbec0f552ff752b95f7cb66f487cace69f104c22..84d90332819318dffe0dc74576459a6c2da57db4 100644 --- a/src/mol-gl/shader/chunks/clip-pixel.glsl.ts +++ b/src/mol-gl/shader/chunks/clip-pixel.glsl.ts @@ -1,12 +1,6 @@ export const clip_pixel = ` #if defined(dClipVariant_pixel) && dClipObjectCount != 0 - #if defined(dClipping) - int clippingFlag = int(floor(vClipping * 255.0 + 0.5)); - #else - int clippingFlag = 0; - #endif - - if (clipTest(vec4(vModelPosition, 0.0), clippingFlag)) + if (clipTest(vec4(vModelPosition, 0.0))) discard; #endif `; \ No newline at end of file diff --git a/src/mol-gl/shader/chunks/common-clip.glsl.ts b/src/mol-gl/shader/chunks/common-clip.glsl.ts index 5bca9dec16e2dd3069af86b7316f00813fda279e..31e759128efcf77973899e89319e80658540f614 100644 --- a/src/mol-gl/shader/chunks/common-clip.glsl.ts +++ b/src/mol-gl/shader/chunks/common-clip.glsl.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Ludovic Autin <autin@scripps.edu> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -7,45 +7,45 @@ export const common_clip = ` #if dClipObjectCount != 0 - vec3 quaternionTransform(vec4 q, vec3 v) { + vec3 quaternionTransform(const in vec4 q, const in vec3 v) { vec3 t = 2.0 * cross(q.xyz, v); return v + q.w * t + cross(q.xyz, t); } - vec4 computePlane(vec3 normal, vec3 inPoint) { + vec4 computePlane(const in vec3 normal, const in vec3 inPoint) { return vec4(normalize(normal), -dot(normal, inPoint)); } - float planeSD(vec4 plane, vec3 center) { + float planeSD(const in vec4 plane, const in vec3 center) { return -dot(plane.xyz, center - plane.xyz * -plane.w); } - float sphereSD(vec3 position, vec4 rotation, vec3 size, vec3 center) { + float sphereSD(const in vec3 position, const in vec4 rotation, const in vec3 size, const in vec3 center) { return ( length(quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position) / size) - 1.0 ) * min(min(size.x, size.y), size.z); } - float cubeSD(vec3 position, vec4 rotation, vec3 size, vec3 center) { + float cubeSD(const in vec3 position, const in vec4 rotation, const in vec3 size, const in vec3 center) { vec3 d = abs(quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position)) - size; return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0)); } - float cylinderSD(vec3 position, vec4 rotation, vec3 size, vec3 center) { + float cylinderSD(const in vec3 position, const in vec4 rotation, const in vec3 size, const in vec3 center) { vec3 t = quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position); vec2 d = abs(vec2(length(t.xz), t.y)) - size.xy; return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)); } - float infiniteConeSD(vec3 position, vec4 rotation, vec3 size, vec3 center) { + float infiniteConeSD(const in vec3 position, const in vec4 rotation, const in vec3 size, const in vec3 center) { vec3 t = quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position); float q = length(t.xy); return dot(size.xy, vec2(q, t.z)); } - float getSignedDistance(vec3 center, int type, vec3 position, vec4 rotation, vec3 scale) { + float getSignedDistance(const in vec3 center, const in int type, const in vec3 position, const in vec4 rotation, const in vec3 scale) { if (type == 1) { vec3 normal = quaternionTransform(rotation, vec3(0.0, 1.0, 0.0)); vec4 plane = computePlane(normal, position); @@ -65,7 +65,7 @@ export const common_clip = ` #if __VERSION__ == 100 // 8-bit - int bitwiseAnd(int a, int b) { + int bitwiseAnd(const in int a, const in int b) { int d = 128; int result = 0; for (int i = 0; i < 8; ++i) { @@ -78,17 +78,23 @@ export const common_clip = ` return result; } - bool hasBit(int mask, int bit) { + bool hasBit(const in int mask, const in int bit) { return bitwiseAnd(mask, bit) == 0; } #else - bool hasBit(int mask, int bit) { + bool hasBit(const in int mask, const in int bit) { return (mask & bit) == 0; } #endif - // flag is a bit-flag for clip-objects to ignore (note, object ids start at 1 not 0) - bool clipTest(vec4 sphere, int flag) { + bool clipTest(const in vec4 sphere) { + // flag is a bit-flag for clip-objects to ignore (note, object ids start at 1 not 0) + #if defined(dClipping) + int flag = int(floor(vClipping * 255.0 + 0.5)); + #else + int flag = 0; + #endif + #pragma unroll_loop_start for (int i = 0; i < dClipObjectCount; ++i) { if (flag == 0 || hasBit(flag, UNROLLED_LOOP_INDEX + 1)) { diff --git a/src/mol-gl/shader/cylinders.frag.ts b/src/mol-gl/shader/cylinders.frag.ts index 7752742553bde109926043e12a996247a6dde2e1..f41e64f80059d986c5c7312076d5c8d62ee24f9c 100644 --- a/src/mol-gl/shader/cylinders.frag.ts +++ b/src/mol-gl/shader/cylinders.frag.ts @@ -34,7 +34,7 @@ bool CylinderImpostor( in vec3 rayOrigin, in vec3 rayDir, in vec3 start, in vec3 end, in float radius, out vec3 cameraNormal, out bool interior, - out vec3 viewPosition, out float fragmentDepth + out vec3 modelPosition, out vec3 viewPosition, out float fragmentDepth ){ vec3 ba = end - start; vec3 oc = rayOrigin - start; @@ -60,8 +60,12 @@ bool CylinderImpostor( if (y > 0.0 && y < baba) { interior = false; cameraNormal = (oc + t * rayDir - ba * y / baba) / radius; - viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + modelPosition = rayOrigin + t * rayDir; + viewPosition = (uView * vec4(modelPosition, 1.0)).xyz; fragmentDepth = calcDepth(viewPosition); + #if defined(dClipVariant_pixel) && dClipObjectCount != 0 + if (clipTest(vec4(modelPosition, 0.0))) fragmentDepth = -1.0; + #endif if (fragmentDepth > 0.0) return true; } @@ -71,7 +75,8 @@ bool CylinderImpostor( if (abs(k1 + k2 * t) < h) { interior = false; cameraNormal = -ba / baba; - viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + modelPosition = rayOrigin + t * rayDir; + viewPosition = (uView * vec4(modelPosition, 1.0)).xyz; fragmentDepth = calcDepth(viewPosition); if (fragmentDepth > 0.0) return true; } @@ -81,7 +86,8 @@ bool CylinderImpostor( if (abs(k1 + k2 * t) < h) { interior = false; cameraNormal = ba / baba; - viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + modelPosition = rayOrigin + t * rayDir; + viewPosition = (uView * vec4(modelPosition, 1.0)).xyz; fragmentDepth = calcDepth(viewPosition); if (fragmentDepth > 0.0) return true; } @@ -95,7 +101,8 @@ bool CylinderImpostor( if (y > 0.0 && y < baba) { interior = true; cameraNormal = -(oc + t * rayDir - ba * y / baba) / radius; - viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + modelPosition = rayOrigin + t * rayDir; + viewPosition = (uView * vec4(modelPosition, 1.0)).xyz; fragmentDepth = calcDepth(viewPosition); return true; } @@ -106,7 +113,8 @@ bool CylinderImpostor( if (abs(k1 + k2 * t) < -h) { interior = true; cameraNormal = ba / baba; - viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + modelPosition = rayOrigin + t * rayDir; + viewPosition = (uView * vec4(modelPosition, 1.0)).xyz; fragmentDepth = calcDepth(viewPosition); if (fragmentDepth > 0.0) return true; } @@ -116,7 +124,8 @@ bool CylinderImpostor( if (abs(k1 + k2 * t) < -h) { interior = true; cameraNormal = -ba / baba; - viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + modelPosition = rayOrigin + t * rayDir; + viewPosition = (uView * vec4(modelPosition, 1.0)).xyz; fragmentDepth = calcDepth(viewPosition); if (fragmentDepth > 0.0) return true; } @@ -127,15 +136,14 @@ bool CylinderImpostor( } void main() { - #include clip_pixel - vec3 rayOrigin = vModelPosition; vec3 rayDir = mix(normalize(vModelPosition - uCameraPosition), uCameraDir, uIsOrtho); vec3 cameraNormal; + vec3 modelPosition; vec3 viewPosition; float fragmentDepth; - bool hit = CylinderImpostor(rayOrigin, rayDir, vStart, vEnd, vSize, cameraNormal, interior, viewPosition, fragmentDepth); + bool hit = CylinderImpostor(rayOrigin, rayDir, vStart, vEnd, vSize, cameraNormal, interior, modelPosition, viewPosition, fragmentDepth); if (!hit) discard; if (fragmentDepth < 0.0) discard; @@ -143,7 +151,10 @@ void main() { gl_FragDepthEXT = fragmentDepth; - vec3 vModelPosition = (uInvView * vec4(viewPosition, 1.0)).xyz; + vec3 vViewPosition = viewPosition; + vec3 vModelPosition = modelPosition; + + #include clip_pixel #include assign_material_color #if defined(dRenderVariant_pick) diff --git a/src/mol-gl/shader/cylinders.vert.ts b/src/mol-gl/shader/cylinders.vert.ts index 52c36b1e0e1bbd53ef24e579cb2095fb86e0388f..ff28e5c1bdc061df4b144510702b59cba75cd687 100644 --- a/src/mol-gl/shader/cylinders.vert.ts +++ b/src/mol-gl/shader/cylinders.vert.ts @@ -55,7 +55,12 @@ void main() { vec3 camDir = -mix(normalize(vModelPosition - uCameraPosition), uCameraDir, uIsOrtho); vec3 dir = vEnd - vStart; // ensure cylinder 'dir' is pointing towards the camera - if(dot(camDir, dir) < 0.0) dir = -dir; + if(dot(camDir, dir) < 0.0) { + dir = -dir; + vec3 tmp = vStart; + vStart = vEnd; + vEnd = tmp; + } vec3 left = cross(camDir, dir); vec3 up = cross(left, dir); diff --git a/src/mol-gl/shader/direct-volume.frag.ts b/src/mol-gl/shader/direct-volume.frag.ts index f64cf9b3c7fa93f1604f9fa53ed6c24284465c34..ce61ae895fe13f54582e7b13008e026348e67392 100644 --- a/src/mol-gl/shader/direct-volume.frag.ts +++ b/src/mol-gl/shader/direct-volume.frag.ts @@ -229,7 +229,7 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) { #if defined(dClipVariant_pixel) && dClipObjectCount != 0 vec3 vModelPosition = v3m4(unitPos * uGridDim, modelTransform); - if (clipTest(vec4(vModelPosition, 0.0), 0)) { + if (clipTest(vec4(vModelPosition, 0.0))) { prevValue = value; pos += step; continue; diff --git a/src/mol-gl/shader/outlines.frag.ts b/src/mol-gl/shader/outlines.frag.ts index e2cb6ec226ee07fca037ae216842dd26da0289af..6c55edc06788f7471fe21d8fd3c3a3b92362913d 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 d41cb9449bea753a2ea3d4f35421acd938d9272d..80e9712225c6589c8e7a39eca283a7f075be7f02 100644 --- a/src/mol-gl/shader/postprocessing.frag.ts +++ b/src/mol-gl/shader/postprocessing.frag.ts @@ -51,7 +51,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/spheres.frag.ts b/src/mol-gl/shader/spheres.frag.ts index 55888215adb0bf946126f01777df352f04be21a0..7d5348d9563856df561e9ea7a965939e6302132b 100644 --- a/src/mol-gl/shader/spheres.frag.ts +++ b/src/mol-gl/shader/spheres.frag.ts @@ -23,7 +23,7 @@ varying float vRadiusSq; varying vec3 vPoint; varying vec3 vPointViewPosition; -bool SphereImpostor(out vec3 cameraPos, out vec3 cameraNormal, out bool interior, out float fragmentDepth){ +bool SphereImpostor(out vec3 modelPos, out vec3 cameraPos, out vec3 cameraNormal, out bool interior, out float fragmentDepth, out bool clipped){ vec3 cameraSpherePos = -vPointViewPosition; vec3 rayOrigin = mix(vec3(0.0, 0.0, 0.0), vPoint, uIsOrtho); @@ -40,14 +40,23 @@ bool SphereImpostor(out vec3 cameraPos, out vec3 cameraNormal, out bool interior float negT = mix(B - sqrtDet, B + sqrtDet, uIsOrtho); cameraPos = rayDirection * negT + rayOrigin; + modelPos = (uInvView * vec4(cameraPos, 1.0)).xyz; fragmentDepth = calcDepth(cameraPos); + #if defined(dClipVariant_pixel) && dClipObjectCount != 0 + if (clipTest(vec4(modelPos, 0.0))) { + clipped = true; + fragmentDepth = -1.0; + } + #endif + if (fragmentDepth > 0.0) { cameraNormal = normalize(cameraPos - cameraSpherePos); interior = false; return true; } else if (uDoubleSided) { cameraPos = rayDirection * posT + rayOrigin; + modelPos = (uInvView * vec4(cameraPos, 1.0)).xyz; fragmentDepth = calcDepth(cameraPos); cameraNormal = -normalize(cameraPos - cameraSpherePos); interior = true; @@ -58,25 +67,27 @@ bool SphereImpostor(out vec3 cameraPos, out vec3 cameraNormal, out bool interior } void main(void){ - #include clip_pixel - + vec3 modelPos; vec3 cameraPos; vec3 cameraNormal; float fragmentDepth; - bool hit = SphereImpostor(cameraPos, cameraNormal, interior, fragmentDepth); + bool clipped = false; + bool hit = SphereImpostor(modelPos, cameraPos, cameraNormal, interior, fragmentDepth, clipped); if (!hit) discard; if (fragmentDepth < 0.0) discard; if (fragmentDepth > 1.0) discard; - if (interior) { + vec3 vViewPosition = cameraPos; + vec3 vModelPosition = modelPos; + + if (interior && !clipped) { fragmentDepth = 0.0 + (0.0000001 / vRadius); } gl_FragDepthEXT = fragmentDepth; - vec3 vViewPosition = cameraPos; - vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz; + #include clip_pixel #include assign_material_color #if defined(dRenderVariant_pick) diff --git a/src/mol-plugin-ui/structure/quick-styles.tsx b/src/mol-plugin-ui/structure/quick-styles.tsx index ef3078b037793844447e996145b719dd4b85586b..27bda32a3ba4376210c13b00bc3345ef35bca922 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', @@ -79,7 +79,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',