Explorar el Código

wip, direct-volume-rendering

- support for flip-sided and double-sided
- add single-layer isosurface option
- fix webgl1 depth pass not clearing
- fix slow out-of-bounds access when creating texture
Alexander Rose hace 4 años
padre
commit
862f8193ef

+ 2 - 2
src/mol-canvas3d/passes/draw.ts

@@ -147,7 +147,7 @@ export class DrawPass {
         }
 
         // do direct-volume rendering
-        if (!toDrawingBuffer && this.scene.volumes.renderables.length > 0) {
+        if (!toDrawingBuffer) {
             if (!this.packedDepth) {
                 this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
                 this.webgl.state.depthMask(true);
@@ -158,7 +158,7 @@ export class DrawPass {
             // do volume depth pass if extensions.depthTexture is unsupported (i.e. depthTarget is set)
             if (this.depthTargetVolumes) {
                 this.depthTargetVolumes.bind();
-                this.renderer.render(this.scene.volumes, this.camera, 'depth', false, transparentBackground, this.depthTexturePrimitives);
+                this.renderer.render(this.scene.volumes, this.camera, 'depth', true, transparentBackground, this.depthTexturePrimitives);
                 this.colorTarget.bind();
             }
         }

+ 20 - 7
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -51,6 +51,8 @@ export interface DirectVolume {
 
     /** Bounding sphere of the volume */
     readonly boundingSphere: Sphere3D
+
+    setBoundingSphere(boundingSphere: Sphere3D): void
 }
 
 export namespace DirectVolume {
@@ -98,7 +100,11 @@ export namespace DirectVolume {
                 }
                 return boundingSphere;
             },
-            packedGroup: ValueCell.create(packedGroup)
+            packedGroup: ValueCell.create(packedGroup),
+            setBoundingSphere(sphere: Sphere3D) {
+                Sphere3D.copy(boundingSphere, sphere);
+                currentHash = hashCode(directVolume);
+            }
         };
         return directVolume;
     }
@@ -135,6 +141,7 @@ export namespace DirectVolume {
         return PD.MappedStatic('volume', {
             isosurface: PD.Group({
                 isoValue: isoValueParam,
+                singleLayer: PD.Boolean(false, { isEssential: true }),
             }, { isFlat: true }),
             volume: PD.Group({
                 controlPoints: PD.LineGraph([
@@ -157,8 +164,8 @@ export namespace DirectVolume {
 
     export const Params = {
         ...BaseGeometry.Params,
-        // doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
-        // flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
+        doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
+        flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
         flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
         ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
         renderMode: createRenderModeParam(),
@@ -210,6 +217,10 @@ export namespace DirectVolume {
             ? props.renderMode.params.isoValue
             : Volume.IsoValue.relative(2);
 
+        const singleLayer = props.renderMode.name === 'isosurface'
+            ? props.renderMode.params.singleLayer
+            : false;
+
         const maxSteps = Math.ceil(Vec3.magnitude(gridDimension.ref.value) * props.stepsPerCell);
 
         return {
@@ -247,10 +258,11 @@ export namespace DirectVolume {
             uCartnToUnit: directVolume.cartnToUnit,
             uUnitToCartn: directVolume.unitToCartn,
             dPackedGroup: directVolume.packedGroup,
+            dSingleLayer: ValueCell.create(singleLayer),
 
-            dDoubleSided: ValueCell.create(false),
+            dDoubleSided: ValueCell.create(props.doubleSided),
             dFlatShaded: ValueCell.create(props.flatShaded),
-            dFlipSided: ValueCell.create(true),
+            dFlipSided: ValueCell.create(props.flipSided),
             dIgnoreLight: ValueCell.create(props.ignoreLight),
         };
     }
@@ -264,14 +276,15 @@ export namespace DirectVolume {
     function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
         ValueCell.updateIfChanged(values.alpha, props.alpha);
         ValueCell.updateIfChanged(values.uAlpha, props.alpha);
-        // ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
+        ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
         ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
-        // ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
+        ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
         ValueCell.updateIfChanged(values.dRenderMode, props.renderMode.name);
 
         if (props.renderMode.name === 'isosurface') {
             ValueCell.updateIfChanged(values.uIsoValue, getNormalizedIsoValue(values.uIsoValue.ref.value, props.renderMode.params.isoValue, values.uGridStats.ref.value));
+            ValueCell.updateIfChanged(values.dSingleLayer, props.renderMode.params.singleLayer);
         } else if (props.renderMode.name === 'volume') {
             const controlPoints = getControlPointsFromVec2Array(props.renderMode.params.controlPoints);
             createTransferFunctionTexture(controlPoints, props.renderMode.params.list.colors, values.tTransferTex);

+ 0 - 1
src/mol-gl/TODO

@@ -1 +0,0 @@
-- handle webgl loose context

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

@@ -70,6 +70,7 @@ export const DirectVolumeSchema = {
     uTransform: UniformSpec('m4'),
     uGridDim: UniformSpec('v3'),
     dRenderMode: DefineSpec('string', ['isosurface', 'volume']),
+    dSingleLayer: DefineSpec('boolean'),
     tTransferTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'linear'),
 
     dGridTexType: DefineSpec('string', ['2d', '3d']),

+ 3 - 1
src/mol-gl/renderer.ts

@@ -267,7 +267,9 @@ namespace Renderer {
             if (depthTexture) program.bindTextures([['tDepth', depthTexture]]);
 
             if (r.values.dDoubleSided) {
-                if (r.values.dDoubleSided.ref.value || r.values.hasReflection.ref.value) {
+                if ((r.values.dDoubleSided.ref.value || r.values.hasReflection.ref.value) &&
+                    !r.values.uStepFactor // indicates direct-volume, always cull
+                ) {
                     state.disable(gl.CULL_FACE);
                 } else {
                     state.enable(gl.CULL_FACE);

+ 19 - 3
src/mol-gl/shader/direct-volume.frag.ts

@@ -97,8 +97,7 @@ uniform mat4 uUnitToCartn;
         return texture3dFrom2dLinear(tGridTex, pos + (vec3(0.5, 0.5, 0.0) / uGridDim), uGridDim, uGridTexDim.xy);
     }
     vec4 textureGroup(vec3 pos) {
-        vec3 nearestPos = floor(pos * uGridDim + 0.5) / uGridDim;
-        return texture3dFrom2dNearest(tGridTex, nearestPos + (vec3(0.5, 0.5, 0.0) / uGridDim), uGridDim, uGridTexDim.xy);
+        return texture3dFrom2dNearest(tGridTex, pos + (vec3(0.5, 0.5, 0.0) / uGridDim), uGridDim, uGridTexDim.xy);
     }
 #elif defined(dGridTexType_3d)
     vec4 textureVal(vec3 pos) {
@@ -223,8 +222,21 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
                         color = uColor;
                     #endif
 
-                    bool flipped = value > uIsoValue.y; // negative isosurfaces
+                    // handle flipping and negative isosurfaces
+                    #ifdef dFlipSided
+                        bool flipped = value < uIsoValue.y; // flipped
+                    #else
+                        bool flipped = value > uIsoValue.y;
+                    #endif
                     interior = value < uIsoValue.x && flipped;
+                    #ifndef dDoubleSided
+                        if (interior) {
+                            prevValue = value;
+                            prevCell = cell;
+                            pos += step;
+                            continue;
+                        }
+                    #endif
                     vec3 vViewPosition = mvPosition.xyz;
                     vec4 material = vec4(color, uAlpha);
 
@@ -261,6 +273,10 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
                     src.rgb *= src.a;
                     dst = (1.0 - dst.a) * src + dst; // standard blending
                 #endif
+
+                #ifdef dSingleLayer
+                    break;
+                #endif
             }
             prevValue = value;
         #endif

+ 50 - 27
src/mol-repr/volume/direct-volume.ts

@@ -23,6 +23,14 @@ import { Loci, EmptyLoci } from '../../mol-model/loci';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { eachVolumeLoci } from './util';
 
+// avoiding namespace lookup improved performance in Chrome (Aug 2020)
+const v3set = Vec3.set;
+const v3normalize = Vec3.normalize;
+const v3sub = Vec3.sub;
+const v3addScalar = Vec3.addScalar;
+const v3scale = Vec3.scale;
+const v3toArray = Vec3.toArray;
+
 function getBoundingBox(gridDimension: Vec3, transform: Mat4) {
     const bbox = Box3D();
     Box3D.add(bbox, gridDimension);
@@ -45,8 +53,6 @@ function getVolumeTexture2dLayout(dim: Vec3, maxTextureSize: number) {
     } else {
         width = dim[0] * dim[2];
     }
-    width += columns; // horizontal padding
-    height += rows; // vertical padding
     return { width, height, columns, rows };
 }
 
@@ -60,31 +66,39 @@ function createVolumeTexture2d(volume: Volume, maxTextureSize: number) {
     const textureImage = { array, width, height };
 
     const diff = max - min;
-    const [ xl, yl, zl ] = dim;
-    const xlp = xl + 1; // horizontal padding
-    const ylp = yl + 1; // vertical padding
+    const [ xn, yn, zn ] = dim;
 
     const n0 = Vec3();
     const n1 = Vec3();
 
-    let i = 0;
-    for (let z = 0; z < zl; ++z) {
-        for (let y = 0; y < yl; ++y) {
-            for (let x = 0; x < xl; ++x) {
-                const column = Math.floor(((z * xlp) % width) / xlp);
-                const row = Math.floor((z * xlp) / width);
-                const px = column * xlp + x;
-                const index = 4 * ((row * ylp * width) + (y * width) + px);
+    const xn1 = xn - 1;
+    const yn1 = yn - 1;
+    const zn1 = zn - 1;
+
+    for (let z = 0; z < zn; ++z) {
+        for (let y = 0; y < yn; ++y) {
+            for (let x = 0; x < xn; ++x) {
+                const column = Math.floor(((z * xn) % width) / xn);
+                const row = Math.floor((z * xn) / width);
+                const px = column * xn + x;
+                const index = 4 * ((row * yn * width) + (y * width) + px);
                 const offset = o(x, y, z);
 
-                Vec3.set(n0, data[o(x - 1, y, z)], data[o(x, y - 1, z)], data[o(x, y, z - 1)]);
-                Vec3.set(n1, data[o(x + 1, y, z)], data[o(x, y + 1, z)], data[o(x, y, z + 1)]);
-                Vec3.normalize(n0, Vec3.sub(n0, n0, n1));
-                Vec3.addScalar(n0, Vec3.scale(n0, n0, 0.5), 0.5);
-                Vec3.toArray(Vec3.scale(n0, n0, 255), array, i);
+                v3set(n0,
+                    data[o(Math.max(0, x - 1), y, z)],
+                    data[o(x, Math.max(0, y - 1), z)],
+                    data[o(x, y, Math.max(0, z - 1))]
+                );
+                v3set(n1,
+                    data[o(Math.min(xn1, x + 1), y, z)],
+                    data[o(x, Math.min(yn1, y + 1), z)],
+                    data[o(x, y, Math.min(zn1, z + 1))]
+                );
+                v3normalize(n0, v3sub(n0, n0, n1));
+                v3addScalar(n0, v3scale(n0, n0, 0.5), 0.5);
+                v3toArray(v3scale(n0, n0, 255), array, index);
 
                 array[index + 3] = ((data[offset] - min) / diff) * 255;
-                i += 4;
             }
         }
     }
@@ -98,9 +112,6 @@ export function createDirectVolume2d(ctx: RuntimeContext, webgl: WebGLContext, v
     // debugTexture(createImageData(textureImage.array, textureImage.width, textureImage.height), 1/3)
     const transform = Grid.getGridToCartesianTransform(volume.grid);
     const bbox = getBoundingBox(gridDimension, transform);
-    const dim = Vec3.create(gridDimension[0], gridDimension[1], gridDimension[2]);
-    dim[0] += 1; // horizontal padding
-    dim[1] += 1; // vertical padding
 
     const texture = directVolume ? directVolume.gridTexture.ref.value : webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
     texture.load(textureImage);
@@ -123,17 +134,29 @@ function createVolumeTexture3d(volume: Volume) {
     const n0 = Vec3();
     const n1 = Vec3();
 
+    const width1 = width - 1;
+    const height1 = height - 1;
+    const depth1 = depth - 1;
+
     let i = 0;
     for (let z = 0; z < depth; ++z) {
         for (let y = 0; y < height; ++y) {
             for (let x = 0; x < width; ++x) {
                 const offset = o(x, y, z);
 
-                Vec3.set(n0, data[o(x - 1, y, z)], data[o(x, y - 1, z)], data[o(x, y, z - 1)]);
-                Vec3.set(n1, data[o(x + 1, y, z)], data[o(x, y + 1, z)], data[o(x, y, z + 1)]);
-                Vec3.normalize(n0, Vec3.sub(n0, n0, n1));
-                Vec3.addScalar(n0, Vec3.scale(n0, n0, 0.5), 0.5);
-                Vec3.toArray(Vec3.scale(n0, n0, 255), array, i);
+                v3set(n0,
+                    data[o(Math.max(0, x - 1), y, z)],
+                    data[o(x, Math.max(0, y - 1), z)],
+                    data[o(x, y, Math.max(0, z - 1))]
+                );
+                v3set(n1,
+                    data[o(Math.min(width1, x + 1), y, z)],
+                    data[o(x, Math.min(height1, y + 1), z)],
+                    data[o(x, y, Math.min(depth1, z + 1))]
+                );
+                v3normalize(n0, v3sub(n0, n0, n1));
+                v3addScalar(n0, v3scale(n0, n0, 0.5), 0.5);
+                v3toArray(v3scale(n0, n0, 255), array, i);
 
                 array[i + 3] = ((data[offset] - min) / diff) * 255;
                 i += 4;