diff --git a/src/mol-geo/geometry/texture-mesh/texture-mesh.ts b/src/mol-geo/geometry/texture-mesh/texture-mesh.ts index cd8c359fd67ef137d8154f74a2674347e70e7cf8..2cd745e3551f30ef10df78e455d8983ed201de69 100644 --- a/src/mol-geo/geometry/texture-mesh/texture-mesh.ts +++ b/src/mol-geo/geometry/texture-mesh/texture-mesh.ts @@ -21,7 +21,6 @@ import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh'; import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util'; import { Texture } from '../../../mol-gl/webgl/texture'; import { Vec2, Vec4 } from '../../../mol-math/linear-algebra'; -import { fillSerial } from '../../../mol-util/array'; import { createEmptyClipping } from '../clipping-data'; import { NullLocation } from '../../../mol-model/location'; @@ -34,22 +33,22 @@ export interface TextureMesh { groupCount: number, readonly geoTextureDim: ValueCell<Vec2>, - /** texture has vertex positions in XYZ and group id in W */ - readonly vertexGroupTexture: ValueCell<Texture>, + readonly vertexTexture: ValueCell<Texture>, + readonly groupTexture: ValueCell<Texture>, readonly normalTexture: ValueCell<Texture>, readonly boundingSphere: Sphere3D } export namespace TextureMesh { - export function create(vertexCount: number, groupCount: number, vertexGroupTexture: Texture, normalTexture: Texture, boundingSphere: Sphere3D, textureMesh?: TextureMesh): TextureMesh { - const width = vertexGroupTexture.getWidth(); - const height = vertexGroupTexture.getHeight(); + export function create(vertexCount: number, groupCount: number, vertexTexture: Texture, groupTexture: Texture, normalTexture: Texture, boundingSphere: Sphere3D, textureMesh?: TextureMesh): TextureMesh { + const width = vertexTexture.getWidth(); + const height = vertexTexture.getHeight(); if (textureMesh) { textureMesh.vertexCount = vertexCount; textureMesh.groupCount = groupCount; ValueCell.update(textureMesh.geoTextureDim, Vec2.set(textureMesh.geoTextureDim.ref.value, width, height)); - ValueCell.update(textureMesh.vertexGroupTexture, vertexGroupTexture); + ValueCell.update(textureMesh.vertexTexture, vertexTexture); ValueCell.update(textureMesh.normalTexture, normalTexture); Sphere3D.copy(textureMesh.boundingSphere, boundingSphere); return textureMesh; @@ -59,7 +58,8 @@ export namespace TextureMesh { vertexCount, groupCount, geoTextureDim: ValueCell.create(Vec2.create(width, height)), - vertexGroupTexture: ValueCell.create(vertexGroupTexture), + vertexTexture: ValueCell.create(vertexTexture), + groupTexture: ValueCell.create(groupTexture), normalTexture: ValueCell.create(normalTexture), boundingSphere: Sphere3D.clone(boundingSphere), }; @@ -109,11 +109,10 @@ export namespace TextureMesh { return { uGeoTexDim: textureMesh.geoTextureDim, - tPositionGroup: textureMesh.vertexGroupTexture, + tPosition: textureMesh.vertexTexture, + tGroup: textureMesh.groupTexture, tNormal: textureMesh.normalTexture, - // aGroup is used as a vertex index here and the group id is retirieved from tPositionGroup - aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount))), boundingSphere: ValueCell.create(boundingSphere), invariantBoundingSphere: ValueCell.create(invariantBoundingSphere), uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)), @@ -148,11 +147,6 @@ export namespace TextureMesh { ValueCell.updateIfChanged(values.dFlipSided, props.flipSided); ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight); ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded); - - if (values.drawCount.ref.value > values.aGroup.ref.value.length) { - // console.log('updating vertex ids in aGroup to handle larger drawCount') - ValueCell.update(values.aGroup, fillSerial(new Float32Array(values.drawCount.ref.value))); - } } function updateBoundingSphere(values: TextureMeshValues, textureMesh: TextureMesh) { diff --git a/src/mol-gl/compute/histogram-pyramid/reduction.ts b/src/mol-gl/compute/histogram-pyramid/reduction.ts index de5caa40f50b3a6f648f157f1d8f0815125061d5..5d8054d2f6cc5f8bb316005adf6b4ea9d433a3a6 100644 --- a/src/mol-gl/compute/histogram-pyramid/reduction.ts +++ b/src/mol-gl/compute/histogram-pyramid/reduction.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { createComputeRenderable } from '../../renderable'; +import { createComputeRenderable, ComputeRenderable } from '../../renderable'; import { WebGLContext } from '../../webgl/context'; import { createComputeRenderItem } from '../../webgl/render-item'; import { Values, TextureSpec, UniformSpec } from '../../renderable/schema'; @@ -18,41 +18,46 @@ import { Framebuffer } from '../../../mol-gl/webgl/framebuffer'; import { isPowerOfTwo } from '../../../mol-math/misc'; import quad_vert from '../../../mol-gl/shader/quad.vert'; import reduction_frag from '../../../mol-gl/shader/histogram-pyramid/reduction.frag'; +import { isWebGL2 } from '../../webgl/compat'; const HistopyramidReductionSchema = { ...QuadSchema, + tInputLevel: TextureSpec('texture', 'rgba', 'float', 'nearest'), tPreviousLevel: TextureSpec('texture', 'rgba', 'float', 'nearest'), uSize: UniformSpec('f'), uTexSize: UniformSpec('f'), uFirst: UniformSpec('b'), }; +type HistopyramidReductionValues = Values<typeof HistopyramidReductionSchema> const HistogramPyramidName = 'histogram-pyramid'; -function getHistopyramidReductionRenderable(ctx: WebGLContext, initialTexture: Texture) { +function getHistopyramidReductionRenderable(ctx: WebGLContext, inputLevel: Texture, previousLevel: Texture): ComputeRenderable<HistopyramidReductionValues> { if (ctx.namedComputeRenderables[HistogramPyramidName]) { - const v = ctx.namedComputeRenderables[HistogramPyramidName].values; + const v = ctx.namedComputeRenderables[HistogramPyramidName].values as HistopyramidReductionValues; - ValueCell.update(v.tPreviousLevel, initialTexture); + ValueCell.update(v.tInputLevel, inputLevel); + ValueCell.update(v.tPreviousLevel, previousLevel); ctx.namedComputeRenderables[HistogramPyramidName].update(); } else { - ctx.namedComputeRenderables[HistogramPyramidName] = createHistopyramidReductionRenderable(ctx, initialTexture); + ctx.namedComputeRenderables[HistogramPyramidName] = createHistopyramidReductionRenderable(ctx, inputLevel, previousLevel); } return ctx.namedComputeRenderables[HistogramPyramidName]; } -function createHistopyramidReductionRenderable(ctx: WebGLContext, initialTexture: Texture) { - const values: Values<typeof HistopyramidReductionSchema> = { +function createHistopyramidReductionRenderable(ctx: WebGLContext, inputLevel: Texture, previousLevel: Texture) { + const values: HistopyramidReductionValues = { ...QuadValues, - tPreviousLevel: ValueCell.create(initialTexture), + tInputLevel: ValueCell.create(inputLevel), + tPreviousLevel: ValueCell.create(previousLevel), uSize: ValueCell.create(0), uTexSize: ValueCell.create(0), uFirst: ValueCell.create(true), }; const schema = { ...HistopyramidReductionSchema }; - const shaderCode = ShaderCode('reduction', quad_vert, reduction_frag); + const shaderCode = ShaderCode('reduction', quad_vert, reduction_frag, {}, { 0: 'ivec4' }); const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values); return createComputeRenderable(renderItem, values); @@ -64,11 +69,13 @@ function getLevelTextureFramebuffer(ctx: WebGLContext, level: number) { let textureFramebuffer = LevelTexturesFramebuffers[level]; const size = Math.pow(2, level); if (textureFramebuffer === undefined) { - const texture = getTexture(`level${level}`, ctx, 'image-float32', 'rgba', 'float', 'nearest'); + const texture = ctx.isWebGL2 + ? getTexture(`level${level}`, ctx, 'image-int32', 'alpha', 'int', 'nearest') + : getTexture(`level${level}`, ctx, 'image-uint8', 'rgba', 'ubyte', 'nearest'); + texture.define(size, size); const framebuffer = getFramebuffer(`level${level}`, ctx); texture.attachFramebuffer(framebuffer, 0); textureFramebuffer = { texture, framebuffer }; - textureFramebuffer.texture.define(size, size); LevelTexturesFramebuffers[level] = textureFramebuffer; } return textureFramebuffer; @@ -122,20 +129,29 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, // This part set the levels const levels = Math.ceil(Math.log(w) / Math.log(2)); const maxSize = Math.pow(2, levels); - // console.log('levels', levels, 'maxSize', maxSize, 'input', w); + const maxSizeX = Math.pow(2, levels); + const maxSizeY = Math.pow(2, levels - 1); + // console.log('levels', levels, 'maxSize', maxSize, [maxSizeX, maxSizeY], 'input', w); - const pyramidTex = getTexture('pyramid', ctx, 'image-float32', 'rgba', 'float', 'nearest'); - pyramidTex.define(maxSize, maxSize); + const pyramidTex = ctx.isWebGL2 + ? getTexture('pyramid', ctx, 'image-int32', 'alpha', 'int', 'nearest') + : getTexture('pyramid', ctx, 'image-uint8', 'rgba', 'ubyte', 'nearest'); + pyramidTex.define(maxSizeX, maxSizeY); const framebuffer = getFramebuffer('pyramid', ctx); pyramidTex.attachFramebuffer(framebuffer, 0); - gl.viewport(0, 0, maxSize, maxSize); - gl.clear(gl.COLOR_BUFFER_BIT); + + gl.viewport(0, 0, maxSizeX, maxSizeY); + if (isWebGL2(gl)) { + gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]); + } else { + gl.clear(gl.COLOR_BUFFER_BIT); + } const levelTexturesFramebuffers: TextureFramebuffer[] = []; for (let i = 0; i < levels; ++i) levelTexturesFramebuffers.push(getLevelTextureFramebuffer(ctx, i)); - const renderable = getHistopyramidReductionRenderable(ctx, inputTexture); + const renderable = getHistopyramidReductionRenderable(ctx, inputTexture, levelTexturesFramebuffers[0].texture); ctx.state.currentRenderItemId = -1; setRenderingDefaults(ctx); @@ -146,7 +162,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, tf.framebuffer.bind(); const size = Math.pow(2, currLevel); - // console.log('size', size, 'draw-level', currLevel, 'read-level', levels - i) + // console.log('size', size, 'draw-level', currLevel, 'read-level', levels - i); ValueCell.update(renderable.values.uSize, Math.pow(2, i + 1) / maxSize); ValueCell.update(renderable.values.uTexSize, size); @@ -158,7 +174,11 @@ 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); + if (isWebGL2(gl)) { + gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]); + } else { + gl.clear(gl.COLOR_BUFFER_BIT); + } gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]); renderable.render(); @@ -179,7 +199,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, const count = Math.max(1, getHistopyramidSum(ctx, levelTexturesFramebuffers[0].texture)); const height = Math.ceil(count / Math.pow(2, levels)); // const scale = Vec2.create(maxSize / inputTexture.width, maxSize / inputTexture.height); - // console.log('height', height, 'finalCount', finalCount, 'scale', scale); + // console.log('height', height, 'finalCount', count, 'scale', scale); return { pyramidTex, count, height, levels, scale }; } \ No newline at end of file diff --git a/src/mol-gl/compute/histogram-pyramid/sum.ts b/src/mol-gl/compute/histogram-pyramid/sum.ts index 6461df6ca112872c4539f9b838e4738094492ff6..a20d779a78ee205637196263600f81c56a9cf422 100644 --- a/src/mol-gl/compute/histogram-pyramid/sum.ts +++ b/src/mol-gl/compute/histogram-pyramid/sum.ts @@ -1,10 +1,10 @@ /** - * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { createComputeRenderable } from '../../renderable'; +import { ComputeRenderable, createComputeRenderable } from '../../renderable'; import { WebGLContext } from '../../webgl/context'; import { createComputeRenderItem } from '../../webgl/render-item'; import { Values, TextureSpec } from '../../renderable/schema'; @@ -15,17 +15,19 @@ import { decodeFloatRGB } from '../../../mol-util/float-packing'; import { QuadSchema, QuadValues } from '../util'; import quad_vert from '../../../mol-gl/shader/quad.vert'; import sum_frag from '../../../mol-gl/shader/histogram-pyramid/sum.frag'; +import { isWebGL2 } from '../../webgl/compat'; const HistopyramidSumSchema = { ...QuadSchema, tTexture: TextureSpec('texture', 'rgba', 'float', 'nearest'), }; +type HistopyramidSumValues = Values<typeof HistopyramidSumSchema> const HistopyramidSumName = 'histopyramid-sum'; -function getHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) { +function getHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture): ComputeRenderable<HistopyramidSumValues> { if (ctx.namedComputeRenderables[HistopyramidSumName]) { - const v = ctx.namedComputeRenderables[HistopyramidSumName].values; + const v = ctx.namedComputeRenderables[HistopyramidSumName].values as HistopyramidSumValues; ValueCell.update(v.tTexture, texture); @@ -37,13 +39,13 @@ function getHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) { } function createHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) { - const values: Values<typeof HistopyramidSumSchema> = { + const values: HistopyramidSumValues = { ...QuadValues, tTexture: ValueCell.create(texture), }; const schema = { ...HistopyramidSumSchema }; - const shaderCode = ShaderCode('sum', quad_vert, sum_frag); + const shaderCode = ShaderCode('sum', quad_vert, sum_frag, {}, { 0: 'ivec4' }); const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values); return createComputeRenderable(renderItem, values); @@ -60,7 +62,9 @@ function setRenderingDefaults(ctx: WebGLContext) { state.clearColor(0, 0, 0, 0); } -const sumArray = new Uint8Array(4); +const sumBytes = new Uint8Array(4); +const sumInts = new Int32Array(4); + export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) { const { gl, resources } = ctx; @@ -73,7 +77,9 @@ export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture const framebuffer = ctx.namedFramebuffers[HistopyramidSumName]; if (!ctx.namedTextures[HistopyramidSumName]) { - ctx.namedTextures[HistopyramidSumName] = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest'); + ctx.namedTextures[HistopyramidSumName] = isWebGL2(gl) + ? resources.texture('image-int32', 'rgba', 'int', 'nearest') + : resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest'); ctx.namedTextures[HistopyramidSumName].define(1, 1); } const sumTexture = ctx.namedTextures[HistopyramidSumName]; @@ -84,8 +90,11 @@ export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture gl.viewport(0, 0, 1, 1); renderable.render(); gl.finish(); - ctx.readPixels(0, 0, 1, 1, sumArray); + + ctx.readPixels(0, 0, 1, 1, isWebGL2(gl) ? sumInts : sumBytes); ctx.unbindFramebuffer(); - return decodeFloatRGB(sumArray[0], sumArray[1], sumArray[2]); + return isWebGL2(gl) + ? sumInts[0] + : decodeFloatRGB(sumBytes[0], sumBytes[1], sumBytes[2]); } \ No newline at end of file diff --git a/src/mol-gl/compute/marching-cubes/active-voxels.ts b/src/mol-gl/compute/marching-cubes/active-voxels.ts index c22119b447755a0a079b58f96530ecbcfda0edf0..04fac2e39d44dd249c20c0b41b4032ebd3f9eff3 100644 --- a/src/mol-gl/compute/marching-cubes/active-voxels.ts +++ b/src/mol-gl/compute/marching-cubes/active-voxels.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { createComputeRenderable } from '../../renderable'; +import { ComputeRenderable, createComputeRenderable } from '../../renderable'; import { WebGLContext } from '../../webgl/context'; import { createComputeRenderItem } from '../../webgl/render-item'; import { Values, TextureSpec, UniformSpec } from '../../renderable/schema'; @@ -29,12 +29,13 @@ const ActiveVoxelsSchema = { uScale: UniformSpec('v2'), }; +type ActiveVoxelsValues = Values<typeof ActiveVoxelsSchema> const ActiveVoxelsName = 'active-voxels'; -function getActiveVoxelsRenderable(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, scale: Vec2) { +function getActiveVoxelsRenderable(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, scale: Vec2): ComputeRenderable<ActiveVoxelsValues> { if (ctx.namedComputeRenderables[ActiveVoxelsName]) { - const v = ctx.namedComputeRenderables[ActiveVoxelsName].values; + const v = ctx.namedComputeRenderables[ActiveVoxelsName].values as ActiveVoxelsValues; ValueCell.update(v.uQuadScale, scale); ValueCell.update(v.tVolumeData, volumeData); @@ -51,7 +52,7 @@ function getActiveVoxelsRenderable(ctx: WebGLContext, volumeData: Texture, gridD } function createActiveVoxelsRenderable(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, scale: Vec2) { - const values: Values<typeof ActiveVoxelsSchema> = { + const values: ActiveVoxelsValues = { ...QuadValues, tTriCount: ValueCell.create(getTriCount()), diff --git a/src/mol-gl/compute/marching-cubes/isosurface.ts b/src/mol-gl/compute/marching-cubes/isosurface.ts index 4217ab20e94d52459c1c3dcdc5c374d6dff129e0..994c55bfd04992d5627435730e98f67061f785ea 100644 --- a/src/mol-gl/compute/marching-cubes/isosurface.ts +++ b/src/mol-gl/compute/marching-cubes/isosurface.ts @@ -4,19 +4,21 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { createComputeRenderable } from '../../renderable'; +import { ComputeRenderable, createComputeRenderable } from '../../renderable'; import { WebGLContext } from '../../webgl/context'; import { createComputeRenderItem } from '../../webgl/render-item'; -import { Values, TextureSpec, UniformSpec } from '../../renderable/schema'; +import { Values, TextureSpec, UniformSpec, DefineSpec } from '../../renderable/schema'; import { Texture } from '../../../mol-gl/webgl/texture'; import { ShaderCode } from '../../../mol-gl/shader-code'; import { ValueCell } from '../../../mol-util'; import { Vec3, Vec2, Mat4 } from '../../../mol-math/linear-algebra'; import { QuadSchema, QuadValues } from '../util'; -import { HistogramPyramid } from '../histogram-pyramid/reduction'; +import { createHistogramPyramid, HistogramPyramid } from '../histogram-pyramid/reduction'; import { getTriIndices } from './tables'; import quad_vert from '../../../mol-gl/shader/quad.vert'; import isosurface_frag from '../../../mol-gl/shader/marching-cubes/isosurface.frag'; +import { calcActiveVoxels } from './active-voxels'; +import { isWebGL2 } from '../../webgl/compat'; const IsosurfaceSchema = { ...QuadSchema, @@ -34,15 +36,17 @@ const IsosurfaceSchema = { uGridDim: UniformSpec('v3'), uGridTexDim: UniformSpec('v3'), uGridTransform: UniformSpec('m4'), - uScale: UniformSpec('v2'), + + dPackedGroup: DefineSpec('boolean') }; +type IsosurfaceValues = Values<typeof IsosurfaceSchema> const IsosurfaceName = 'isosurface'; -function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, height: number) { +function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, packedGroup: boolean): ComputeRenderable<IsosurfaceValues> { if (ctx.namedComputeRenderables[IsosurfaceName]) { - const v = ctx.namedComputeRenderables[IsosurfaceName].values; + const v = ctx.namedComputeRenderables[IsosurfaceName].values as IsosurfaceValues; ValueCell.update(v.tActiveVoxelsPyramid, activeVoxelsPyramid); ValueCell.update(v.tActiveVoxelsBase, activeVoxelsBase); @@ -58,16 +62,18 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture ValueCell.update(v.uGridTransform, transform); ValueCell.update(v.uScale, scale); + ValueCell.update(v.dPackedGroup, packedGroup); + ctx.namedComputeRenderables[IsosurfaceName].update(); } else { - ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, height); + ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, packedGroup); } return ctx.namedComputeRenderables[IsosurfaceName]; } -function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, height: number) { +function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, packedGroup: boolean) { // console.log('uSize', Math.pow(2, levels)) - const values: Values<typeof IsosurfaceSchema> = { + const values: IsosurfaceValues = { ...QuadValues, tTriIndices: ValueCell.create(getTriIndices()), @@ -84,6 +90,8 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text uGridTexDim: ValueCell.create(gridTexDim), uGridTransform: ValueCell.create(transform), uScale: ValueCell.create(scale), + + dPackedGroup: ValueCell.create(packedGroup) }; const schema = { ...IsosurfaceSchema }; @@ -104,65 +112,72 @@ function setRenderingDefaults(ctx: WebGLContext) { state.clearColor(0, 0, 0, 0); } -export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, vertexGroupTexture?: Texture, normalTexture?: Texture) { - const { gl, resources } = ctx; +export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) { + const { gl, resources, extensions } = ctx; const { pyramidTex, height, levels, scale, count } = histogramPyramid; const width = pyramidTex.getWidth(); - // console.log('iso', 'gridDim', gridDim, 'scale', scale, 'gridTexDim', gridTexDim) - // console.log('iso volumeData', volumeData) + // console.log('width', width, 'height', height); + // console.log('iso', 'gridDim', gridDim, 'scale', scale, 'gridTexDim', gridTexDim); + // console.log('iso volumeData', volumeData); if (!ctx.namedFramebuffers[IsosurfaceName]) { ctx.namedFramebuffers[IsosurfaceName] = resources.framebuffer(); } const framebuffer = ctx.namedFramebuffers[IsosurfaceName]; - if (!vertexGroupTexture) { - vertexGroupTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest'); - } - vertexGroupTexture.define(width, height); - - if (!normalTexture) { - normalTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest'); - } - normalTexture.define(width, height); + if (isWebGL2(gl)) { + if (!vertexTexture) { + vertexTexture = extensions.colorBufferHalfFloat && extensions.textureHalfFloat + ? resources.texture('image-float16', 'rgba', 'fp16', 'nearest') + : resources.texture('image-float32', 'rgba', 'float', 'nearest'); + } + + if (!groupTexture) { + groupTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest'); + } + + if (!normalTexture) { + normalTexture = extensions.colorBufferHalfFloat && extensions.textureHalfFloat + ? resources.texture('image-float16', 'rgba', 'fp16', 'nearest') + : resources.texture('image-float32', 'rgba', 'float', 'nearest'); + } + } else { + // in webgl1 drawbuffers must be in the same format for some reason + // this is quite wasteful but good enough for medium size meshes - // const infoTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') - // infoTex.define(pyramidTex.width, pyramidTex.height) + if (!vertexTexture) { + vertexTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest'); + } - // const pointTexA = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') - // pointTexA.define(pyramidTex.width, pyramidTex.height) + if (!groupTexture) { + groupTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest'); + } - // const pointTexB = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') - // pointTexB.define(pyramidTex.width, pyramidTex.height) + if (!normalTexture) { + normalTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest'); + } + } - // const coordTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') - // coordTex.define(pyramidTex.width, pyramidTex.height) + vertexTexture.define(width, height); + groupTexture.define(width, height); + normalTexture.define(width, height); - // const indexTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest') - // indexTex.define(pyramidTex.width, pyramidTex.height) + vertexTexture.attachFramebuffer(framebuffer, 0); + groupTexture.attachFramebuffer(framebuffer, 1); + normalTexture.attachFramebuffer(framebuffer, 2); - const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, height); + const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, packedGroup); ctx.state.currentRenderItemId = -1; - vertexGroupTexture.attachFramebuffer(framebuffer, 0); - normalTexture.attachFramebuffer(framebuffer, 1); - // infoTex.attachFramebuffer(framebuffer, 1) - // pointTexA.attachFramebuffer(framebuffer, 2) - // pointTexB.attachFramebuffer(framebuffer, 3) - // coordTex.attachFramebuffer(framebuffer, 4) - // indexTex.attachFramebuffer(framebuffer, 5) - const { drawBuffers } = ctx.extensions; if (!drawBuffers) throw new Error('need WebGL draw buffers'); + framebuffer.bind(); drawBuffers.drawBuffers([ drawBuffers.COLOR_ATTACHMENT0, drawBuffers.COLOR_ATTACHMENT1, - // drawBuffers.COLOR_ATTACHMENT2, - // drawBuffers.COLOR_ATTACHMENT3, - // drawBuffers.COLOR_ATTACHMENT4, - // drawBuffers.COLOR_ATTACHMENT5 + drawBuffers.COLOR_ATTACHMENT2, ]); setRenderingDefaults(ctx); @@ -172,45 +187,26 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex gl.flush(); - // const vgt = readTexture(ctx, vertexGroupTexture, pyramidTex.width, height) - // console.log('vertexGroupTexture', vgt.array.subarray(0, 4 * count)) - - // const vt = readTexture(ctx, verticesTex, pyramidTex.width, height) - // console.log('vt', vt) - // const vertices = new Float32Array(3 * compacted.count) - // for (let i = 0; i < compacted.count; ++i) { - // vertices[i * 3] = vt.array[i * 4] - // vertices[i * 3 + 1] = vt.array[i * 4 + 1] - // vertices[i * 3 + 2] = vt.array[i * 4 + 2] - // } - // console.log('vertices', vertices) - - // const it = readTexture(ctx, infoTex, pyramidTex.width, height) - // console.log('info', it.array.subarray(0, 4 * compacted.count)) - - // const pat = readTexture(ctx, pointTexA, pyramidTex.width, height) - // console.log('point a', pat.array.subarray(0, 4 * compacted.count)) - - // const pbt = readTexture(ctx, pointTexB, pyramidTex.width, height) - // console.log('point b', pbt.array.subarray(0, 4 * compacted.count)) + return { vertexTexture, groupTexture, normalTexture, vertexCount: count }; +} - // const ct = readTexture(ctx, coordTex, pyramidTex.width, height) - // console.log('coord', ct.array.subarray(0, 4 * compacted.count)) +// - // const idxt = readTexture(ctx, indexTex, pyramidTex.width, height) - // console.log('index', idxt.array.subarray(0, 4 * compacted.count)) +export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, packedGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) { + // console.time('calcActiveVoxels'); + const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale); + // ctx.webgl.waitForGpuCommandsCompleteSync(); + // console.timeEnd('calcActiveVoxels'); - // const { field, idField } = await fieldFromTexture2d(ctx, volumeData, gridDimensions) - // console.log({ field, idField }) + // console.time('createHistogramPyramid'); + const compacted = createHistogramPyramid(ctx, activeVoxelsTex, gridTexScale, gridTexDim); + // ctx.webgl.waitForGpuCommandsCompleteSync(); + // console.timeEnd('createHistogramPyramid'); - // const valuesA = new Float32Array(compacted.count) - // const valuesB = new Float32Array(compacted.count) - // for (let i = 0; i < compacted.count; ++i) { - // valuesA[i] = field.space.get(field.data, pat.array[i * 4], pat.array[i * 4 + 1], pat.array[i * 4 + 2]) - // valuesB[i] = field.space.get(field.data, pbt.array[i * 4], pbt.array[i * 4 + 1], pbt.array[i * 4 + 2]) - // } - // console.log('valuesA', valuesA) - // console.log('valuesB', valuesB) + // console.time('createIsosurfaceBuffers'); + const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, packedGroup, vertexTexture, groupTexture, normalTexture); + // ctx.webgl.waitForGpuCommandsCompleteSync(); + // console.timeEnd('createIsosurfaceBuffers'); - return { vertexGroupTexture, normalTexture, vertexCount: count }; + return gv; } \ No newline at end of file diff --git a/src/mol-gl/renderable/texture-mesh.ts b/src/mol-gl/renderable/texture-mesh.ts index b6eb7fc62cd8c8c3bf9e497192e1485a9c31f4d8..66c5308fb59d94ffc337afccbefd47f8462b4356 100644 --- a/src/mol-gl/renderable/texture-mesh.ts +++ b/src/mol-gl/renderable/texture-mesh.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -7,17 +7,16 @@ import { Renderable, RenderableState, createRenderable } from '../renderable'; import { WebGLContext } from '../webgl/context'; import { createGraphicsRenderItem } from '../webgl/render-item'; -import { GlobalUniformSchema, BaseSchema, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec, GlobalTextureSchema, AttributeSpec } from './schema'; +import { GlobalUniformSchema, BaseSchema, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec, GlobalTextureSchema } from './schema'; import { MeshShaderCode } from '../shader-code'; import { ValueCell } from '../../mol-util'; export const TextureMeshSchema = { ...BaseSchema, - aGroup: AttributeSpec('float32', 1, 0), uGeoTexDim: UniformSpec('v2'), - /** texture has vertex positions in XYZ and group id in W */ - tPositionGroup: TextureSpec('texture', 'rgba', 'float', 'nearest'), - tNormal: TextureSpec('texture', 'rgba', 'float', 'nearest'), + tPosition: TextureSpec('texture', 'rgb', 'float', 'nearest'), + tGroup: TextureSpec('texture', 'alpha', 'float', 'nearest'), + tNormal: TextureSpec('texture', 'rgb', 'float', 'nearest'), dFlatShaded: DefineSpec('boolean'), dDoubleSided: DefineSpec('boolean'), diff --git a/src/mol-gl/shader/chunks/assign-color-varying.glsl.ts b/src/mol-gl/shader/chunks/assign-color-varying.glsl.ts index a78c62cf64b924ec8b313f99881e9874c21eb89f..14717342505408ce3ca032b4b638c59de74b80ac 100644 --- a/src/mol-gl/shader/chunks/assign-color-varying.glsl.ts +++ b/src/mol-gl/shader/chunks/assign-color-varying.glsl.ts @@ -9,9 +9,9 @@ export default ` #elif defined(dColorType_groupInstance) vColor.rgb = readFromTexture(tColor, aInstance * float(uGroupCount) + group, uColorTexDim).rgb; #elif defined(dColorType_vertex) - vColor.rgb = readFromTexture(tColor, aVertex, uColorTexDim).rgb; + vColor.rgb = readFromTexture(tColor, VertexID, uColorTexDim).rgb; #elif defined(dColorType_vertexInstance) - vColor.rgb = readFromTexture(tColor, aInstance * float(uVertexCount) + aVertex, uColorTexDim).rgb; + vColor.rgb = readFromTexture(tColor, int(aInstance) * uVertexCount + VertexID, uColorTexDim).rgb; #endif #ifdef dOverpaint diff --git a/src/mol-gl/shader/chunks/assign-group.glsl.ts b/src/mol-gl/shader/chunks/assign-group.glsl.ts index 9af3e1dbdd950c4fb4462de893b3ea3437074d8f..dc1a163693a5c4e542e951214d494f9bb177ebd4 100644 --- a/src/mol-gl/shader/chunks/assign-group.glsl.ts +++ b/src/mol-gl/shader/chunks/assign-group.glsl.ts @@ -1,7 +1,6 @@ export default ` #ifdef dGeoTexture - // aGroup is used as a vertex index here and the group id is retirieved from tPositionGroup - float group = readFromTexture(tPositionGroup, aGroup, uGeoTexDim).w; + float group = decodeFloatRGB(readFromTexture(tGroup, VertexID, uGeoTexDim).rgb); #else float group = aGroup; #endif diff --git a/src/mol-gl/shader/chunks/assign-position.glsl.ts b/src/mol-gl/shader/chunks/assign-position.glsl.ts index 7cb5bcac0f33605c142e1aa22030cc4a219e7ee5..14a164dece50711c57e1c898e1d0c3588778d5df 100644 --- a/src/mol-gl/shader/chunks/assign-position.glsl.ts +++ b/src/mol-gl/shader/chunks/assign-position.glsl.ts @@ -2,7 +2,7 @@ export default ` mat4 model = uModel * aTransform; mat4 modelView = uView * model; #ifdef dGeoTexture - vec3 position = readFromTexture(tPositionGroup, aGroup, uGeoTexDim).xyz; + vec3 position = readFromTexture(tPosition, VertexID, uGeoTexDim).xyz; #else vec3 position = aPosition; #endif diff --git a/src/mol-gl/shader/chunks/color-vert-params.glsl.ts b/src/mol-gl/shader/chunks/color-vert-params.glsl.ts index 3cc4112782ce1d0eb94e22af02756248678dd652..06c6cedb807b02b7006768703daf6acc188c9852 100644 --- a/src/mol-gl/shader/chunks/color-vert-params.glsl.ts +++ b/src/mol-gl/shader/chunks/color-vert-params.glsl.ts @@ -11,14 +11,6 @@ export default ` uniform sampler2D tColor; #endif - #if defined(dColorType_vertex) || defined(dColorType_vertexInstance) - #if __VERSION__ == 100 - attribute float aVertex; - #else - #define aVertex float(gl_VertexID) - #endif - #endif - #ifdef dOverpaint varying vec4 vOverpaint; uniform vec2 uOverpaintTexDim; diff --git a/src/mol-gl/shader/chunks/common-vert-params.glsl.ts b/src/mol-gl/shader/chunks/common-vert-params.glsl.ts index 965f46a0d6cf84eacbeb0dff2480d4f1e3be27a2..9ee8e3d95f02d19a6a9bd3a040b6a2539c031563 100644 --- a/src/mol-gl/shader/chunks/common-vert-params.glsl.ts +++ b/src/mol-gl/shader/chunks/common-vert-params.glsl.ts @@ -36,4 +36,11 @@ uniform sampler2D tMarker; varying vec3 vModelPosition; varying vec3 vViewPosition; + +#if __VERSION__ == 100 + attribute float aVertex; + #define VertexID int(aVertex) +#else + #define VertexID gl_VertexID +#endif `; \ No newline at end of file diff --git a/src/mol-gl/shader/chunks/common.glsl.ts b/src/mol-gl/shader/chunks/common.glsl.ts index 1786927b9995637d10da794477ad012b4b4fd4e6..1d64760ef26b09366119cce8cbf7302d39ec188a 100644 --- a/src/mol-gl/shader/chunks/common.glsl.ts +++ b/src/mol-gl/shader/chunks/common.glsl.ts @@ -28,6 +28,7 @@ export default ` float intDiv(const in float a, const in float b) { return float(int(a) / int(b)); } vec2 ivec2Div(const in vec2 a, const in vec2 b) { return vec2(ivec2(a) / ivec2(b)); } float intMod(const in float a, const in float b) { return a - b * float(int(a) / int(b)); } +int imod(const in int a, const in int b) { return a - b * (a / b); } float pow2(const in float x) { return x * x; } diff --git a/src/mol-gl/shader/chunks/read-from-texture.glsl.ts b/src/mol-gl/shader/chunks/read-from-texture.glsl.ts index 4e97075a1c2bead711b00ac4c8db8d4733df65fd..8d7c6184c1c375cdf6e70815700e1a77850d885a 100644 --- a/src/mol-gl/shader/chunks/read-from-texture.glsl.ts +++ b/src/mol-gl/shader/chunks/read-from-texture.glsl.ts @@ -5,10 +5,17 @@ */ export default ` -vec4 readFromTexture (const in sampler2D tex, const in float i, const in vec2 dim) { +vec4 readFromTexture(const in sampler2D tex, const in float i, const in vec2 dim) { float x = intMod(i, dim.x); float y = floor(intDiv(i, dim.x)); vec2 uv = (vec2(x, y) + 0.5) / dim; return texture2D(tex, uv); } + +vec4 readFromTexture(const in sampler2D tex, const in int i, const in vec2 dim) { + int x = imod(i, int(dim.x)); + int y = i / int(dim.x); + vec2 uv = (vec2(x, y) + 0.5) / dim; + return texture2D(tex, uv); +} `; \ No newline at end of file diff --git a/src/mol-gl/shader/histogram-pyramid/reduction.frag.ts b/src/mol-gl/shader/histogram-pyramid/reduction.frag.ts index d7f44c82e3702c6de69e5c69ee55e7e6a4409f86..867168c913b02a8ff7d67832a98cc753ceac299f 100644 --- a/src/mol-gl/shader/histogram-pyramid/reduction.frag.ts +++ b/src/mol-gl/shader/histogram-pyramid/reduction.frag.ts @@ -1,35 +1,59 @@ export default ` precision highp float; +precision highp int; precision highp sampler2D; -// input texture (previous level used to evaluate the new level) -uniform sampler2D tPreviousLevel; +uniform sampler2D tInputLevel; + +// previous level used to evaluate the new level +#if __VERSION__ == 100 + uniform sampler2D tPreviousLevel; +#else + precision highp isampler2D; + uniform isampler2D tPreviousLevel; +#endif // inverted size of the previous level texture. uniform float uSize; uniform float uTexSize; uniform bool uFirst; +#include common + void main(void) { float k = 0.5 * uSize; vec2 position = floor((gl_FragCoord.xy / uTexSize) / uSize) * uSize; - float a, b, c, d; - - if (uFirst) { - a = texture2D(tPreviousLevel, position).r * 255.0; - b = texture2D(tPreviousLevel, position + vec2(k, 0.0)).r * 255.0; - c = texture2D(tPreviousLevel, position + vec2(0.0, k)).r * 255.0; - d = texture2D(tPreviousLevel, position + vec2(k, k)).r * 255.0; - } else { - a = texture2D(tPreviousLevel, position).r; - b = texture2D(tPreviousLevel, position + vec2(k, 0.0)).r; - c = texture2D(tPreviousLevel, position + vec2(0.0, k)).r; - d = texture2D(tPreviousLevel, position + vec2(k, k)).r; - } - - gl_FragColor.a = a; - gl_FragColor.b = a + b; - gl_FragColor.g = gl_FragColor.b + c; - gl_FragColor.r = gl_FragColor.g + d; + + #if __VERSION__ == 100 + float a, b, c, d; + + if (uFirst) { + a = texture2D(tInputLevel, position).r * 255.0; + b = texture2D(tInputLevel, position + vec2(k, 0.0)).r * 255.0; + c = texture2D(tInputLevel, position + vec2(0.0, k)).r * 255.0; + d = texture2D(tInputLevel, position + vec2(k, k)).r * 255.0; + } else { + a = decodeFloatRGB(texture2D(tPreviousLevel, position).rgb); + b = decodeFloatRGB(texture2D(tPreviousLevel, position + vec2(k, 0.0)).rgb); + c = decodeFloatRGB(texture2D(tPreviousLevel, position + vec2(0.0, k)).rgb); + d = decodeFloatRGB(texture2D(tPreviousLevel, position + vec2(k, k)).rgb); + } + gl_FragColor = vec4(encodeFloatRGB(a + b + c + d), 1.0); + #else + int a, b, c, d; + + if (uFirst) { + a = int(texture2D(tInputLevel, position).r * 255.0); + b = int(texture2D(tInputLevel, position + vec2(k, 0.0)).r * 255.0); + c = int(texture2D(tInputLevel, position + vec2(0.0, k)).r * 255.0); + d = int(texture2D(tInputLevel, position + vec2(k, k)).r * 255.0); + } else { + a = texture2D(tPreviousLevel, position).r; + b = texture2D(tPreviousLevel, position + vec2(k, 0.0)).r; + c = texture2D(tPreviousLevel, position + vec2(0.0, k)).r; + d = texture2D(tPreviousLevel, position + vec2(k, k)).r; + } + gl_FragColor = ivec4(a + b + c + d); + #endif } `; \ No newline at end of file diff --git a/src/mol-gl/shader/histogram-pyramid/sum.frag.ts b/src/mol-gl/shader/histogram-pyramid/sum.frag.ts index fbe31eccc5d9d083c1d4ff0a7cbba15e6286195d..7760ee52306d7f1b12e77de143c35beaee5ec098 100644 --- a/src/mol-gl/shader/histogram-pyramid/sum.frag.ts +++ b/src/mol-gl/shader/histogram-pyramid/sum.frag.ts @@ -1,18 +1,26 @@ /** - * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ export default ` precision highp float; -precision highp sampler2D; +precision highp int; -uniform sampler2D tTexture; - -#include common +#if __VERSION__ == 100 + precision highp sampler2D; + uniform sampler2D tTexture; +#else + precision highp isampler2D; + uniform isampler2D tTexture; +#endif void main(void) { - gl_FragColor = vec4(encodeFloatRGB(texture2D(tTexture, vec2(0.5)).r), 1.0); + #if __VERSION__ == 100 + gl_FragColor = texture2D(tTexture, vec2(0.5)); + #else + gl_FragColor = ivec4(texture2D(tTexture, vec2(0.5)).r); + #endif } `; \ No newline at end of file diff --git a/src/mol-gl/shader/marching-cubes/isosurface.frag.ts b/src/mol-gl/shader/marching-cubes/isosurface.frag.ts index e29802d761f3c799c2ef7ae2ed305c770e436edb..75d5f3a610a35a11f97e5c09687bf37c0068d5f7 100644 --- a/src/mol-gl/shader/marching-cubes/isosurface.frag.ts +++ b/src/mol-gl/shader/marching-cubes/isosurface.frag.ts @@ -3,7 +3,13 @@ precision highp float; precision highp int; precision highp sampler2D; -uniform sampler2D tActiveVoxelsPyramid; +#if __VERSION__ == 100 + uniform sampler2D tActiveVoxelsPyramid; +#else + precision highp isampler2D; + uniform isampler2D tActiveVoxelsPyramid; +#endif + uniform sampler2D tActiveVoxelsBase; uniform sampler2D tVolumeData; uniform sampler2D tTriIndices; @@ -52,65 +58,114 @@ vec4 voxel(vec3 pos) { return texture3dFrom2dNearest(tVolumeData, pos / uGridDim, uGridDim, uGridTexDim.xy); } -vec4 voxel2(vec3 pos) { - pos = min(max(vec3(0.0), pos), uGridDim - vec3(vec2(2.0), 1.0)); +vec4 voxelPadded(vec3 pos) { + pos = min(max(vec3(0.0), pos), uGridDim - vec3(vec2(2.0), 1.0)); // remove xy padding return texture3dFrom2dNearest(tVolumeData, pos / uGridDim, uGridDim, uGridTexDim.xy); } +int idot2(const in ivec2 a, const in ivec2 b) { + return a.x * b.x + a.y * b.y; +} + +int idot4(const in ivec4 a, const in ivec4 b) { + return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; +} + +#if __VERSION__ == 100 + int pyramidVoxel(vec2 pos) { + return int(decodeFloatRGB(texture2D(tActiveVoxelsPyramid, pos / (vec2(1.0, 0.5) * uSize)).rgb)); + } +#else + int pyramidVoxel(vec2 pos) { + return texture2D(tActiveVoxelsPyramid, pos / (vec2(1.0, 0.5) * uSize)).r; + } +#endif + +vec4 baseVoxel(vec2 pos) { + return texture2D(tActiveVoxelsBase, pos / uSize); +} + void main(void) { // get 1D index - float vI = dot(floor(uSize * (gl_FragCoord.xy / uSize)), vec2(1.0, uSize)); + int vI = int(gl_FragCoord.x) + int(gl_FragCoord.y) * int(uSize); // ignore 1D indices outside of the grid - if(vI >= uCount) discard; - - float offset = uSize - 2.; - float k = 1. / uSize; - - vec2 relativePosition = k * vec2(offset, 0.); - vec4 partialSums = texture2D(tActiveVoxelsPyramid, relativePosition); - float start = 0.; - vec4 starts = vec4(0.); - vec4 ends = vec4(0.); - float diff = 2.; - vec4 m = vec4(0.); - vec2 position = vec2(0.); - vec4 vI4 = vec4(vI); + if(vI >= int(uCount)) discard; + + ivec2 offset = ivec2(int(uSize) - 2, 0); + + int start = 0; + ivec4 starts = ivec4(0); + ivec4 ends = ivec4(0); + int diff = 2; + ivec4 m = ivec4(0); + ivec2 position = ivec2(0); + ivec4 vI4 = ivec4(vI); + + ivec2 relativePosition = ivec2(0); + int end = 0; + ivec2 pos1 = ivec2(0); + ivec2 pos2 = ivec2(0); + ivec2 pos3 = ivec2(0); + ivec2 pos4 = ivec2(0); + ivec3 vI3 = ivec3(vI); + ivec3 mask = ivec3(0); // traverse the different levels of the pyramid for(int i = 1; i < 14; i++) { if(float(i) >= uLevels) break; - offset -= diff; - diff *= 2.; - relativePosition = position + k * vec2(offset, 0.); - - ends = partialSums.wzyx + vec4(start); - starts = vec4(start, ends.xyz); - m = vec4(greaterThanEqual(vI4, starts)) * vec4(lessThan(vI4, ends)); - relativePosition += m.y * vec2(k, 0.) + m.z * vec2(0., k) + m.w * vec2(k, k); - - start = dot(m, starts); - position = 2. * (relativePosition - k * vec2(offset, 0.)); - partialSums = texture2D(tActiveVoxelsPyramid, relativePosition); + offset.x -= diff; + diff *= 2; + relativePosition = position + offset; + + end = start + pyramidVoxel(vec2(relativePosition)); + pos1 = ivec2(relativePosition); + starts.x = start; + ends.x = end; + pos2 = ivec2(relativePosition + ivec2(1, 0)); + starts.y = ends.x; + ends.y = ends.x + pyramidVoxel(vec2(pos2)); + pos3 = relativePosition + ivec2(0, 1); + starts.z = ends.y; + ends.z = ends.y + pyramidVoxel(vec2(pos3)); + pos4 = relativePosition + ivec2(1, 1); + starts.w = ends.z; + mask = ivec3(greaterThanEqual(vI3, starts.rgb)) * ivec3(lessThan(vI3, ends.rgb)); + m = ivec4(mask, 1 - int(any(bvec3(mask)))); + + relativePosition = m.x * pos1 + m.y * pos2 + m.z * pos3 + m.w * pos4; + start = idot4(m, starts); + position = 2 * (relativePosition - offset); } - ends = partialSums.wzyx + vec4(start); - starts = vec4(start, ends.xyz); - m = vec4(greaterThanEqual(vI4, starts)) * vec4(lessThan(vI4, ends)); - position += m.y * vec2(k, 0.) + m.z * vec2(0., k) + m.w * vec2(k, k); - - vec2 coord2d = position / uScale; + end = start + int(baseVoxel(vec2(position)).r * 255.0); + pos1 = position; + starts.x = start; + ends.x = end; + pos2 = position + ivec2(1, 0); + starts.y = ends.x; + ends.y = ends.x + int(baseVoxel(vec2(pos2)).r * 255.0); + pos3 = position + ivec2(0, 1); + starts.z = ends.y; + ends.z = ends.y + int(baseVoxel(vec2(pos3)).r * 255.0); + pos4 = position + ivec2(1, 1); + starts.w = ends.z; + mask = ivec3(greaterThanEqual(vI3, starts.rgb)) * ivec3(lessThan(vI3, ends.rgb)); + m = ivec4(mask, 1 - int(any(bvec3(mask)))); + position = m.x * pos1 + m.y * pos2 + m.z * pos3 + m.w * pos4; + + vec2 coord2d = (vec2(position) / uSize) / uScale; vec3 coord3d = floor(index3dFrom2d(coord2d) + 0.5); - float edgeIndex = floor(texture2D(tActiveVoxelsBase, position).a * 255.0 + 0.5); + float edgeIndex = floor(baseVoxel(vec2(position)).a * 255.0 + 0.5); // current vertex for the up to 15 MC cases - float currentVertex = vI - dot(m, starts); + int currentVertex = vI - idot4(m, starts); // get index into triIndices table - float mcIndex = 16. * edgeIndex + currentVertex; - vec4 mcData = texture2D(tTriIndices, vec2(intMod(mcIndex, 64.), floor(mcIndex / 64.)) / 64.); + int mcIndex = 16 * int(edgeIndex) + currentVertex; + vec4 mcData = texture2D(tTriIndices, vec2(imod(mcIndex, 64), mcIndex / 64) / 64.); // bit mask to avoid conditionals (see comment below) for getting MC case corner vec4 m0 = vec4(floor(mcData.a * 255.0 + 0.5)); @@ -188,30 +243,41 @@ void main(void) { // group id #if __VERSION__ == 100 // webgl1 does not support 'flat' interpolation (i.e. no interpolation) - // so we ensure a constant group id per triangle - gl_FragData[0].w = decodeFloatRGB(voxel(coord3d).rgb); + // so we ensure a constant group id per triangle here + #ifdef dPackedGroup + gl_FragData[1] = vec4(voxel(coord3d).rgb, 1.0); + #else + vec3 gridDim = uGridDim - vec3(1.0, 1.0, 0.0); // remove xy padding + float group = coord3d.z + coord3d.y * gridDim.z + coord3d.x * gridDim.z * gridDim.y; + gl_FragData[1] = vec4(group > 16777215.5 ? vec3(1.0) : encodeFloatRGB(group), 1.0); + #endif #else - gl_FragData[0].w = t < 0.5 ? decodeFloatRGB(d0.rgb) : decodeFloatRGB(d1.rgb); + #ifdef dPackedGroup + gl_FragData[1] = vec4(t < 0.5 ? d0.rgb : d1.rgb, 1.0); + #else + vec3 b = t < 0.5 ? b0 : b1; + vec3 gridDim = uGridDim - vec3(1.0, 1.0, 0.0); // remove xy padding + float group = b.z + b.y * gridDim.z + b.x * gridDim.z * gridDim.y; + gl_FragData[1] = vec4(group > 16777215.5 ? vec3(1.0) : encodeFloatRGB(group), 1.0); + #endif #endif // normals from gradients vec3 n0 = -normalize(vec3( - voxel2(b0 - c1).a - voxel2(b0 + c1).a, - voxel2(b0 - c3).a - voxel2(b0 + c3).a, - voxel2(b0 - c4).a - voxel2(b0 + c4).a + voxelPadded(b0 - c1).a - voxelPadded(b0 + c1).a, + voxelPadded(b0 - c3).a - voxelPadded(b0 + c3).a, + voxelPadded(b0 - c4).a - voxelPadded(b0 + c4).a )); vec3 n1 = -normalize(vec3( - voxel2(b1 - c1).a - voxel2(b1 + c1).a, - voxel2(b1 - c3).a - voxel2(b1 + c3).a, - voxel2(b1 - c4).a - voxel2(b1 + c4).a + voxelPadded(b1 - c1).a - voxelPadded(b1 + c1).a, + voxelPadded(b1 - c3).a - voxelPadded(b1 + c3).a, + voxelPadded(b1 - c4).a - voxelPadded(b1 + c4).a )); - gl_FragData[1].xyz = -vec3( + mat3 normalMatrix = transpose3(inverse3(mat3(uGridTransform))); + gl_FragData[2].xyz = normalMatrix * -vec3( n0.x + t * (n0.x - n1.x), n0.y + t * (n0.y - n1.y), n0.z + t * (n0.z - n1.z) ); - - mat3 normalMatrix = transpose3(inverse3(mat3(uGridTransform))); - gl_FragData[1].xyz = normalMatrix * gl_FragData[1].xyz; } `; \ No newline at end of file diff --git a/src/mol-gl/shader/mesh.vert.ts b/src/mol-gl/shader/mesh.vert.ts index 1117ccb5952867ee6897dc9b78d28e650862c6ee..9014a43b7209fedaf8b96ddd98bd3b630a19b69c 100644 --- a/src/mol-gl/shader/mesh.vert.ts +++ b/src/mol-gl/shader/mesh.vert.ts @@ -7,6 +7,7 @@ export default ` precision highp float; precision highp int; +precision highp sampler2D; #include common #include read_from_texture @@ -16,19 +17,17 @@ precision highp int; #ifdef dGeoTexture uniform vec2 uGeoTexDim; - uniform sampler2D tPositionGroup; + uniform sampler2D tPosition; + uniform sampler2D tGroup; + uniform sampler2D tNormal; #else attribute vec3 aPosition; + attribute float aGroup; + attribute vec3 aNormal; #endif attribute mat4 aTransform; attribute float aInstance; -attribute float aGroup; -#ifdef dGeoTexture - uniform sampler2D tNormal; -#else - attribute vec3 aNormal; -#endif varying vec3 vNormal; void main(){ @@ -40,7 +39,7 @@ void main(){ #include clip_instance #ifdef dGeoTexture - vec3 normal = readFromTexture(tNormal, aGroup, uGeoTexDim).xyz; + vec3 normal = readFromTexture(tNormal, VertexID, uGeoTexDim).xyz; #else vec3 normal = aNormal; #endif diff --git a/src/mol-gl/webgl/texture.ts b/src/mol-gl/webgl/texture.ts index 5cad275f33d16ebdb0f86d3edd4fdee4c2cd3638..5719c006de43a70fefd3dedf7775b0f970ed140b 100644 --- a/src/mol-gl/webgl/texture.ts +++ b/src/mol-gl/webgl/texture.ts @@ -57,6 +57,7 @@ export function getFormat(gl: GLRenderingContext, format: TextureFormat, type: T switch (format) { case 'alpha': if (isWebGL2(gl) && type === 'float') return gl.RED; + else if (isWebGL2(gl) && type === 'int') return gl.RED_INTEGER; else return gl.ALPHA; case 'rgb': if (isWebGL2(gl) && type === 'int') return gl.RGB_INTEGER; diff --git a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts index 546d98e3a8c817228209d2eadd93ff3df5f173dd..f63ed65b1bd54be7b6a07502a3b2b5bd8a2f9551 100644 --- a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts +++ b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts @@ -15,9 +15,7 @@ import { computeMarchingCubesMesh } from '../../../mol-geo/util/marching-cubes/a import { ElementIterator, getElementLoci, eachElement, getSerialElementLoci, eachSerialElement } from './util/element'; import { VisualUpdateState } from '../../util'; import { TextureMesh } from '../../../mol-geo/geometry/texture-mesh/texture-mesh'; -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'; +import { extractIsosurface } from '../../../mol-gl/compute/marching-cubes/isosurface'; import { Sphere3D } from '../../../mol-math/geometry'; import { ComplexVisual, ComplexMeshParams, ComplexMeshVisual, ComplexTextureMeshVisual, ComplexTextureMeshParams } from '../complex-visual'; import { getUnitExtraRadius, getStructureExtraRadius } from './util/common'; @@ -169,29 +167,11 @@ async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit, 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(); - // console.timeEnd('calcActiveVoxels'); - - // 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(); - // console.timeEnd('createIsosurfaceBuffers'); + const gv = extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, true, textureMesh?.vertexTexture.ref.value, textureMesh?.groupTexture.ref.value, textureMesh?.normalTexture.ref.value); const boundingSphere = Sphere3D.expand(Sphere3D(), unit.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, - // framebuffers: ctx.webgl.namedFramebuffers, - // textures: ctx.webgl.namedTextures, - // }); - // ctx.webgl.waitForGpuCommandsCompleteSync(); + const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh); + return surface; } @@ -214,8 +194,9 @@ export function GaussianSurfaceTextureMeshVisual(materialId: number): UnitsVisua return !props.useGpu || !webgl; }, dispose: (geometry: TextureMesh) => { + geometry.vertexTexture.ref.value.destroy(); + geometry.groupTexture.ref.value.destroy(); geometry.normalTexture.ref.value.destroy(); - geometry.vertexGroupTexture.ref.value.destroy(); } }, materialId); } @@ -243,29 +224,11 @@ async function createStructureGaussianSurfaceTextureMesh(ctx: VisualContext, str 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(); - // console.timeEnd('calcActiveVoxels'); - - // 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(); - // console.timeEnd('createIsosurfaceBuffers'); + const gv = extractIsosurface(ctx.webgl, densityTextureData.texture, densityTextureData.gridDim, densityTextureData.gridTexDim, densityTextureData.gridTexScale, densityTextureData.transform, isoLevel, true, textureMesh?.vertexTexture.ref.value, textureMesh?.groupTexture.ref.value, textureMesh?.normalTexture.ref.value); 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, - // framebuffers: ctx.webgl.namedFramebuffers, - // textures: ctx.webgl.namedTextures, - // }); - // ctx.webgl.waitForGpuCommandsCompleteSync(); + const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, boundingSphere, textureMesh); + return surface; } @@ -287,8 +250,9 @@ export function StructureGaussianSurfaceTextureMeshVisual(materialId: number): C return !props.useGpu || !webgl; }, dispose: (geometry: TextureMesh) => { + geometry.vertexTexture.ref.value.destroy(); + geometry.groupTexture.ref.value.destroy(); geometry.normalTexture.ref.value.destroy(); - geometry.vertexGroupTexture.ref.value.destroy(); } }, materialId); } \ No newline at end of file diff --git a/src/mol-repr/volume/isosurface.ts b/src/mol-repr/volume/isosurface.ts index 7ffac46b5aa5db42b22551c52702d836e90b7a79..b3d48765917f97a64ff107014b0ce2484affdf18 100644 --- a/src/mol-repr/volume/isosurface.ts +++ b/src/mol-repr/volume/isosurface.ts @@ -24,9 +24,7 @@ import { Tensor, Vec2, Vec3 } from '../../mol-math/linear-algebra'; import { fillSerial } from '../../mol-util/array'; import { createVolumeTexture2d, eachVolumeLoci, getVolumeTexture2dLayout } from './util'; import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh'; -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'; +import { extractIsosurface } from '../../mol-gl/compute/marching-cubes/isosurface'; import { WebGLContext } from '../../mol-gl/webgl/context'; import { CustomPropertyDescriptor } from '../../mol-model/custom-property'; import { Texture } from '../../mol-gl/webgl/texture'; @@ -128,11 +126,11 @@ namespace VolumeIsosurfaceTexture { // console.log({ texDim, width, height, gridDimension }); if (!volume._propertyData[name]) { - volume._propertyData[name] = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear'); + volume._propertyData[name] = resources.texture('image-uint8', 'alpha', 'ubyte', 'linear'); const texture = volume._propertyData[name] as Texture; texture.define(texDim, texDim); // load volume into sub-section of texture - texture.load(createVolumeTexture2d(volume, 'groups', padding), true); + texture.load(createVolumeTexture2d(volume, 'data', Padding), true); volume.customProperties.add(descriptor); volume.customProperties.assets(descriptor, [{ dispose: () => texture.destroy() }]); } @@ -160,28 +158,10 @@ async function createVolumeIsosurfaceTextureMesh(ctx: VisualContext, volume: Vol const { texture, gridDimension, gridTexDim, gridTexScale, transform } = VolumeIsosurfaceTexture.get(volume, ctx.webgl); - // console.time('calcActiveVoxels'); - const activeVoxelsTex = calcActiveVoxels(ctx.webgl, texture, gridDimension, gridTexDim, isoLevel, gridTexScale); - // ctx.webgl.waitForGpuCommandsCompleteSync(); - // console.timeEnd('calcActiveVoxels'); - - // console.time('createHistogramPyramid'); - const compacted = createHistogramPyramid(ctx.webgl, activeVoxelsTex, gridTexScale, gridTexDim); - // ctx.webgl.waitForGpuCommandsCompleteSync(); - // console.timeEnd('createHistogramPyramid'); - - // console.time('createIsosurfaceBuffers'); - const gv = createIsosurfaceBuffers(ctx.webgl, activeVoxelsTex, texture, compacted, gridDimension, gridTexDim, transform, isoLevel, textureMesh ? textureMesh.vertexGroupTexture.ref.value : undefined, textureMesh ? textureMesh.normalTexture.ref.value : undefined); - // ctx.webgl.waitForGpuCommandsCompleteSync(); - // console.timeEnd('createIsosurfaceBuffers'); - - const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexGroupTexture, gv.normalTexture, Volume.getBoundingSphere(volume), textureMesh); - // console.log({ - // renderables: ctx.webgl.namedComputeRenderables, - // framebuffers: ctx.webgl.namedFramebuffers, - // textures: ctx.webgl.namedTextures, - // }); - // ctx.webgl.waitForGpuCommandsCompleteSync(); + const gv = extractIsosurface(ctx.webgl, texture, gridDimension, gridTexDim, gridTexScale, transform, isoLevel, false, textureMesh?.vertexTexture.ref.value, textureMesh?.groupTexture.ref.value, textureMesh?.normalTexture.ref.value); + + const surface = TextureMesh.create(gv.vertexCount, 1, gv.vertexTexture, gv.groupTexture, gv.normalTexture, Volume.getBoundingSphere(volume), textureMesh); + return surface; } @@ -200,8 +180,9 @@ export function IsosurfaceTextureMeshVisual(materialId: number): VolumeVisual<Is return !props.useGpu || !webgl; }, dispose: (geometry: TextureMesh) => { + geometry.vertexTexture.ref.value.destroy(); + geometry.groupTexture.ref.value.destroy(); geometry.normalTexture.ref.value.destroy(); - geometry.vertexGroupTexture.ref.value.destroy(); } }, materialId); } diff --git a/src/mol-repr/volume/util.ts b/src/mol-repr/volume/util.ts index b35fd08e82ac015fbeca86c4a0971b7926d8a4fe..486639421fb0b8caea8dde64771b8b993ddfd3f2 100644 --- a/src/mol-repr/volume/util.ts +++ b/src/mol-repr/volume/util.ts @@ -75,13 +75,14 @@ export function getVolumeTexture2dLayout(dim: Vec3, padding = 0) { return { width, height, columns, rows, powerOfTwoSize: height < powerOfTwoSize ? powerOfTwoSize : powerOfTwoSize * 2 }; } -export function createVolumeTexture2d(volume: Volume, variant: 'normals' | 'groups', padding = 0) { +export function createVolumeTexture2d(volume: Volume, variant: 'normals' | 'groups' | 'data', padding = 0) { const { cells: { space, data }, stats: { max, min } } = volume.grid; const dim = space.dimensions as Vec3; const { dataOffset: o } = space; const { width, height } = getVolumeTexture2dLayout(dim, padding); - const array = new Uint8Array(width * height * 4); + const itemSize = variant === 'data' ? 1 : 4; + const array = new Uint8Array(width * height * itemSize); const textureImage = { array, width, height }; const diff = max - min; @@ -102,28 +103,32 @@ export function createVolumeTexture2d(volume: Volume, variant: 'normals' | 'grou const column = Math.floor(((z * xnp) % width) / xnp); const row = Math.floor((z * xnp) / width); const px = column * xnp + x; - const index = 4 * ((row * ynp * width) + (y * width) + px); + const index = itemSize * ((row * ynp * width) + (y * width) + px); const offset = o(x, y, z); - if (variant === 'groups') { - encodeFloatRGBtoArray(offset, array, index); + if (variant === 'data') { + array[index] = Math.round(((data[offset] - min) / diff) * 255); } else { - 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); + if (variant === 'groups') { + encodeFloatRGBtoArray(offset, array, index); + } else { + 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] = Math.round(((data[offset] - min) / diff) * 255); } - - array[index + 3] = ((data[offset] - min) / diff) * 255; } } } @@ -167,7 +172,7 @@ export function createVolumeTexture3d(volume: Volume) { v3addScalar(n0, v3scale(n0, n0, 0.5), 0.5); v3toArray(v3scale(n0, n0, 255), array, i); - array[i + 3] = ((data[offset] - min) / diff) * 255; + array[i + 3] = Math.round(((data[offset] - min) / diff) * 255); i += 4; } }