Browse Source

wip, gaussian surface

- fix webgl1 gaussian volume broken
- fix 2d volume slice missing first row
- use scissor test to avoid useless calculations
- reduce radius for which gaussians are calculated
-
Alexander Rose 4 năm trước cách đây
mục cha
commit
20ee659b00

+ 5 - 3
src/mol-gl/compute/histogram-pyramid/reduction.ts

@@ -12,7 +12,7 @@ import { Texture, TextureFilter, TextureFormat, TextureKind, TextureType } from
 import { ShaderCode } from '../../../mol-gl/shader-code';
 import { ValueCell } from '../../../mol-util';
 import { QuadSchema, QuadValues } from '../util';
-import { Vec2 } from '../../../mol-math/linear-algebra';
+import { Vec2, Vec3 } from '../../../mol-math/linear-algebra';
 import { getHistopyramidSum } from './sum';
 import { Framebuffer } from '../../../mol-gl/webgl/framebuffer';
 import { isPowerOfTwo } from '../../../mol-math/misc';
@@ -77,7 +77,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
     state.disable(gl.CULL_FACE);
     state.disable(gl.BLEND);
     state.disable(gl.DEPTH_TEST);
-    state.disable(gl.SCISSOR_TEST);
+    state.enable(gl.SCISSOR_TEST);
     state.depthMask(false);
     state.colorMask(true, true, true, true);
     state.clearColor(0, 0, 0, 0);
@@ -107,7 +107,7 @@ export interface HistogramPyramid {
     scale: Vec2
 }
 
-export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2): HistogramPyramid {
+export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2, gridTexDim: Vec3): HistogramPyramid {
     const { gl } = ctx;
 
     // printTexture(ctx, inputTexture, 2)
@@ -153,7 +153,9 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
         }
         ctx.state.currentRenderItemId = -1;
         gl.viewport(0, 0, size, size);
+        gl.scissor(0, 0, size, size);
         gl.clear(gl.COLOR_BUFFER_BIT);
+        gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
         renderable.render();
 
         pyramidTexture.bind(0);

+ 3 - 1
src/mol-gl/compute/marching-cubes/active-voxels.ts

@@ -75,7 +75,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
     state.disable(gl.CULL_FACE);
     state.disable(gl.BLEND);
     state.disable(gl.DEPTH_TEST);
-    state.disable(gl.SCISSOR_TEST);
+    state.enable(gl.SCISSOR_TEST);
     state.depthMask(false);
     state.colorMask(true, true, true, true);
     state.clearColor(0, 0, 0, 0);
@@ -104,7 +104,9 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
     activeVoxelsTex.attachFramebuffer(framebuffer, 0);
     setRenderingDefaults(ctx);
     gl.viewport(0, 0, width, height);
+    gl.scissor(0, 0, width, height);
     gl.clear(gl.COLOR_BUFFER_BIT);
+    gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
     renderable.render();
 
     // console.log('gridScale', gridScale, 'gridTexDim', gridTexDim, 'gridDim', gridDim)

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

@@ -76,6 +76,7 @@ export function printImageData(imageData: ImageData, scale = 1, pixelated = fals
         img.style.top = '0px';
         img.style.left = '0px';
         img.style.border = 'solid grey';
+        img.style.pointerEvents = 'none';
         document.body.appendChild(img);
     }, 'image/png');
 }

+ 5 - 5
src/mol-gl/shader/gaussian-density.frag.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -31,7 +31,7 @@ uniform float uCurrentX;
 uniform float uCurrentY;
 uniform float uAlpha;
 uniform float uResolution;
-uniform float uRadiusFactor;
+uniform float uRadiusFactorInv;
 
 void main() {
     vec2 v = gl_FragCoord.xy - vec2(uCurrentX, uCurrentY) - 0.5;
@@ -40,16 +40,16 @@ void main() {
 
     #if defined(dCalcType_density)
         float density = exp(-uAlpha * ((dist * dist) * vRadiusSqInv));
-        gl_FragColor.a = density / uRadiusFactor;
+        gl_FragColor.a = density * uRadiusFactorInv;
     #elif defined(dCalcType_minDistance)
-        gl_FragColor.a = 1.0 - dist / uRadiusFactor;
+        gl_FragColor.a = 1.0 - dist * uRadiusFactorInv;
     #elif defined(dCalcType_groupId)
         #if defined(dGridTexType_2d)
             float minDistance = 1.0 - texture2D(tMinDistanceTex, (gl_FragCoord.xy) / (uGridTexDim.xy / uGridTexScale)).a;
         #elif defined(dGridTexType_3d)
             float minDistance = 1.0 - texelFetch(tMinDistanceTex, ivec3(gl_FragCoord.xy, uCurrentSlice), 0).a;
         #endif
-        if (dist / uRadiusFactor > minDistance + uResolution * 0.05)
+        if (dist * uRadiusFactorInv > minDistance + uResolution * 0.05)
             discard;
         gl_FragColor.rgb = encodeFloatRGB(vGroup);
     #endif

+ 2 - 3
src/mol-gl/shader/gaussian-density.vert.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -21,7 +21,6 @@ varying float vRadiusSqInv;
 
 uniform vec3 uBboxSize;
 uniform vec3 uBboxMin;
-uniform float uCurrentSlice;
 uniform float uResolution;
 
 void main() {
@@ -29,7 +28,7 @@ void main() {
     #if defined(dCalcType_groupId)
         vGroup = aGroup;
     #endif
-    gl_PointSize = floor(((aRadius * 6.0) / uResolution) + 0.5);
+    gl_PointSize = ceil(((aRadius * 3.0) / uResolution) + uResolution);
     vPosition = (aPosition - uBboxMin) / uResolution;
     gl_Position = vec4(((aPosition - uBboxMin) / uBboxSize) * 2.0 - 1.0, 1.0);
 }

+ 1 - 1
src/mol-math/geometry/gaussian-density.ts

@@ -73,7 +73,7 @@ function _computeGaussianDensityTexture(type: '2d' | '3d', position: PositionDat
     if (!GaussianDensityTexture) throw 'GPU computation not supported on this platform';
     return Task.create('Gaussian Density', async ctx => {
         return type === '2d' ?
-            GaussianDensityTexture2d(webgl, position, box, radius, props, texture) :
+            GaussianDensityTexture2d(webgl, position, box, radius, false, props, texture) :
             GaussianDensityTexture3d(webgl, position, box, radius, props, texture);
     });
 }

+ 25 - 15
src/mol-math/geometry/gaussian-density/gpu.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -40,7 +40,7 @@ export const GaussianDensitySchema = {
     uGridTexScale: UniformSpec('v2', true),
     uAlpha: UniformSpec('f', true),
     uResolution: UniformSpec('f', true),
-    uRadiusFactor: UniformSpec('f', true),
+    uRadiusFactorInv: UniformSpec('f', true),
     tMinDistanceTex: TextureSpec('texture', 'rgba', 'float', 'nearest'),
 
     dGridTexType: DefineSpec('string', ['2d', '3d']),
@@ -73,7 +73,7 @@ export function GaussianDensityGPU(position: PositionData, box: Box3D, radius: (
     // it's faster than texture3d
     // console.time('GaussianDensityTexture2d')
     const tmpTexture = getTexture('tmp', webgl, 'image-uint8', 'rgba', 'ubyte', 'linear');
-    const { scale, bbox, texture, gridDim, gridTexDim, radiusFactor } = calcGaussianDensityTexture2d(webgl, position, box, radius, props, tmpTexture);
+    const { scale, bbox, texture, gridDim, gridTexDim, radiusFactor } = calcGaussianDensityTexture2d(webgl, position, box, radius, false, props, tmpTexture);
     // webgl.waitForGpuCommandsCompleteSync()
     // console.timeEnd('GaussianDensityTexture2d')
     const { field, idField } = fieldFromTexture2d(webgl, texture, gridDim, gridTexDim);
@@ -84,11 +84,11 @@ export function GaussianDensityGPU(position: PositionData, box: Box3D, radius: (
 export function GaussianDensityTexture(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): GaussianDensityTextureData {
     return webgl.isWebGL2 ?
         GaussianDensityTexture3d(webgl, position, box, radius, props, oldTexture) :
-        GaussianDensityTexture2d(webgl, position, box, radius, props, oldTexture);
+        GaussianDensityTexture2d(webgl, position, box, radius, false, props, oldTexture);
 }
 
-export function GaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): GaussianDensityTextureData {
-    return finalizeGaussianDensityTexture(calcGaussianDensityTexture2d(webgl, position, box, radius, props, oldTexture));
+export function GaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, powerOfTwo: boolean, props: GaussianDensityGPUProps, oldTexture?: Texture): GaussianDensityTextureData {
+    return finalizeGaussianDensityTexture(calcGaussianDensityTexture2d(webgl, position, box, radius, powerOfTwo, props, oldTexture));
 }
 
 export function GaussianDensityTexture3d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, oldTexture?: Texture): GaussianDensityTextureData {
@@ -118,7 +118,7 @@ type _GaussianDensityTextureData = {
     radiusFactor: number
 }
 
-function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, texture?: Texture): _GaussianDensityTextureData {
+function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, powerOfTwo: boolean, props: GaussianDensityGPUProps, texture?: Texture): _GaussianDensityTextureData {
     // console.log('2d');
     const { gl, resources, state, extensions: { colorBufferFloat, textureFloat } } = webgl;
     const { smoothness, resolution } = props;
@@ -131,8 +131,11 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
     const gridTexScale = Vec2.create(texDimX / powerOfTwoSize, texDimY / powerOfTwoSize);
     const radiusFactor = maxRadius * 2;
 
+    const width = powerOfTwo ? powerOfTwoSize : texDimX;
+    const height = powerOfTwo ? powerOfTwoSize : texDimY;
+
     const minDistTex = getTexture('min-dist-2d', webgl, 'image-uint8', 'rgba', 'ubyte', 'nearest');
-    minDistTex.define(powerOfTwoSize, powerOfTwoSize);
+    minDistTex.define(width, height);
 
     const renderable = getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, minDistTex, expandedBox, dim, gridTexDim, gridTexScale, smoothness, resolution, radiusFactor);
 
@@ -147,14 +150,19 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
     if (!texture) texture = colorBufferFloat && textureFloat
         ? resources.texture('image-float32', 'rgba', 'float', 'linear')
         : resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
-    texture.define(powerOfTwoSize, powerOfTwoSize);
+    texture.define(width, height);
 
     // console.log(renderable)
 
     function render(fbTex: Texture, clear: boolean) {
         state.currentRenderItemId = -1;
         fbTex.attachFramebuffer(framebuffer, 0);
-        if (clear) gl.clear(gl.COLOR_BUFFER_BIT);
+        if (clear) {
+            gl.viewport(0, 0, width, height);
+            gl.scissor(0, 0, width, height);
+            gl.clear(gl.COLOR_BUFFER_BIT);
+        }
+        ValueCell.update(uCurrentY, 0);
         let currCol = 0;
         let currY = 0;
         let currX = 0;
@@ -165,10 +173,11 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
                 currX = 0;
                 ValueCell.update(uCurrentY, currY);
             }
-            // console.log({ i, currX, currY })
+            // console.log({ i, currX, currY });
             ValueCell.update(uCurrentX, currX);
             ValueCell.update(uCurrentSlice, i);
             gl.viewport(currX, currY, dx, dy);
+            gl.scissor(currX, currY, dx, dy);
             renderable.render();
             ++currCol;
             currX += dx;
@@ -185,7 +194,7 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
     setupGroupIdRendering(webgl, renderable);
     render(texture, false);
 
-    // printTexture(webgl, texture, 1);
+    // printTexture(webgl, minDistTex, 0.75);
 
     return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridTexScale, radiusFactor };
 }
@@ -214,6 +223,7 @@ function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionDat
     framebuffer.bind();
     setRenderingDefaults(webgl);
     gl.viewport(0, 0, dx, dy);
+    gl.scissor(0, 0, dx, dy);
 
     if (!texture) texture = colorBufferFloat && textureFloat
         ? resources.texture('volume-float32', 'rgba', 'float', 'linear')
@@ -304,7 +314,7 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po
         ValueCell.update(v.uGridTexScale, gridTexScale);
         ValueCell.updateIfChanged(v.uAlpha, smoothness);
         ValueCell.updateIfChanged(v.uResolution, resolution);
-        ValueCell.updateIfChanged(v.uRadiusFactor, radiusFactor);
+        ValueCell.updateIfChanged(v.uRadiusFactorInv, 1 / radiusFactor);
         ValueCell.update(v.tMinDistanceTex, minDistanceTexture);
 
         ValueCell.updateIfChanged(v.dGridTexType, minDistanceTexture.getDepth() > 0 ? '3d' : '2d');
@@ -338,7 +348,7 @@ function createGaussianDensityRenderable(webgl: WebGLContext, drawCount: number,
         uGridTexScale: ValueCell.create(gridTexScale),
         uAlpha: ValueCell.create(smoothness),
         uResolution: ValueCell.create(resolution),
-        uRadiusFactor: ValueCell.create(radiusFactor),
+        uRadiusFactorInv: ValueCell.create(1 / radiusFactor),
         tMinDistanceTex: ValueCell.create(minDistanceTexture),
 
         dGridTexType: ValueCell.create(minDistanceTexture.getDepth() > 0 ? '3d' : '2d'),
@@ -356,7 +366,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
     state.disable(gl.CULL_FACE);
     state.enable(gl.BLEND);
     state.disable(gl.DEPTH_TEST);
-    state.disable(gl.SCISSOR_TEST);
+    state.enable(gl.SCISSOR_TEST);
     state.depthMask(false);
     state.clearColor(0, 0, 0, 0);
 }

+ 15 - 9
src/mol-repr/structure/visual/gaussian-surface-mesh.ts

@@ -131,25 +131,31 @@ async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit,
             : resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
     }
 
-    const densityTextureData = await computeUnitGaussianDensityTexture2d(structure, unit, props, ctx.webgl, namedTextures[GaussianSurfaceName]).runInContext(ctx.runtime);
+    // console.time('computeUnitGaussianDensityTexture2d');
+    const densityTextureData = await computeUnitGaussianDensityTexture2d(structure, unit, true, props, ctx.webgl, namedTextures[GaussianSurfaceName]).runInContext(ctx.runtime);
     // console.log(densityTextureData)
     // console.log('vertexGroupTexture', readTexture(ctx.webgl, densityTextureData.texture))
-    // ctx.webgl.waitForGpuCommandsCompleteSync()
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    // console.timeEnd('computeUnitGaussianDensityTexture2d');
 
     const isoLevel = Math.exp(-props.smoothness) / densityTextureData.radiusFactor;
 
+    // console.time('calcActiveVoxels');
     const activeVoxelsTex = calcActiveVoxels(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, isoLevel, densityTextureData.gridTexScale);
-    // ctx.webgl.waitForGpuCommandsCompleteSync()
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    // console.timeEnd('calcActiveVoxels');
 
-    const compacted = createHistogramPyramid(ctx.webgl, activeVoxelsTex, densityTextureData.gridTexScale);
-    // ctx.webgl.waitForGpuCommandsCompleteSync()
+    // console.time('createHistogramPyramid');
+    const compacted = createHistogramPyramid(ctx.webgl, activeVoxelsTex, densityTextureData.gridTexScale, densityTextureData.gridTexDim);
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    // console.timeEnd('createHistogramPyramid');
 
+    // console.time('createIsosurfaceBuffers');
     const gv = createIsosurfaceBuffers(ctx.webgl, activeVoxelsTex, densityTextureData.texture, compacted, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.transform, isoLevel, textureMesh ? textureMesh.vertexGroupTexture.ref.value : undefined, textureMesh ? textureMesh.normalTexture.ref.value : undefined);
-    // ctx.webgl.waitForGpuCommandsCompleteSync()
+    // ctx.webgl.waitForGpuCommandsCompleteSync();
+    // console.timeEnd('createIsosurfaceBuffers');
 
-    // const boundingSphere = Sphere3D.zero()
-    // Sphere3D.addVec3(boundingSphere, boundingSphere, densityTextureData.gridDimension)
-    const boundingSphere = Sphere3D.fromBox3D(Sphere3D(), densityTextureData.bbox);
+    const boundingSphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset + getStructureExtraRadius(structure));
     const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexGroupTexture, gv.normalTexture, boundingSphere, textureMesh);
     // console.log({
     //     renderables: ctx.webgl.namedComputeRenderables,

+ 2 - 2
src/mol-repr/structure/visual/util/gaussian.ts

@@ -54,12 +54,12 @@ export function computeUnitGaussianDensityTexture(structure: Structure, unit: Un
     });
 }
 
-export function computeUnitGaussianDensityTexture2d(structure: Structure, unit: Unit, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
+export function computeUnitGaussianDensityTexture2d(structure: Structure, unit: Unit, powerOfTwo: boolean, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
     const { box } = unit.lookup3d.boundary;
     const p = ensureReasonableResolution(box, props);
     const { position, radius } = getUnitConformationAndRadius(structure, unit, p);
     return Task.create('Gaussian Density', async ctx => {
-        return GaussianDensityTexture2d(webgl, position, box, radius, p, texture);
+        return GaussianDensityTexture2d(webgl, position, box, radius, powerOfTwo, p, texture);
     });
 }
 

+ 2 - 2
src/tests/browser/marching-cubes.ts

@@ -68,7 +68,7 @@ async function init() {
         console.timeEnd('gpu mc active2');
 
         console.time('gpu mc pyramid2');
-        const compacted2 = createHistogramPyramid(webgl, activeVoxelsTex2, densityTextureData2.gridTexScale);
+        const compacted2 = createHistogramPyramid(webgl, activeVoxelsTex2, densityTextureData2.gridTexScale, densityTextureData2.gridTexDim);
         webgl.waitForGpuCommandsCompleteSync();
         console.timeEnd('gpu mc pyramid2');
 
@@ -91,7 +91,7 @@ async function init() {
     console.timeEnd('gpu mc active');
 
     console.time('gpu mc pyramid');
-    const compacted = createHistogramPyramid(webgl, activeVoxelsTex, densityTextureData.gridTexScale);
+    const compacted = createHistogramPyramid(webgl, activeVoxelsTex, densityTextureData.gridTexScale, densityTextureData.gridTexDim);
     webgl.waitForGpuCommandsCompleteSync();
     console.timeEnd('gpu mc pyramid');