diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ebfd0619b603cf3ad97b725d1d7e6e8f1da67b4..a5fd7caf87b0cdbd60641b1151a16f695f667906 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/src/mol-geo/geometry/cylinders/cylinders.ts b/src/mol-geo/geometry/cylinders/cylinders.ts index 99929acd159c5d62398dd813a2892a28d5a7037f..41eadfd43cb4bb2c99baa2b03caa9e8441c499f1 100644 --- a/src/mol-geo/geometry/cylinders/cylinders.ts +++ b/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); } diff --git a/src/mol-geo/geometry/spheres/spheres.ts b/src/mol-geo/geometry/spheres/spheres.ts index b7be6f095ad44f85ee525fc7bd7af1c3f82c6a77..0b82bb7da1ccf980b1543cc212d7cf2f3de60947 100644 --- a/src/mol-geo/geometry/spheres/spheres.ts +++ b/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); } diff --git a/src/mol-gl/renderable/cylinders.ts b/src/mol-gl/renderable/cylinders.ts index db145d3edcf2eeff50f53b50d2eb07134fd8e5c4..cbe4fcd8b67031e723c0861e7335d8fc91066335 100644 --- a/src/mol-gl/renderable/cylinders.ts +++ b/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'), }; diff --git a/src/mol-gl/renderable/spheres.ts b/src/mol-gl/renderable/spheres.ts index 73c0e750754cb7617e34d23d32b2c9c6bf261012..a56b2a1f282e93f12c782c7c89ab305bec614b01 100644 --- a/src/mol-gl/renderable/spheres.ts +++ b/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'), }; diff --git a/src/mol-gl/shader/cylinders.frag.ts b/src/mol-gl/shader/cylinders.frag.ts index f41e64f80059d986c5c7312076d5c8d62ee24f9c..b820b770948e38fe5af005bf613e57172ccc3dd1 100644 --- a/src/mol-gl/shader/cylinders.frag.ts +++ b/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; + } } } } diff --git a/src/mol-gl/shader/cylinders.vert.ts b/src/mol-gl/shader/cylinders.vert.ts index ff28e5c1bdc061df4b144510702b59cba75cd687..60b3f19328854456f0fdfccfbd6b985d29f0b8c6 100644 --- a/src/mol-gl/shader/cylinders.vert.ts +++ b/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); diff --git a/src/mol-gl/shader/spheres.frag.ts b/src/mol-gl/shader/spheres.frag.ts index 7d5348d9563856df561e9ea7a965939e6302132b..7653acf7f331374a1c7971d30e1105b25f8a997e 100644 --- a/src/mol-gl/shader/spheres.frag.ts +++ b/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