ソースを参照

improve impostor shaders

- fix sphere near-clipping with orthographic projection
- fix cylinder near-clipping
- add interior cylinder caps
Alexander Rose 2 年 前
コミット
601bd5ba7a

+ 4 - 0
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
 

+ 46 - 19
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

+ 4 - 1
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
 }
 `;

+ 24 - 28
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
 

+ 4 - 2
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
 }
 `;