Selaa lähdekoodia

Merge pull request #665 from molstar/solid-impostor

Solid impostors
Alexander Rose 2 vuotta sitten
vanhempi
commit
b7ec7ea686

+ 1 - 1
CHANGELOG.md

@@ -7,7 +7,7 @@ Note that since we don't clearly distinguish between a public and private interf
 ## [Unreleased]
 
 - Show histogram in direct volume control point settings
-
+- Add `solidInterior` parameter to sphere/cylinder impostors
 
 ## [v3.27.0] - 2022-12-15
 

+ 3 - 0
src/mol-geo/geometry/cylinders/cylinders.ts

@@ -158,6 +158,7 @@ export namespace Cylinders {
         ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
         xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
         transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
+        solidInterior: PD.Boolean(true, BaseGeometry.ShadingCategory),
         bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
         bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
     };
@@ -245,6 +246,7 @@ export namespace Cylinders {
             dIgnoreLight: ValueCell.create(props.ignoreLight),
             dXrayShaded: ValueCell.create(props.xrayShaded),
             dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
+            dSolidInterior: ValueCell.create(props.solidInterior),
             uBumpFrequency: ValueCell.create(props.bumpFrequency),
             uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
         };
@@ -263,6 +265,7 @@ export namespace Cylinders {
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
         ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
         ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
+        ValueCell.updateIfChanged(values.dSolidInterior, props.solidInterior);
         ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
         ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
     }

+ 3 - 0
src/mol-geo/geometry/spheres/spheres.ts

@@ -130,6 +130,7 @@ export namespace Spheres {
         ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
         xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
         transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
+        solidInterior: PD.Boolean(true, BaseGeometry.ShadingCategory),
         bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
         bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
     };
@@ -212,6 +213,7 @@ export namespace Spheres {
             dIgnoreLight: ValueCell.create(props.ignoreLight),
             dXrayShaded: ValueCell.create(props.xrayShaded),
             dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
+            dSolidInterior: ValueCell.create(props.solidInterior),
             uBumpFrequency: ValueCell.create(props.bumpFrequency),
             uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
         };
@@ -230,6 +232,7 @@ export namespace Spheres {
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
         ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
         ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
+        ValueCell.updateIfChanged(values.dSolidInterior, props.solidInterior);
         ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
         ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
     }

+ 1 - 0
src/mol-gl/renderable/cylinders.ts

@@ -27,6 +27,7 @@ export const CylindersSchema = {
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
     dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
+    dSolidInterior: DefineSpec('boolean'),
     uBumpFrequency: UniformSpec('f', 'material'),
     uBumpAmplitude: UniformSpec('f', 'material'),
 };

+ 1 - 0
src/mol-gl/renderable/spheres.ts

@@ -24,6 +24,7 @@ export const SpheresSchema = {
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
     dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
+    dSolidInterior: DefineSpec('boolean'),
     uBumpFrequency: UniformSpec('f', 'material'),
     uBumpAmplitude: UniformSpec('f', 'material'),
 };

+ 110 - 27
src/mol-gl/shader/cylinders.frag.ts

@@ -28,6 +28,12 @@ uniform mat4 uInvView;
 #include light_frag_params
 #include common_clip
 
+#ifdef dSolidInterior
+    const bool solidInterior = true;
+#else
+    const bool solidInterior = false;
+#endif
+
 // adapted from https://www.shadertoy.com/view/4lcSRn
 // The MIT License, Copyright 2016 Inigo Quilez
 bool CylinderImpostor(
@@ -53,6 +59,19 @@ bool CylinderImpostor(
     bool topCap = (vCap > 0.9 && vCap < 1.1) || vCap >= 2.9;
     bool bottomCap = (vCap > 1.9 && vCap < 2.1) || vCap >= 2.9;
 
+    #ifdef dSolidInterior
+        bool topInterior = !topCap;
+        bool bottomInterior = !bottomCap;
+        topCap = true;
+        bottomCap = true;
+    #else
+        bool topInterior = false;
+        bool bottomInterior = false;
+    #endif
+
+    bool clipped = false;
+    bool objectClipped = false;
+
     // body outside
     h = sqrt(h);
     float t = (-k1 - h) / k2;
@@ -64,36 +83,76 @@ bool CylinderImpostor(
         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;
+            if (clipTest(vec4(modelPosition, 0.0))) {
+                objectClipped = true;
+                fragmentDepth = -1.0;
+                #ifdef dSolidInterior
+                    topCap = !topInterior;
+                    bottomCap = !bottomInterior;
+                #endif
+            }
         #endif
         if (fragmentDepth > 0.0) return true;
+        clipped = true;
     }
 
-    if (topCap && y < 0.0) {
-        // top cap
-        t = -baoc / bard;
-        if (abs(k1 + k2 * t) < h) {
-            interior = false;
-            cameraNormal = -ba / baba;
-            modelPosition = rayOrigin + t * rayDir;
-            viewPosition = (uView * vec4(modelPosition, 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;
-            cameraNormal = ba / baba;
-            modelPosition = rayOrigin + t * rayDir;
-            viewPosition = (uView * vec4(modelPosition, 1.0)).xyz;
-            fragmentDepth = calcDepth(viewPosition);
-            if (fragmentDepth > 0.0) return true;
+    if (!clipped) {
+        if (topCap && y < 0.0) {
+            // top cap
+            t = -baoc / bard;
+            if (abs(k1 + k2 * t) < h) {
+                interior = topInterior;
+                cameraNormal = -ba / baba;
+                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))) {
+                        objectClipped = true;
+                        fragmentDepth = -1.0;
+                        #ifdef dSolidInterior
+                            topCap = !topInterior;
+                            bottomCap = !bottomInterior;
+                        #endif
+                    }
+                #endif
+                if (fragmentDepth > 0.0) {
+                    #ifdef dSolidInterior
+                        if (interior) cameraNormal = -rayDir;
+                    #endif
+                    return true;
+                }
+            }
+        } else if (bottomCap && y >= 0.0) {
+            // bottom cap
+            t = (baba - baoc) / bard;
+            if (abs(k1 + k2 * t) < h) {
+                interior = bottomInterior;
+                cameraNormal = ba / baba;
+                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))) {
+                        objectClipped = true;
+                        fragmentDepth = -1.0;
+                        #ifdef dSolidInterior
+                            topCap = !topInterior;
+                            bottomCap = !bottomInterior;
+                        #endif
+                    }
+                #endif
+                if (fragmentDepth > 0.0) {
+                    #ifdef dSolidInterior
+                        if (interior) cameraNormal = -rayDir;
+                    #endif
+                    return true;
+                }
+            }
         }
     }
 
-    if (uDoubleSided) {
+    if (uDoubleSided || solidInterior) {
         // body inside
         h = -h;
         t = (-k1 - h) / k2;
@@ -104,7 +163,15 @@ bool CylinderImpostor(
             modelPosition = rayOrigin + t * rayDir;
             viewPosition = (uView * vec4(modelPosition, 1.0)).xyz;
             fragmentDepth = calcDepth(viewPosition);
-            return true;
+            if (fragmentDepth > 0.0) {
+                #ifdef dSolidInterior
+                    if (!objectClipped) {
+                        fragmentDepth = 0.0 + (0.0000002 / vSize);
+                        cameraNormal = -rayDir;
+                    }
+                #endif
+                return true;
+            }
         }
 
         if (topCap && y < 0.0) {
@@ -116,9 +183,17 @@ bool CylinderImpostor(
                 modelPosition = rayOrigin + t * rayDir;
                 viewPosition = (uView * vec4(modelPosition, 1.0)).xyz;
                 fragmentDepth = calcDepth(viewPosition);
-                if (fragmentDepth > 0.0) return true;
+                if (fragmentDepth > 0.0) {
+                    #ifdef dSolidInterior
+                        if (!objectClipped) {
+                            fragmentDepth = 0.0 + (0.0000002 / vSize);
+                            cameraNormal = -rayDir;
+                        }
+                    #endif
+                    return true;
+                }
             }
-        } else if(bottomCap && y >= 0.0) {
+        } else if (bottomCap && y >= 0.0) {
             // bottom cap
             t = (baba - baoc) / bard;
             if (abs(k1 + k2 * t) < -h) {
@@ -127,7 +202,15 @@ bool CylinderImpostor(
                 modelPosition = rayOrigin + t * rayDir;
                 viewPosition = (uView * vec4(modelPosition, 1.0)).xyz;
                 fragmentDepth = calcDepth(viewPosition);
-                if (fragmentDepth > 0.0) return true;
+                if (fragmentDepth > 0.0) {
+                    #ifdef dSolidInterior
+                        if (!objectClipped) {
+                            fragmentDepth = 0.0 + (0.0000002 / vSize);
+                            cameraNormal = -rayDir;
+                        }
+                    #endif
+                    return true;
+                }
             }
         }
     }

+ 0 - 3
src/mol-gl/shader/cylinders.vert.ts

@@ -57,9 +57,6 @@ void main() {
     // ensure cylinder 'dir' is pointing towards the camera
     if(dot(camDir, dir) < 0.0) {
         dir = -dir;
-        vec3 tmp = vStart;
-        vStart = vEnd;
-        vEnd = tmp;
     }
 
     vec3 left = cross(camDir, dir);

+ 21 - 9
src/mol-gl/shader/spheres.frag.ts

@@ -23,7 +23,13 @@ varying float vRadiusSq;
 varying vec3 vPoint;
 varying vec3 vPointViewPosition;
 
-bool SphereImpostor(out vec3 modelPos, out vec3 cameraPos, out vec3 cameraNormal, out bool interior, out float fragmentDepth, out bool clipped){
+#ifdef dSolidInterior
+    const bool solidInterior = true;
+#else
+    const bool solidInterior = false;
+#endif
+
+bool SphereImpostor(out vec3 modelPos, out vec3 cameraPos, out vec3 cameraNormal, out bool interior, out float fragmentDepth){
     vec3 cameraSpherePos = -vPointViewPosition;
 
     vec3 rayOrigin = mix(vec3(0.0, 0.0, 0.0), vPoint, uIsOrtho);
@@ -43,9 +49,11 @@ bool SphereImpostor(out vec3 modelPos, out vec3 cameraPos, out vec3 cameraNormal
     modelPos = (uInvView * vec4(cameraPos, 1.0)).xyz;
     fragmentDepth = calcDepth(cameraPos);
 
+    bool objectClipped = false;
+
     #if defined(dClipVariant_pixel) && dClipObjectCount != 0
         if (clipTest(vec4(modelPos, 0.0))) {
-            clipped = true;
+            objectClipped = true;
             fragmentDepth = -1.0;
         }
     #endif
@@ -54,13 +62,21 @@ bool SphereImpostor(out vec3 modelPos, out vec3 cameraPos, out vec3 cameraNormal
         cameraNormal = normalize(cameraPos - cameraSpherePos);
         interior = false;
         return true;
-    } else if (uDoubleSided) {
+    } else if (uDoubleSided || solidInterior) {
         cameraPos = rayDirection * posT + rayOrigin;
         modelPos = (uInvView * vec4(cameraPos, 1.0)).xyz;
         fragmentDepth = calcDepth(cameraPos);
         cameraNormal = -normalize(cameraPos - cameraSpherePos);
         interior = true;
-        return true;
+        if (fragmentDepth > 0.0) {
+            #ifdef dSolidInterior
+                if (!objectClipped) {
+                    fragmentDepth = 0.0 + (0.0000001 / vRadius);
+                    cameraNormal = -mix(normalize(vPoint), vec3(0.0, 0.0, 1.0), uIsOrtho);
+                }
+            #endif
+            return true;
+        }
     }
 
     return false;
@@ -72,7 +88,7 @@ void main(void){
     vec3 cameraNormal;
     float fragmentDepth;
     bool clipped = false;
-    bool hit = SphereImpostor(modelPos, cameraPos, cameraNormal, interior, fragmentDepth, clipped);
+    bool hit = SphereImpostor(modelPos, cameraPos, cameraNormal, interior, fragmentDepth);
     if (!hit) discard;
 
     if (fragmentDepth < 0.0) discard;
@@ -81,10 +97,6 @@ void main(void){
     vec3 vViewPosition = cameraPos;
     vec3 vModelPosition = modelPos;
 
-    if (interior && !clipped) {
-        fragmentDepth = 0.0 + (0.0000001 / vRadius);
-    }
-
     gl_FragDepthEXT = fragmentDepth;
 
     #include clip_pixel