diff --git a/src/mol-gl/shader/gaussian-density.frag b/src/mol-gl/shader/gaussian-density.frag index 652e87891b45aa5e4a4bbafb8a5d654926acfe57..a78f893d0925baa6a2b91c122cdc2dd65c8fbc9c 100644 --- a/src/mol-gl/shader/gaussian-density.frag +++ b/src/mol-gl/shader/gaussian-density.frag @@ -22,8 +22,8 @@ varying float vRadius; #endif #pragma glslify: import('./chunks/common.glsl') -#pragma glslify: encodeFloatLog = require(./utils/encode-float-log.glsl) -#pragma glslify: decodeFloatLog = require(./utils/decode-float-log.glsl) +// #pragma glslify: encodeFloatLog = require(./utils/encode-float-log.glsl) +// #pragma glslify: decodeFloatLog = require(./utils/decode-float-log.glsl) #pragma glslify: encodeFloatRGB = require(./utils/encode-float-rgb.glsl) #pragma glslify: texture3dFrom2dNearest = require(./utils/texture3d-from-2d-nearest.glsl, intMod=intMod, intDiv=intDiv, foo=foo) // foo=foo is a workaround for a bug in glslify @@ -58,9 +58,11 @@ void main() { float density = exp(-uAlpha * ((dist * dist) / radiusSq)); gl_FragColor = vec4(density); #elif defined(dCalcType_minDistance) - gl_FragColor.a = 1.0 - encodeFloatLog(dist); + gl_FragColor.a = 10000.0 - dist; + // gl_FragColor.a = 1.0 - encodeFloatLog(dist); #elif defined(dCalcType_groupId) - float minDistance = decodeFloatLog(1.0 - textureMinDist(fragPos).a); + float minDistance = 10000.0 - textureMinDist(fragPos).a; + // float minDistance = decodeFloatLog(1.0 - textureMinDist(fragPos).a); // TODO verify `length(uBboxSize / uGridDim) * 2.0` // on some machines `* 2.0` is needed while on others `* 0.5` works if (dist > minDistance + length(uBboxSize / uGridDim) * 0.5) diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index 0dcb92b520f8eb79e8b56b3f15585d04e6036e11..cf12509b2189e37e1364f59f1ea41e55ffb3829d 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -115,7 +115,7 @@ async function calcGaussianDensityTexture2d(ctx: RuntimeContext, webgl: WebGLCon framebuffer.bind() setRenderingDefaults(gl) - if (!texture) texture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'linear') + if (!texture) texture = createTexture(webgl, 'image-float32', 'rgba', 'float', 'nearest') texture.define(texDimX, texDimY) function render(fbTex: Texture) { @@ -173,7 +173,7 @@ async function calcGaussianDensityTexture3d(ctx: RuntimeContext, webgl: WebGLCon setRenderingDefaults(gl) gl.viewport(0, 0, dx, dy) - if (!texture) texture = createTexture(webgl, 'volume-uint8', 'rgba', 'ubyte', 'linear') + if (!texture) texture = createTexture(webgl, 'volume-float32', 'rgba', 'float', 'nearest') texture.define(dx, dy, dz) function render(fbTex: Texture) { @@ -330,7 +330,7 @@ function getTexture2dSize(gridDim: Vec3) { return { texDimX, texDimY, texRows, texCols } } -async function fieldFromTexture2d(ctx: WebGLContext, texture: Texture, dim: Vec3) { +export async function fieldFromTexture2d(ctx: WebGLContext, texture: Texture, dim: Vec3) { // console.time('fieldFromTexture2d') const { framebufferCache } = ctx const [ dx, dy, dz ] = dim @@ -343,7 +343,8 @@ async function fieldFromTexture2d(ctx: WebGLContext, texture: Texture, dim: Vec3 const idData = space.create() const idField = Tensor.create(space, idData) - const image = new Uint8Array(width * height * 4) + // const image = new Uint8Array(width * height * 4) + const image = new Float32Array(width * height * 4) const framebuffer = framebufferCache.get(FramebufferName).value framebuffer.bind() @@ -365,8 +366,8 @@ async function fieldFromTexture2d(ctx: WebGLContext, texture: Texture, dim: Vec3 for (let iy = 0; iy < dy; ++iy) { for (let ix = 0; ix < dx; ++ix) { const idx = 4 * (tmpCol * dx + (iy + tmpRow) * width + ix) - data[j] = image[idx + 3] / 255 - idData[j] = decodeFloatRGB(image[idx], image[idx + 1], image[idx + 2]) + data[j] = image[idx + 3] // / 255 + idData[j] = decodeFloatRGB(image[idx] * 255, image[idx + 1] * 255, image[idx + 2] * 255) j++ } } diff --git a/src/tests/browser/marching-cubes.ts b/src/tests/browser/marching-cubes.ts new file mode 100644 index 0000000000000000000000000000000000000000..2aea27108434f504c12199b3c9862b0b27eba79e --- /dev/null +++ b/src/tests/browser/marching-cubes.ts @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import './index.html' +import { Canvas3D } from 'mol-canvas3d/canvas3d'; +import { Representation } from 'mol-repr/representation'; +import { Color } from 'mol-util/color'; +import { createRenderObject } from 'mol-gl/render-object'; +import { computeGaussianDensity, computeGaussianDensityTexture2d } from 'mol-math/geometry/gaussian-density'; +import { PositionData, Box3D, Sphere3D } from 'mol-math/geometry'; +import { OrderedSet } from 'mol-data/int'; +import { Vec3 } from 'mol-math/linear-algebra'; +import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm'; +import { Mesh } from 'mol-geo/geometry/mesh/mesh'; +import { ColorNames } from 'mol-util/color/tables'; +import { Isosurface } from 'mol-geo/geometry/isosurface/isosurface'; +import { calcActiveVoxels } from 'mol-gl/compute/marching-cubes/active-voxels'; +import { createHistogramPyramid } from 'mol-gl/compute/histogram-pyramid/reduction'; +import { createIsosurfaceBuffers } from 'mol-gl/compute/marching-cubes/isosurface'; + +const parent = document.getElementById('app')! +parent.style.width = '100%' +parent.style.height = '100%' + +const canvas = document.createElement('canvas') +canvas.style.width = '100%' +canvas.style.height = '100%' +parent.appendChild(canvas) + +const canvas3d = Canvas3D.create(canvas, parent, { + backgroundColor: ColorNames.white, + cameraMode: 'orthographic' +}) +canvas3d.animate() + +async function init() { + const { webgl } = canvas3d + + const position: PositionData = { + x: [0, 2], + y: [0, 2], + z: [0, 2], + indices: OrderedSet.ofSortedArray([0, 1]), + } + const box = Box3D.create(Vec3.create(-1, -1, -1), Vec3.create(3, 3, 3)) + // const position: PositionData = { + // x: [0], + // y: [0], + // z: [0], + // indices: OrderedSet.ofSortedArray([0]), + // } + // const box = Box3D.create(Vec3.create(-1, -1, -1), Vec3.create(1, 1, 1)) + const radius = () => 1.6 + const props = { + resolution: 0.1, + radiusOffset: 0, + smoothness: 1.5 + } + const isoValue = Math.exp(-props.smoothness) + + // console.log('bbox', densityTextureData.bbox) + + // console.time('gpu gaussian2') + // const densityTextureData2 = await computeGaussianDensityTexture2d(position, box, radius, props, webgl).run() + // webgl.waitForGpuCommandsCompleteSync() + // console.timeEnd('gpu gaussian2') + + // console.time('gpu mc2') + // console.time('gpu mc active2') + // const activeVoxelsTex2 = calcActiveVoxels(webgl, densityTextureData2.texture, densityTextureData2.gridDimension, isoValue) + // webgl.waitForGpuCommandsCompleteSync() + // console.timeEnd('gpu mc active2') + + // console.time('gpu mc pyramid2') + // const compacted2 = createHistogramPyramid(webgl, activeVoxelsTex2) + // webgl.waitForGpuCommandsCompleteSync() + // console.timeEnd('gpu mc pyramid2') + + // console.time('gpu mc vert2') + // const gv2 = createIsosurfaceBuffers(webgl, activeVoxelsTex2, densityTextureData2.texture, compacted2, densityTextureData2.gridDimension, densityTextureData2.transform, isoValue) + // webgl.waitForGpuCommandsCompleteSync() + // console.timeEnd('gpu mc vert2') + // console.timeEnd('gpu mc2') + + console.time('gpu gaussian') + const densityTextureData = await computeGaussianDensityTexture2d(position, box, radius, props, webgl).run() + webgl.waitForGpuCommandsCompleteSync() + console.timeEnd('gpu gaussian') + + console.time('gpu mc') + console.time('gpu mc active') + const activeVoxelsTex = calcActiveVoxels(webgl, densityTextureData.texture, densityTextureData.gridDimension, isoValue) + webgl.waitForGpuCommandsCompleteSync() + console.timeEnd('gpu mc active') + + console.time('gpu mc pyramid') + const compacted = createHistogramPyramid(webgl, activeVoxelsTex) + webgl.waitForGpuCommandsCompleteSync() + console.timeEnd('gpu mc pyramid') + + console.time('gpu mc vert') + const gv = createIsosurfaceBuffers(webgl, activeVoxelsTex, densityTextureData.texture, compacted, densityTextureData.gridDimension, densityTextureData.transform, isoValue) + webgl.waitForGpuCommandsCompleteSync() + console.timeEnd('gpu mc vert') + console.timeEnd('gpu mc') + + console.log({ ...webgl.stats, programCount: webgl.programCache.count, shaderCount: webgl.shaderCache.count }) + + const mcIsosurface = Isosurface.create(gv.vertexCount, 1, gv.vertexTexture, gv.normalBuffer, gv.groupBuffer, Sphere3D.fromBox3D(Sphere3D.zero(), densityTextureData.bbox)) + const mcIsoSurfaceProps = { doubleSided: true, flatShaded: true, alpha: 1.0 } + const mcIsoSurfaceValues = Isosurface.Utils.createValuesSimple(mcIsosurface, mcIsoSurfaceProps, Color(0x112299), 1) + // console.log('mcIsoSurfaceValues', mcIsoSurfaceValues) + const mcIsoSurfaceState = Isosurface.Utils.createRenderableState(mcIsoSurfaceProps) + const mcIsoSurfaceRenderObject = createRenderObject('isosurface', mcIsoSurfaceValues, mcIsoSurfaceState, -1) + const mcIsoSurfaceRepr = Representation.fromRenderObject('isosurface', mcIsoSurfaceRenderObject) + + canvas3d.add(mcIsoSurfaceRepr) + canvas3d.resetCamera() + + // + + console.time('cpu gaussian') + const densityData = await computeGaussianDensity(position, box, radius, { ...props, useGpu: false }, webgl).run() + console.timeEnd('cpu gaussian') + // console.log({ densityData }) + + const params = { + isoLevel: isoValue, + scalarField: densityData.field, + idField: densityData.idField + } + + console.time('cpu mc') + const surface = await computeMarchingCubesMesh(params).run() + console.timeEnd('cpu mc') + // console.log('surface', surface) + Mesh.computeNormalsImmediate(surface) + const meshProps = { doubleSided: true, flatShaded: true, alpha: 1.0 } + const meshValues = Mesh.Utils.createValuesSimple(surface, meshProps, Color(0x995511), 1) + const meshState = Mesh.Utils.createRenderableState(meshProps) + const meshRenderObject = createRenderObject('mesh', meshValues, meshState, -1) + const meshRepr = Representation.fromRenderObject('mesh', meshRenderObject) + + canvas3d.add(meshRepr) + canvas3d.resetCamera() +} + +init() \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index bda3612a92f256206750a7c20e5422ed44d57fc9..f3cf06a2970a8589f0a570f336987d8cbc0f0a67 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -102,6 +102,7 @@ module.exports = [ createApp('model-server-query'), createBrowserTest('font-atlas'), + createBrowserTest('marching-cubes'), createBrowserTest('render-lines'), createBrowserTest('render-mesh'), createBrowserTest('render-shape'),