Explorar el Código

wip, direct-volume rendering

- volume marking
- position iterator
- trilinear position/vertex color interpolation
- fix missing update on traceOnly param change
Alexander Rose hace 4 años
padre
commit
caefe7ba67

+ 26 - 4
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -5,7 +5,7 @@
  */
 
 import { hashFnv32a } from '../../../mol-data/util';
-import { LocationIterator } from '../../../mol-geo/util/location-iterator';
+import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
 import { RenderableState } from '../../../mol-gl/renderable';
 import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
 import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
@@ -28,7 +28,6 @@ import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './
 import { createEmptyClipping } from '../clipping-data';
 import { Grid, Volume } from '../../../mol-model/volume';
 import { ColorNames } from '../../../mol-util/color/names';
-import { NullLocation } from '../../../mol-model/location';
 
 const VolumeBox = Box();
 
@@ -182,9 +181,31 @@ export namespace DirectVolume {
         updateBoundingSphere,
         createRenderableState,
         updateRenderableState,
-        createPositionIterator: () => LocationIterator(1, 1, 1, () => NullLocation)
+        createPositionIterator
     };
 
+    function createPositionIterator(directVolume: DirectVolume, transform: TransformData): LocationIterator {
+        const t = directVolume.transform.ref.value;
+        const [x, y, z] = directVolume.gridDimension.ref.value;
+        const groupCount = x * y * z;
+        const instanceCount = transform.instanceCount.ref.value;
+        const location = PositionLocation();
+        const p = location.position;
+        const m = transform.aTransform.ref.value;
+        const getLocation = (groupIndex: number, instanceIndex: number) => {
+            const k = Math.floor(groupIndex / z);
+            p[0] = Math.floor(k / y);
+            p[1] = k % y;
+            p[2] = groupIndex % z;
+            Vec3.transformMat4(p, p, t);
+            if (instanceIndex >= 0) {
+                Vec3.transformMat4Offset(p, p, m, 0, 0, instanceIndex * 16);
+            }
+            return location;
+        };
+        return LocationIterator(groupCount, instanceCount, 1, getLocation);
+    }
+
     function getNormalizedIsoValue(out: Vec2, isoValue: Volume.IsoValue, stats: Vec4) {
         const [min, max, mean, sigma] = stats;
         const value = Volume.IsoValue.toAbsolute(isoValue, { min, max, mean, sigma }).absoluteValue;
@@ -205,7 +226,8 @@ export namespace DirectVolume {
         const transparency = createEmptyTransparency();
         const clipping = createEmptyClipping();
 
-        const counts = { drawCount: VolumeBox.indices.length, vertexCount: VolumeBox.vertices.length / 3, groupCount, instanceCount };
+        const [x, y, z] = gridDimension.ref.value;
+        const counts = { drawCount: VolumeBox.indices.length, vertexCount: x * y * z, groupCount, instanceCount };
 
         const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere);
         const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);

+ 3 - 1
src/mol-geo/geometry/geometry.ts

@@ -80,7 +80,9 @@ export namespace Geometry {
             case 'spheres': return geometry.sphereCount * 4;
             case 'text': return geometry.charCount * 4;
             case 'lines': return geometry.lineCount * 4;
-            case 'direct-volume': return 24;
+            case 'direct-volume':
+                const [x, y, z] = geometry.gridDimension.ref.value;
+                return x * y * z;
             case 'image': return 4;
             case 'texture-mesh': return geometry.vertexCount / 3;
         }

+ 2 - 0
src/mol-gl/shader-code.ts

@@ -55,6 +55,7 @@ import matrix_scale from './shader/chunks/matrix-scale.glsl';
 import normal_frag_params from './shader/chunks/normal-frag-params.glsl';
 import read_from_texture from './shader/chunks/read-from-texture.glsl';
 import size_vert_params from './shader/chunks/size-vert-params.glsl';
+import texture3d_from_1d_trilinear from './shader/chunks/texture3d-from-1d-trilinear.glsl';
 import texture3d_from_2d_linear from './shader/chunks/texture3d-from-2d-linear.glsl';
 import texture3d_from_2d_nearest from './shader/chunks/texture3d-from-2d-nearest.glsl';
 
@@ -84,6 +85,7 @@ const ShaderChunks: { [k: string]: string } = {
     normal_frag_params,
     read_from_texture,
     size_vert_params,
+    texture3d_from_1d_trilinear,
     texture3d_from_2d_linear,
     texture3d_from_2d_nearest
 };

+ 1 - 0
src/mol-gl/shader/chunks/apply-marker-color.glsl.ts

@@ -3,6 +3,7 @@ float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on som
 if (marker > 0.1) {
     if (intMod(marker, 2.0) > 0.1) {
         gl_FragColor.rgb = mix(uHighlightColor, gl_FragColor.rgb, 0.3);
+        gl_FragColor.a = max(0.02, gl_FragColor.a); // for direct-volume rendering
     } else {
         gl_FragColor.rgb = mix(uSelectColor, gl_FragColor.rgb, 0.3);
     }

+ 30 - 0
src/mol-gl/shader/chunks/texture3d-from-1d-trilinear.glsl.ts

@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+export default `
+vec4 texture3dFrom1dTrilinear(const in sampler2D tex, const in vec3 pos, const in vec3 gridDim, const in vec2 texDim, const in float offset) {
+    float gdYZ = gridDim.z * gridDim.y;
+    float gdZ = gridDim.z;
+    vec3 p0 = floor(pos * gridDim);
+    vec3 p1 = ceil(pos * gridDim);
+    vec3 pd = (pos * gridDim - p0) / (p1 - p0);
+    vec4 s000 = readFromTexture(tex, offset + p0.z + p0.y * gdZ + p0.x * gdYZ, texDim);
+    vec4 s100 = readFromTexture(tex, offset + p0.z + p0.y * gdZ + p1.x * gdYZ, texDim);
+    vec4 s001 = readFromTexture(tex, offset + p1.z + p0.y * gdZ + p0.x * gdYZ, texDim);
+    vec4 s101 = readFromTexture(tex, offset + p1.z + p0.y * gdZ + p1.x * gdYZ, texDim);
+    vec4 s010 = readFromTexture(tex, offset + p0.z + p1.y * gdZ + p0.x * gdYZ, texDim);
+    vec4 s110 = readFromTexture(tex, offset + p0.z + p1.y * gdZ + p1.x * gdYZ, texDim);
+    vec4 s011 = readFromTexture(tex, offset + p1.z + p1.y * gdZ + p0.x * gdYZ, texDim);
+    vec4 s111 = readFromTexture(tex, offset + p1.z + p1.y * gdZ + p1.x * gdYZ, texDim);
+    vec4 s00 = mix(s000, s100, pd.x);
+    vec4 s01 = mix(s001, s101, pd.x);
+    vec4 s10 = mix(s010, s110, pd.x);
+    vec4 s11 = mix(s011, s111, pd.x);
+    vec4 s0 = mix(s00, s10, pd.y);
+    vec4 s1 = mix(s01, s11, pd.y);
+    return mix(s0, s1, pd.z);
+}
+`;

+ 22 - 6
src/mol-gl/shader/direct-volume.frag.ts

@@ -13,6 +13,7 @@ precision highp int;
 #include light_frag_params
 
 #include read_from_texture
+#include texture3d_from_1d_trilinear
 #include texture3d_from_2d_nearest
 #include texture3d_from_2d_linear
 
@@ -35,6 +36,7 @@ uniform sampler2D tTransferTex;
 uniform float uStepFactor;
 
 uniform int uObjectId;
+uniform int uVertexCount;
 uniform int uInstanceCount;
 uniform int uGroupCount;
 
@@ -212,14 +214,18 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
                         float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y;
                     #endif
 
-                    #if defined(dColorType_instance)
+                    #if defined(dColorType_uniform)
+                        color = uColor;
+                    #elif defined(dColorType_instance)
                         color = readFromTexture(tColor, instance, uColorTexDim).rgb;
                     #elif defined(dColorType_group)
                         color = readFromTexture(tColor, group, uColorTexDim).rgb;
                     #elif defined(dColorType_groupInstance)
                         color = readFromTexture(tColor, instance * float(uGroupCount) + group, uColorTexDim).rgb;
-                    #elif defined(dColorType_uniform)
-                        color = uColor;
+                    #elif defined(dColorType_vertex)
+                        color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, 0.0).rgb;
+                    #elif defined(dColorType_vertexInstance)
+                        color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, instance * float(uVertexCount)).rgb;
                     #endif
 
                     // handle flipping and negative isosurfaces
@@ -304,6 +310,16 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
             }
 
             gl_FragColor.a = material.a * uAlpha;
+
+            #ifdef dPackedGroup
+                float group = decodeFloatRGB(textureGroup(floor(isoPos * uGridDim + 0.5) / uGridDim).rgb);
+            #else
+                vec3 g = floor(isoPos * uGridDim + 0.5);
+                float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y;
+            #endif
+
+            float vMarker = readFromTexture(tMarker, instance * float(uGroupCount) + group, uMarkerTexDim).a;
+            #include apply_marker_color
             #include apply_fog
 
             src = gl_FragColor;
@@ -334,7 +350,7 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
 // TODO support float texture for higher precision values???
 
 void main () {
-    // TODO handle on CPU in renderloop
+    // TODO handle on CPU in renderloop?
     #if defined(dRenderVariant_pick)
         #if defined(dRenderMode_volume)
             discard;
@@ -344,13 +360,13 @@ void main () {
         #endif
     #endif
 
-    vec3 cameraPos = uInvView[3].xyz / uInvView[3].w;
     vec3 rayDir = mix(normalize(origPos - uCameraPosition), uCameraDir, uIsOrtho);;
 
     // TODO: set the scale as uniform?
     float stepScale = min(uCellDim.x, min(uCellDim.y, uCellDim.z)) * uStepFactor;
     vec3 step = rayDir * stepScale;
 
-    gl_FragColor = raymarch(origPos, step);
+    float d = uNear - distance(origPos, uCameraPosition);
+    gl_FragColor = raymarch(origPos + (d * rayDir), step);
 }
 `;

+ 1 - 0
src/mol-repr/structure/visual/gaussian-density-volume.ts

@@ -56,6 +56,7 @@ export function GaussianDensityVolumeVisual(materialId: number): ComplexVisual<G
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true;
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true;
             if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true;
+            if (newProps.traceOnly !== currentProps.traceOnly) state.createGeometry = true;
             if (newProps.includeParent !== currentProps.includeParent) state.createGeometry = true;
         }
     }, materialId);

+ 6 - 4
src/mol-repr/volume/direct-volume.ts

@@ -213,7 +213,9 @@ export async function createDirectVolume(ctx: VisualContext, volume: Volume, the
 }
 
 function getLoci(volume: Volume, props: PD.Values<DirectVolumeParams>) {
-    return Volume.Loci(volume);
+    return props.renderMode.name === 'isosurface'
+        ? Volume.Isosurface.Loci(volume, props.renderMode.params.isoValue)
+        : Volume.Loci(volume);
 }
 
 export function getDirectVolumeLoci(pickingId: PickingId, volume: Volume, props: DirectVolumeProps, id: number) {
@@ -225,9 +227,9 @@ export function getDirectVolumeLoci(pickingId: PickingId, volume: Volume, props:
 }
 
 export function eachDirectVolume(loci: Loci, volume: Volume, props: DirectVolumeProps, apply: (interval: Interval) => boolean) {
-    return props.renderMode.name === 'isosurface'
-        ? eachVolumeLoci(loci, volume, props.renderMode.params.isoValue, apply)
-        : false;
+    const isoValue = props.renderMode.name === 'isosurface'
+        ? props.renderMode.params.isoValue : undefined;
+    return eachVolumeLoci(loci, volume, isoValue, apply);
 }
 
 //

+ 3 - 28
src/mol-repr/volume/slice.ts

@@ -16,13 +16,13 @@ import { RepresentationContext, RepresentationParamsGetter } from '../representa
 import { VisualContext } from '../visual';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci } from '../../mol-model/loci';
-import { Interval, OrderedSet, SortedArray } from '../../mol-data/int';
+import { Interval, SortedArray } from '../../mol-data/int';
 import { transformPositionArray } from '../../mol-geo/util';
-import { equalEps } from '../../mol-math/linear-algebra/3d/common';
 import { RenderableState } from '../../mol-gl/renderable';
 import { Color } from '../../mol-util/color';
 import { ColorTheme } from '../../mol-theme/color';
 import { encodeFloatRGBtoArray } from '../../mol-util/float-packing';
+import { eachVolumeLoci } from './util';
 
 export async function createImage(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<SliceParams>, image?: Image) {
     const { dimension: { name: dim }, isoValue } = props;
@@ -156,32 +156,7 @@ function getSliceLoci(pickingId: PickingId, volume: Volume, props: PD.Values<Sli
 }
 
 function eachSlice(loci: Loci, volume: Volume, props: PD.Values<SliceParams>, apply: (interval: Interval) => boolean) {
-    let changed = false;
-    if (Volume.isLoci(loci)) {
-        if (!Volume.areEquivalent(loci.volume, volume)) return false;
-        if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
-    } else if (Volume.Isosurface.isLoci(loci)) {
-        if (!Volume.areEquivalent(loci.volume, volume)) return false;
-        // TODO find a cheaper way?
-        const { stats, cells: { data } } = volume.grid;
-        const eps = stats.sigma;
-        const v = Volume.IsoValue.toAbsolute(loci.isoValue, stats).absoluteValue;
-        for (let i = 0, il = data.length; i < il; ++i) {
-            if (equalEps(v, data[i], eps)) {
-                if (apply(Interval.ofSingleton(i))) changed = true;
-            }
-        }
-    } else if (Volume.Cell.isLoci(loci)) {
-        if (!Volume.areEquivalent(loci.volume, volume)) return false;
-        if (Interval.is(loci.indices)) {
-            if (apply(loci.indices)) changed = true;
-        } else {
-            OrderedSet.forEach(loci.indices, v => {
-                if (apply(Interval.ofSingleton(v))) changed = true;
-            });
-        }
-    }
-    return changed;
+    return eachVolumeLoci(loci, volume, undefined, apply);
 }
 
 //

+ 16 - 3
src/mol-repr/volume/util.ts

@@ -7,16 +7,29 @@
 import { Volume } from '../../mol-model/volume';
 import { Loci } from '../../mol-model/loci';
 import { Interval, OrderedSet } from '../../mol-data/int';
+import { equalEps } from '../../mol-math/linear-algebra/3d/common';
 
-export function eachVolumeLoci(loci: Loci, volume: Volume, isoValue: Volume.IsoValue, apply: (interval: Interval) => boolean) {
+export function eachVolumeLoci(loci: Loci, volume: Volume, isoValue: Volume.IsoValue | undefined, apply: (interval: Interval) => boolean) {
     let changed = false;
     if (Volume.isLoci(loci)) {
         if (!Volume.areEquivalent(loci.volume, volume)) return false;
         if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
     } else if (Volume.Isosurface.isLoci(loci)) {
         if (!Volume.areEquivalent(loci.volume, volume)) return false;
-        if (!Volume.IsoValue.areSame(loci.isoValue, isoValue, volume.grid.stats)) return false;
-        if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
+        if (isoValue) {
+            if (Volume.IsoValue.areSame(loci.isoValue, isoValue, volume.grid.stats)) return false;
+            if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
+        } else {
+            // TODO find a cheaper way?
+            const { stats, cells: { data } } = volume.grid;
+            const eps = stats.sigma;
+            const v = Volume.IsoValue.toAbsolute(loci.isoValue, stats).absoluteValue;
+            for (let i = 0, il = data.length; i < il; ++i) {
+                if (equalEps(v, data[i], eps)) {
+                    if (apply(Interval.ofSingleton(i))) changed = true;
+                }
+            }
+        }
     } else if (Volume.Cell.isLoci(loci)) {
         if (!Volume.areEquivalent(loci.volume, volume)) return false;
         if (Interval.is(loci.indices)) {