Browse Source

single and multi layer screen-door transparency

Alexander Rose 6 years ago
parent
commit
3a5a12af66

+ 6 - 1
src/mol-geo/geometry/transparency-data.ts

@@ -7,11 +7,13 @@
 import { ValueCell } from 'mol-util/value-cell'
 import { Vec2 } from 'mol-math/linear-algebra'
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
+import { Transparency } from 'mol-theme/transparency';
 
 export type TransparencyData = {
     tTransparency: ValueCell<TextureImage<Uint8Array>>
     uTransparencyTexDim: ValueCell<Vec2>
     dTransparency: ValueCell<boolean>,
+    dTransparencyVariant: ValueCell<string>,
 }
 
 export function applyTransparencyValue(array: Uint8Array, start: number, end: number, value: number) {
@@ -25,18 +27,20 @@ export function clearTransparency(array: Uint8Array, start: number, end: number)
     array.fill(0, start, end)
 }
 
-export function createTransparency(count: number, transparencyData?: TransparencyData): TransparencyData {
+export function createTransparency(count: number, variant: Transparency.Variant, transparencyData?: TransparencyData): TransparencyData {
     const transparency = createTextureImage(Math.max(1, count), 1, transparencyData && transparencyData.tTransparency.ref.value.array)
     if (transparencyData) {
         ValueCell.update(transparencyData.tTransparency, transparency)
         ValueCell.update(transparencyData.uTransparencyTexDim, Vec2.create(transparency.width, transparency.height))
         ValueCell.update(transparencyData.dTransparency, count > 0)
+        ValueCell.update(transparencyData.dTransparencyVariant, variant)
         return transparencyData
     } else {
         return {
             tTransparency: ValueCell.create(transparency),
             uTransparencyTexDim: ValueCell.create(Vec2.create(transparency.width, transparency.height)),
             dTransparency: ValueCell.create(count > 0),
+            dTransparencyVariant: ValueCell.create(variant),
         }
     }
 }
@@ -52,6 +56,7 @@ export function createEmptyTransparency(transparencyData?: TransparencyData): Tr
             tTransparency: ValueCell.create(emptyTransparencyTexture),
             uTransparencyTexDim: ValueCell.create(Vec2.create(1, 1)),
             dTransparency: ValueCell.create(false),
+            dTransparencyVariant: ValueCell.create('single'),
         }
     }
 }

+ 1 - 0
src/mol-gl/renderable/direct-volume.ts

@@ -27,6 +27,7 @@ export const DirectVolumeSchema = {
     uTransparencyTexDim: UniformSpec('v2'),
     tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
     dTransparency: DefineSpec('boolean'),
+    dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
 
     uInstanceCount: UniformSpec('i'),
     uGroupCount: UniformSpec('i'),

+ 3 - 0
src/mol-gl/renderable/schema.ts

@@ -208,9 +208,12 @@ export type OverpaintSchema = typeof OverpaintSchema
 export type OverpaintValues = Values<OverpaintSchema>
 
 export const TransparencySchema = {
+    // aTransparency: AttributeSpec('float32', 1, 0), // TODO
     uTransparencyTexDim: UniformSpec('v2'),
     tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
     dTransparency: DefineSpec('boolean'),
+    // dTransparencyType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']), // TODO
+    dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
 }
 export type TransparencySchema = typeof TransparencySchema
 export type TransparencyValues = Values<TransparencySchema>

+ 16 - 37
src/mol-gl/shader/chunks/assign-material-color.glsl

@@ -11,44 +11,23 @@
     material.rgb = mix(material.rgb, vOverpaint.rgb, vOverpaint.a);
 #endif
 
-// apply transparency
+// apply screendoor transparency
 #if defined(dTransparency) && (defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance))
-    float ma = material.a * (1.0 - vTransparency);
-    ivec2 pixelCoord = ivec2(gl_FragCoord.xy);
+    float ta = 1.0 - vTransparency;
+    float at = 0.0;
 
-    // const mat4 thresholdMatrix = mat4(
-    //     1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
-    //     13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
-    //     4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
-    //     16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0
-    // );
-    // float at = thresholdMatrix[pixelCoord.x % 4][pixelCoord.y % 4];
+    #if defined(dTransparencyVariant_single)
+        ivec2 pixelCoord = ivec2(gl_FragCoord.xy);
+        const mat4 thresholdMatrix = mat4(
+            1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
+            13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
+            4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
+            16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0
+        );
+        at = thresholdMatrix[pixelCoord.x % 4][pixelCoord.y % 4];
+    #elif defined(dTransparencyVariant_multi)
+        at = fract(dot(vec3(gl_FragCoord.xy, vGroup + 0.5), vec3(2.0, 7.0, 23.0) / 17.0f));
+    #endif
 
-    // // https://research.nvidia.com/publication/hashed-alpha-testing
-    // // Find the discretized derivatives of our coordinates
-    // float maxDeriv = max(length(dFdx(vViewPosition)), length(dFdy(vViewPosition)));
-    // float pixScale = 1.0 / maxDeriv;
-    // // Find two nearest log-discretized noise scales
-    // vec2 pixScales = vec2(exp2(floor(log2(pixScale))), exp2(ceil(log2(pixScale))));
-    // // Compute alpha thresholds at our two noise scales
-    // vec2 alpha = vec2(hash3d(floor(pixScales.x * vViewPosition)), hash3d(floor(pixScales.y * vViewPosition)));
-    // // Factor to interpolate lerp with
-    // float lerpFactor = fract(log2(pixScale));
-    // // Interpolate alpha threshold from noise at two scales
-    // float x = (1.0 - lerpFactor) * alpha.x + lerpFactor * alpha.y;
-    // // Pass into CDF to compute uniformly distrib threshold
-    // float a = min(lerpFactor, 1.0 - lerpFactor);
-    // vec3 cases = vec3(
-    //     x * x / (2.0 * a * (1.0 - a)),
-    //     (x - 0.5 * a) / (1.0 - a),
-    //     1.0 - ((1.0 - x) * (1.0 - x) / (2.0 * a * (1.0 - a)))
-    // );
-    // // Find our final, uniformly distributed alpha threshold
-    // float at = (x < (1.0 - a)) ? ((x < a) ? cases.x : cases.y) : cases.z;
-    // // Avoids ατ == 0. Could also do
-    // at = clamp(at, 1.0e-6, 1.0);
-
-    float at = fract(dot(vec3(gl_FragCoord.xy, vGroup + 0.5), vec3(2.0, 7.0, 23.0) / 17.0f));
-
-    if (ma < 0.99 && (ma < 0.01 || ma < at)) discard;
+    if (ta < 0.99 && (ta < 0.01 || ta < at)) discard;
 #endif

+ 13 - 24
src/mol-plugin/state/transforms/helpers.ts

@@ -14,36 +14,25 @@ import { Transparency } from 'mol-theme/transparency';
 
 type Script = { language: string, expression: string }
 
+function scriptToLoci(structure: Structure, script: Script) {
+    const parsed = parseMolScript(script.expression)
+    if (parsed.length === 0) throw new Error('No query')
+    const query = transpileMolScript(parsed[0])
+
+    const compiled = compile<StructureSelection>(query)
+    const result = compiled(new QueryContext(structure))
+    return StructureSelection.toLoci2(result)
+}
+
 export function getStructureOverpaint(structure: Structure, scriptLayers: { script: Script, color: Color }[], alpha: number): Overpaint {
     const layers: Overpaint.Layer[] = []
     for (let i = 0, il = scriptLayers.length; i < il; ++i) {
         const { script, color } = scriptLayers[i]
-        const parsed = parseMolScript(script.expression)
-        if (parsed.length === 0) throw new Error('No query')
-        const query = transpileMolScript(parsed[0])
-
-        const compiled = compile<StructureSelection>(query)
-        const result = compiled(new QueryContext(structure))
-        const loci = StructureSelection.toLoci2(result)
-
-        layers.push({ loci, color })
+        layers.push({ loci: scriptToLoci(structure, script), color })
     }
     return { layers, alpha }
 }
 
-export function getStructureTransparency(structure: Structure, scriptLayers: { script: Script, value: number }[]): Transparency {
-    const layers: Transparency.Layer[] = []
-    for (let i = 0, il = scriptLayers.length; i < il; ++i) {
-        const { script, value } = scriptLayers[i]
-        const parsed = parseMolScript(script.expression)
-        if (parsed.length === 0) throw new Error('No query')
-        const query = transpileMolScript(parsed[0])
-
-        const compiled = compile<StructureSelection>(query)
-        const result = compiled(new QueryContext(structure))
-        const loci = StructureSelection.toLoci2(result)
-
-        layers.push({ loci, value })
-    }
-    return { layers }
+export function getStructureTransparency(structure: Structure, script: Script, value: number, variant: Transparency.Variant): Transparency {
+    return { loci: scriptToLoci(structure, script), value, variant }
 }

+ 7 - 18
src/mol-plugin/state/transforms/representation.ts

@@ -376,20 +376,9 @@ const TransparencyStructureRepresentation3D = PluginStateTransform.BuiltIn({
     from: SO.Molecule.Structure.Representation3D,
     to: SO.Molecule.Structure.Representation3DState,
     params: {
-        layers: PD.ObjectList({
-            script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :chain-test (= atom.label_asym_id A))' }),
-            value: PD.Numeric(0.5)
-        }, e => `Transparency ${e.value}`, {
-            defaultValue: [
-                {
-                    script: {
-                        language: 'mol-script',
-                        expression: '(sel.atom.atom-groups :chain-test (= atom.label_asym_id A))'
-                    },
-                    value: 0.5
-                }
-            ]
-        }),
+        script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :chain-test (= atom.label_asym_id A))' }),
+        value: PD.Numeric(0.75, { min: 0, max: 1, step: 0.01 }, { label: 'Transparency' }),
+        variant: PD.Select('single', [['single', 'Single-layer'], ['multi', 'Multi-layer']])
     }
 })({
     canAutoUpdate() {
@@ -397,25 +386,25 @@ const TransparencyStructureRepresentation3D = PluginStateTransform.BuiltIn({
     },
     apply({ a, params }) {
         const structure = a.data.source.data
-        const transparency = getStructureTransparency(structure, params.layers)
+        const transparency = getStructureTransparency(structure, params.script, params.value, params.variant)
 
         return new SO.Molecule.Structure.Representation3DState({
             state: { transparency },
             initialState: { transparency: Transparency.Empty },
             info: structure,
             source: a
-        }, { label: `Transparency (${transparency.layers.length} Layers)` })
+        }, { label: `Transparency (${transparency.value})` })
     },
     update({ a, b, newParams, oldParams }) {
         const structure = b.data.info as Structure
         if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate
         const oldTransparency = b.data.state.transparency!
-        const newTransparency = getStructureTransparency(structure, newParams.layers)
+        const newTransparency = getStructureTransparency(structure, newParams.script, newParams.value, newParams.variant)
         if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged
 
         b.data.state.transparency = newTransparency
         b.data.source = a
-        b.label = `Transparency (${newTransparency.layers.length} Layers)`
+        b.label = `Transparency (${newTransparency.value})`
         return StateTransformer.UpdateResult.Updated
     }
 });

+ 11 - 11
src/mol-repr/visual.ts

@@ -7,7 +7,7 @@
 import { RuntimeContext } from 'mol-task'
 import { GraphicsRenderObject } from 'mol-gl/render-object'
 import { PickingId } from '../mol-geo/geometry/picking';
-import { Loci } from 'mol-model/loci';
+import { Loci, isEmptyLoci } from 'mol-model/loci';
 import { MarkerAction, applyMarkerAction } from '../mol-geo/geometry/marker-data';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { WebGLContext } from 'mol-gl/webgl/context';
@@ -106,21 +106,21 @@ namespace Visual {
         const { tTransparency, uGroupCount, instanceCount } = renderObject.values
         const count = uGroupCount.ref.value * instanceCount.ref.value
 
-        // ensure texture has right size
-        createTransparency(transparency.layers.length ? count : 0, renderObject.values)
+        const { loci, value, variant } = transparency
+
+        // ensure texture has right size and variant
+        createTransparency(value && !isEmptyLoci(loci) ? count : 0, variant, renderObject.values)
 
         // clear if requested
         if (clear) clearTransparency(tTransparency.ref.value.array, 0, count)
 
-        for (let i = 0, il = transparency.layers.length; i < il; ++i) {
-            const { loci, value } = transparency.layers[i]
-            const apply = (interval: Interval) => {
-                const start = Interval.start(interval)
-                const end = Interval.end(interval)
-                return applyTransparencyValue(tTransparency.ref.value.array, start, end, value)
-            }
-            lociApply(loci, apply)
+        const apply = (interval: Interval) => {
+            const start = Interval.start(interval)
+            const end = Interval.end(interval)
+            return applyTransparencyValue(tTransparency.ref.value.array, start, end, value)
         }
+        lociApply(loci, apply)
+
         ValueCell.update(tTransparency, tTransparency.ref.value)
     }
 

+ 11 - 10
src/mol-theme/transparency.ts

@@ -4,23 +4,24 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Loci } from 'mol-model/loci';
+import { Loci, EmptyLoci } from 'mol-model/loci';
 
 export { Transparency }
 
-type Transparency = { layers: ReadonlyArray<Transparency.Layer> }
+interface Transparency {
+    readonly loci: Loci
+    readonly value: number
+    readonly variant: Transparency.Variant
+}
 
 namespace Transparency {
-    export type Layer = { readonly loci: Loci, readonly value: number }
-    export const Empty: Transparency = { layers: [] }
+    export type Variant = 'single' | 'multi'
+    export const Empty: Transparency = { loci: EmptyLoci, value: 0, variant: 'single' }
 
     export function areEqual(tA: Transparency, tB: Transparency) {
-        if (tA.layers.length === 0 && tB.layers.length === 0) return true
-        if (tA.layers.length !== tB.layers.length) return false
-        for (let i = 0, il = tA.layers.length; i < il; ++i) {
-            if (tA.layers[i].value !== tB.layers[i].value) return false
-            if (!Loci.areEqual(tA.layers[i].loci, tB.layers[i].loci)) return false
-        }
+        if (tA.value !== tB.value) return false
+        if (tA.variant !== tB.variant) return false
+        if (!Loci.areEqual(tA.loci, tB.loci)) return false
         return true
     }
 }