From 601bd5ba7a634eb5fd6f28a28b4337e450a0f710 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Sat, 10 Dec 2022 21:47:16 -0800 Subject: [PATCH] improve impostor shaders - fix sphere near-clipping with orthographic projection - fix cylinder near-clipping - add interior cylinder caps --- CHANGELOG.md | 4 ++ src/mol-gl/shader/cylinders.frag.ts | 65 ++++++++++++++++++++--------- src/mol-gl/shader/cylinders.vert.ts | 5 ++- src/mol-gl/shader/spheres.frag.ts | 52 +++++++++++------------ src/mol-gl/shader/spheres.vert.ts | 6 ++- 5 files changed, 82 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a57fcc8af..8c92d6aa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Note that since we don't clearly distinguish between a public and private interf - 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 ## [v3.26.0] - 2022-12-04 diff --git a/src/mol-gl/shader/cylinders.frag.ts b/src/mol-gl/shader/cylinders.frag.ts index ba17705d7..775274255 100644 --- a/src/mol-gl/shader/cylinders.frag.ts +++ b/src/mol-gl/shader/cylinders.frag.ts @@ -33,7 +33,8 @@ uniform mat4 uInvView; bool CylinderImpostor( in vec3 rayOrigin, in vec3 rayDir, in vec3 start, in vec3 end, in float radius, - out vec4 intersection, out bool interior + out vec3 cameraNormal, out bool interior, + out vec3 viewPosition, out float fragmentDepth ){ vec3 ba = end - start; vec3 oc = rayOrigin - start; @@ -42,7 +43,7 @@ bool CylinderImpostor( float bard = dot(ba, rayDir); float baoc = dot(ba, oc); - float k2 = baba - bard*bard; + float k2 = baba - bard * bard; float k1 = baba * dot(oc, rayDir) - baoc * bard; float k0 = baba * dot(oc, oc) - baoc * baoc - radius * radius * baba; @@ -58,8 +59,10 @@ bool CylinderImpostor( float y = baoc + t * bard; if (y > 0.0 && y < baba) { interior = false; - intersection = vec4(t, (oc + t * rayDir - ba * y / baba) / radius); - return true; + cameraNormal = (oc + t * rayDir - ba * y / baba) / radius; + viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + fragmentDepth = calcDepth(viewPosition); + if (fragmentDepth > 0.0) return true; } if (topCap && y < 0.0) { @@ -67,16 +70,20 @@ bool CylinderImpostor( t = -baoc / bard; if (abs(k1 + k2 * t) < h) { interior = false; - intersection = vec4(t, ba * sign(y) / baba); - return true; + cameraNormal = -ba / baba; + viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + fragmentDepth = calcDepth(viewPosition); + if (fragmentDepth > 0.0) return true; } } else if(bottomCap && y >= 0.0) { // bottom cap t = (baba - baoc) / bard; if (abs(k1 + k2 * t) < h) { interior = false; - intersection = vec4(t, ba * sign(y) / baba); - return true; + cameraNormal = ba / baba; + viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + fragmentDepth = calcDepth(viewPosition); + if (fragmentDepth > 0.0) return true; } } @@ -87,11 +94,33 @@ bool CylinderImpostor( y = baoc + t * bard; if (y > 0.0 && y < baba) { interior = true; - intersection = vec4(t, (oc + t * rayDir - ba * y / baba) / radius); + cameraNormal = -(oc + t * rayDir - ba * y / baba) / radius; + viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + fragmentDepth = calcDepth(viewPosition); return true; } - // TODO: handle inside caps??? + if (topCap && y < 0.0) { + // top cap + t = -baoc / bard; + if (abs(k1 + k2 * t) < -h) { + interior = true; + cameraNormal = ba / baba; + viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + fragmentDepth = calcDepth(viewPosition); + if (fragmentDepth > 0.0) return true; + } + } else if(bottomCap && y >= 0.0) { + // bottom cap + t = (baba - baoc) / bard; + if (abs(k1 + k2 * t) < -h) { + interior = true; + cameraNormal = -ba / baba; + viewPosition = (uView * vec4(rayOrigin + t * rayDir, 1.0)).xyz; + fragmentDepth = calcDepth(viewPosition); + if (fragmentDepth > 0.0) return true; + } + } } return false; @@ -100,23 +129,21 @@ bool CylinderImpostor( void main() { #include clip_pixel + vec3 rayOrigin = vModelPosition; vec3 rayDir = mix(normalize(vModelPosition - uCameraPosition), uCameraDir, uIsOrtho); - vec4 intersection; - bool interior; - bool hit = CylinderImpostor(vModelPosition, rayDir, vStart, vEnd, vSize, intersection, interior); + vec3 cameraNormal; + vec3 viewPosition; + float fragmentDepth; + bool hit = CylinderImpostor(rayOrigin, rayDir, vStart, vEnd, vSize, cameraNormal, interior, viewPosition, fragmentDepth); if (!hit) discard; - vec3 vViewPosition = vModelPosition + intersection.x * rayDir; - vViewPosition = (uView * vec4(vViewPosition, 1.0)).xyz; - float fragmentDepth = calcDepth(vViewPosition); - if (fragmentDepth < 0.0) discard; if (fragmentDepth > 1.0) discard; gl_FragDepthEXT = fragmentDepth; - vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz; + vec3 vModelPosition = (uInvView * vec4(viewPosition, 1.0)).xyz; #include assign_material_color #if defined(dRenderVariant_pick) @@ -135,7 +162,7 @@ void main() { gl_FragColor = material; #elif defined(dRenderVariant_color) mat3 normalMatrix = transpose3(inverse3(mat3(uView))); - vec3 normal = normalize(normalMatrix * -normalize(intersection.yzw)); + vec3 normal = normalize(normalMatrix * -normalize(cameraNormal)); #include apply_light_color #include apply_interior_color diff --git a/src/mol-gl/shader/cylinders.vert.ts b/src/mol-gl/shader/cylinders.vert.ts index b6ea8d5b8..52c36b1e0 100644 --- a/src/mol-gl/shader/cylinders.vert.ts +++ b/src/mol-gl/shader/cylinders.vert.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 Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -69,6 +69,9 @@ void main() { vViewPosition = mvPosition.xyz; gl_Position = uProjection * mvPosition; + mvPosition.z -= 2.0 * (length(vEnd - vStart) + vSize); // avoid clipping + gl_Position.z = (uProjection * mvPosition).z; + #include clip_instance } `; \ No newline at end of file diff --git a/src/mol-gl/shader/spheres.frag.ts b/src/mol-gl/shader/spheres.frag.ts index 9b3def951..f02fc18f9 100644 --- a/src/mol-gl/shader/spheres.frag.ts +++ b/src/mol-gl/shader/spheres.frag.ts @@ -23,12 +23,8 @@ varying float vRadiusSq; varying vec3 vPoint; varying vec3 vPointViewPosition; -vec3 cameraPos; -vec3 cameraNormal; - -bool Impostor(out vec3 cameraPos, out vec3 cameraNormal){ +bool SphereImpostor(out vec3 cameraPos, out vec3 cameraNormal, out bool interior, out float fragmentDepth){ vec3 cameraSpherePos = -vPointViewPosition; - cameraSpherePos.z += vRadius; vec3 rayOrigin = mix(vec3(0.0, 0.0, 0.0), vPoint, uIsOrtho); vec3 rayDirection = mix(normalize(vPoint), vec3(0.0, 0.0, 1.0), uIsOrtho); @@ -37,49 +33,49 @@ bool Impostor(out vec3 cameraPos, out vec3 cameraNormal){ float B = dot(rayDirection, cameraSphereDir); float det = B * B + vRadiusSq - dot(cameraSphereDir, cameraSphereDir); - if (det < 0.0){ - discard; - return false; - } + if (det < 0.0) return false; float sqrtDet = sqrt(det); - float posT = mix(B + sqrtDet, B + sqrtDet, uIsOrtho); - float negT = mix(B - sqrtDet, sqrtDet - B, uIsOrtho); + float posT = mix(B + sqrtDet, B - sqrtDet, uIsOrtho); + float negT = mix(B - sqrtDet, B + sqrtDet, uIsOrtho); cameraPos = rayDirection * negT + rayOrigin; + fragmentDepth = calcDepth(cameraPos); - if (calcDepth(cameraPos) <= 0.0) { + if (fragmentDepth > 0.0) { + cameraNormal = normalize(cameraPos - cameraSpherePos); + interior = false; + return true; + } else if (uDoubleSided) { cameraPos = rayDirection * posT + rayOrigin; + fragmentDepth = calcDepth(cameraPos); + cameraNormal = -normalize(cameraPos - cameraSpherePos); interior = true; - } else { - interior = false; + return true; } - cameraNormal = normalize(cameraPos - cameraSpherePos); - cameraNormal *= float(!interior) * 2.0 - 1.0; - - return !interior; + return false; } void main(void){ #include clip_pixel - bool flag = Impostor(cameraPos, cameraNormal); - if (!uDoubleSided) { - if (interior) discard; - } - - vec3 vViewPosition = cameraPos; - float fragmentDepth = calcDepth(vViewPosition); - if (!flag && fragmentDepth >= 0.0) { - fragmentDepth = 0.0 + (0.0000001 / vRadius); - } + vec3 cameraPos; + vec3 cameraNormal; + float fragmentDepth; + bool hit = SphereImpostor(cameraPos, cameraNormal, interior, fragmentDepth); + if (!hit) discard; if (fragmentDepth < 0.0) discard; if (fragmentDepth > 1.0) discard; + if (interior && fragmentDepth >= 0.0) { + fragmentDepth = 0.0 + (0.0000001 / vRadius); + } + gl_FragDepthEXT = fragmentDepth; + vec3 vViewPosition = cameraPos; vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz; #include assign_material_color diff --git a/src/mol-gl/shader/spheres.vert.ts b/src/mol-gl/shader/spheres.vert.ts index 891a04f74..70a3daf72 100644 --- a/src/mol-gl/shader/spheres.vert.ts +++ b/src/mol-gl/shader/spheres.vert.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019-2020 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> */ @@ -85,7 +85,6 @@ void main(void){ vec4 position4 = vec4(aPosition, 1.0); vec4 mvPosition = uModelView * aTransform * position4; - mvPosition.z -= vRadius; // avoid clipping, added again in fragment shader gl_Position = uProjection * vec4(mvPosition.xyz, 1.0); quadraticProjection(size, aPosition); @@ -97,6 +96,9 @@ void main(void){ vModelPosition = (uModel * aTransform * position4).xyz; // for clipping in frag shader + mvPosition.z -= 2.0 * vRadius; // avoid clipping + gl_Position.z = (uProjection * vec4(mvPosition.xyz, 1.0)).z; + #include clip_instance } `; \ No newline at end of file -- GitLab