diff --git a/CHANGELOG.md b/CHANGELOG.md index a57fcc8af8a39c345923ec40381ad7bf9823036f..8c92d6aa847ea0c626ca7e49a0ce603c1ca7fee3 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 ba17705d7c67d751e744a3f70be36695d6087269..7752742553bde109926043e12a996247a6dde2e1 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 b6ea8d5b8b14daad28ab35dba75f6e8557bdec59..52c36b1e0e1bbd53ef24e579cb2095fb86e0388f 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 9b3def951a34d227d9211ed14fa835b9aefc5fde..f02fc18f962e49e9fe6337a8eeb36073218aba1b 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 891a04f748b1dd888fcb6497ed6aa439e7dfe58d..70a3daf72268f529bb6e57245e1cbd2aceb0d983 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