diff --git a/src/mol-gl/renderable/gaussian-density.ts b/src/mol-gl/renderable/gaussian-density.ts index abb6b8b803ea26ace606f930649cfd0952c4c371..5d2a4f5c5736d5cc42c62df713131964f64459d8 100644 --- a/src/mol-gl/renderable/gaussian-density.ts +++ b/src/mol-gl/renderable/gaussian-density.ts @@ -25,9 +25,11 @@ export const GaussianDensitySchema = { uBboxMax: UniformSpec('v3'), uBboxSize: UniformSpec('v3'), uGridDim: UniformSpec('v3'), + uGridTexDim: UniformSpec('v2'), uAlpha: UniformSpec('f'), - tMinDistanceTex: TextureSpec('texture2d', 'rgba', 'ubyte', 'nearest'), + tMinDistanceTex: TextureSpec('texture3d', 'rgba', 'ubyte', 'nearest'), + dGridTexType: DefineSpec('string', ['2d', '3d']), dCalcType: DefineSpec('string', ['density', 'minDistance', 'groupId']), } export type GaussianDensitySchema = typeof GaussianDensitySchema diff --git a/src/mol-gl/shader/direct-volume.frag b/src/mol-gl/shader/direct-volume.frag index 3082b5ccf6bd2c2cd9d29cb3035789435083a051..96df2263e85c10de21200adb093b7fa792e806cd 100644 --- a/src/mol-gl/shader/direct-volume.frag +++ b/src/mol-gl/shader/direct-volume.frag @@ -5,11 +5,6 @@ * @author Michael Krone <michael.krone@uni-tuebingen.de> */ -#if defined(dGridTexType_2d) - precision mediump sampler2D; -#elif defined(dGridTexType_3d) - precision mediump sampler3D; -#endif precision highp float; varying vec3 unitCoord; @@ -22,9 +17,11 @@ uniform vec3 uGridDim; uniform sampler2D tTransferTex; #if defined(dGridTexType_2d) + precision mediump sampler2D; uniform sampler2D tGridTex; uniform vec2 uGridTexDim; #elif defined(dGridTexType_3d) + precision mediump sampler3D; uniform sampler3D tGridTex; #endif diff --git a/src/mol-gl/shader/gaussian-density.frag b/src/mol-gl/shader/gaussian-density.frag index f9558f86612853dab61e106ac5480170a04512c8..dbb5e67b195bce57eecb1fff62edad06af76eb4e 100644 --- a/src/mol-gl/shader/gaussian-density.frag +++ b/src/mol-gl/shader/gaussian-density.frag @@ -10,12 +10,19 @@ precision highp float; varying vec3 vPosition; varying float vRadius; #if defined(dCalcType_groupId) - precision highp sampler3D; - uniform sampler3D tMinDistanceTex; + #if defined(dGridTexType_2d) + precision mediump sampler2D; + uniform sampler2D tMinDistanceTex; + uniform vec2 uGridTexDim; + #elif defined(dGridTexType_3d) + precision highp sampler3D; + uniform sampler3D tMinDistanceTex; + #endif varying float vGroup; #endif #pragma glslify: encodeIdRGBA = require(./utils/encode-id-rgba.glsl) +#pragma glslify: texture3dFrom2dNearest = require(./utils/texture3d-from-2d-nearest.glsl) uniform vec3 uBboxSize; uniform vec3 uBboxMin; @@ -26,6 +33,18 @@ uniform float uCurrentX; uniform float uCurrentY; uniform float uAlpha; +#if defined(dCalcType_groupId) + #if defined(dGridTexType_2d) + vec4 textureMinDist(vec3 pos) { + return texture3dFrom2dNearest(tMinDistanceTex, pos, uGridDim, uGridTexDim); + } + #elif defined(dGridTexType_3d) + vec4 textureMinDist(vec3 pos) { + return texture(tMinDistanceTex, pos); + } + #endif +#endif + // encode distance logarithmically with given maxDistance const float maxDistance = 10000.0; const float distLogFactor = log(maxDistance + 1.0); @@ -42,10 +61,10 @@ void main() { float density = exp(-uAlpha * ((dist * dist) / radiusSq)); gl_FragColor = vec4(density); #elif defined(dCalcType_minDistance) - gl_FragColor.r = 1.0 - encodeDistLog(dist); + gl_FragColor.a = 1.0 - encodeDistLog(dist); #elif defined(dCalcType_groupId) - float minDistance = decodeDistLog(1.0 - texture(tMinDistanceTex, fragPos).r); - if (dist > minDistance + log(minDistance) / 2.0) + float minDistance = decodeDistLog(1.0 - textureMinDist(fragPos).a); + if (dist > minDistance + length(uBboxSize / uGridDim) / 1.5) discard; gl_FragColor = encodeIdRGBA(vGroup); #endif diff --git a/src/mol-gl/shader/utils/encode-id-rgba.glsl b/src/mol-gl/shader/utils/encode-id-rgba.glsl index 9a47039656c6e0e0e8e0d08b8f411d3f6f3b89eb..b487be72f3d04f9a17c723117562c20508468c78 100644 --- a/src/mol-gl/shader/utils/encode-id-rgba.glsl +++ b/src/mol-gl/shader/utils/encode-id-rgba.glsl @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + #pragma glslify: encodeFloatRGBA = require(../utils/encode-float-rgba.glsl) vec4 encodeIdRGBA(const in float v) { diff --git a/src/mol-gl/shader/utils/my-div.glsl b/src/mol-gl/shader/utils/my-div.glsl new file mode 100644 index 0000000000000000000000000000000000000000..49c46189473c817dee69d1e562bd5826f7e83aab --- /dev/null +++ b/src/mol-gl/shader/utils/my-div.glsl @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2017-2018 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> + */ + +// TODO workaround due to some kind of GPU quirk +float myDiv(float a, float b) { + return float(int(a) / int(b)); +} + +#pragma glslify: export(myDiv) \ No newline at end of file diff --git a/src/mol-gl/shader/utils/my-mod.glsl b/src/mol-gl/shader/utils/my-mod.glsl new file mode 100644 index 0000000000000000000000000000000000000000..3030ff3f182c3cdbed3b5d500e5d1c2caf748012 --- /dev/null +++ b/src/mol-gl/shader/utils/my-mod.glsl @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2017-2018 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> + */ + +// TODO workaround due to some kind of GPU quirk +float myMod(float a, float b) { + return a - b * float(int(a) / int(b)); +} + +#pragma glslify: export(myMod) \ No newline at end of file diff --git a/src/mol-gl/shader/utils/texture3d-from-2d-linear.glsl b/src/mol-gl/shader/utils/texture3d-from-2d-linear.glsl new file mode 100644 index 0000000000000000000000000000000000000000..5b8fca4f0961be5126b051513cff79f011d0b806 --- /dev/null +++ b/src/mol-gl/shader/utils/texture3d-from-2d-linear.glsl @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2017-2018 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> + */ + +#pragma glslify: myMod = require(./my-mod.glsl) +#pragma glslify: myDiv = require(./my-div.glsl) + +vec4 texture3dFrom2dLinear(sampler2D tex, vec3 pos, vec3 gridDim, vec2 texDim) { + float zSlice0 = floor(pos.z * gridDim.z); + float column0 = myMod(zSlice0 * gridDim.x, texDim.x) / gridDim.x; + float row0 = floor(myDiv(zSlice0 * gridDim.x, texDim.x)); + vec2 coord0 = (vec2(column0 * gridDim.x, row0 * gridDim.y) + (pos.xy * gridDim.xy)) / texDim; + vec4 color0 = texture2D(tex, coord0); + + float zSlice1 = zSlice0 + 1.0; + float column1 = myMod(zSlice1 * gridDim.x, texDim.x) / gridDim.x; + float row1 = floor(myDiv(zSlice1 * gridDim.x, texDim.x)); + vec2 coord1 = (vec2(column1 * gridDim.x, row1 * gridDim.y) + (pos.xy * gridDim.xy)) / texDim; + vec4 color1 = texture2D(tex, coord1); + + float delta0 = abs((pos.z * gridDim.z) - zSlice0); + return mix(color0, color1, delta0); +} + +#pragma glslify: export(texture3dFrom2dLinear) \ No newline at end of file diff --git a/src/mol-gl/shader/utils/texture3d-from-2d-nearest.glsl b/src/mol-gl/shader/utils/texture3d-from-2d-nearest.glsl new file mode 100644 index 0000000000000000000000000000000000000000..9cf714cd63ec3eacd9a442035c45cd7d9ac295bb --- /dev/null +++ b/src/mol-gl/shader/utils/texture3d-from-2d-nearest.glsl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017-2018 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> + */ + +#pragma glslify: myMod = require(./my-mod.glsl) +#pragma glslify: myDiv = require(./my-div.glsl) + +vec4 texture3dFrom2dNearest(sampler2D tex, vec3 pos, vec3 gridDim, vec2 texDim) { + float zSlice = floor(pos.z * gridDim.z + 0.5); // round to nearest z-slice + float column = myMod(zSlice * gridDim.x, texDim.x) / gridDim.x; + float row = floor(myDiv(zSlice * gridDim.x, texDim.x)); + vec2 coord = (vec2(column * gridDim.x, row * gridDim.y) + (pos.xy * gridDim.xy)) / texDim; + return texture2D(tex, coord); +} + +#pragma glslify: export(texture3dFrom2dNearest) \ No newline at end of file diff --git a/src/mol-gl/webgl/compat.ts b/src/mol-gl/webgl/compat.ts index f0bad2f5f48e7991d4393afa51da63f92d5de86b..9fbaba5956960411ab9f14dab57e411db02a3143 100644 --- a/src/mol-gl/webgl/compat.ts +++ b/src/mol-gl/webgl/compat.ts @@ -106,4 +106,19 @@ export interface COMPAT_texture_float_linear { export function getTextureFloatLinear(gl: GLRenderingContext): COMPAT_texture_float_linear | null { return gl.getExtension('OES_texture_float_linear') +} + +export interface COMPAT_blend_minmax { + readonly MIN: number + readonly MAX: number +} + +export function getBlendMinMax(gl: GLRenderingContext): COMPAT_blend_minmax | null { + if (isWebGL2(gl)) { + return { MIN: gl.MIN, MAX: gl.MAX } + } else { + const ext = gl.getExtension('EXT_blend_minmax') + if (ext === null) return null + return { MIN: ext.MIN_EXT, MAX: ext.MAX_EXT } + } } \ No newline at end of file diff --git a/src/mol-gl/webgl/context.ts b/src/mol-gl/webgl/context.ts index 869526c5fe3c76e8364ccb09c8a9c312502b1f3b..3fd9a2e3fa7e1c67af3fd8c39f62ef140f7bc7ac 100644 --- a/src/mol-gl/webgl/context.ts +++ b/src/mol-gl/webgl/context.ts @@ -6,7 +6,7 @@ import { createProgramCache, ProgramCache } from './program' import { createShaderCache, ShaderCache } from './shader' -import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, getVertexArrayObject, isWebGL2, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear } from './compat'; +import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, getVertexArrayObject, isWebGL2, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax } from './compat'; export function getGLContext(canvas: HTMLCanvasElement, contextAttributes?: WebGLContextAttributes): GLRenderingContext | null { function getContext(contextId: 'webgl' | 'experimental-webgl' | 'webgl2') { @@ -100,8 +100,9 @@ export function createImageData(buffer: ArrayLike<number>, width: number, height type Extensions = { instancedArrays: COMPAT_instanced_arrays standardDerivatives: COMPAT_standard_derivatives - textureFloat: COMPAT_texture_float, - textureFloatLinear: COMPAT_texture_float_linear, + blendMinMax: COMPAT_blend_minmax + textureFloat: COMPAT_texture_float + textureFloatLinear: COMPAT_texture_float_linear elementIndexUint: COMPAT_element_index_uint | null vertexArrayObject: COMPAT_vertex_array_object | null } @@ -144,6 +145,10 @@ export function createContext(gl: GLRenderingContext): Context { if (standardDerivatives === null) { throw new Error('Could not find support for "standard_derivatives"') } + const blendMinMax = getBlendMinMax(gl) + if (blendMinMax === null) { + throw new Error('Could not find support for "blend_minmax"') + } const textureFloat = getTextureFloat(gl) if (textureFloat === null) { throw new Error('Could not find support for "texture_float"') @@ -175,6 +180,7 @@ export function createContext(gl: GLRenderingContext): Context { extensions: { instancedArrays, standardDerivatives, + blendMinMax, textureFloat, textureFloatLinear, elementIndexUint, diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index d3fbaa40f7611c9208495a0eb983615466d52eca..233ce9aa6e22e473c603e97a678dea0b873cf7c6 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -10,10 +10,10 @@ import { PositionData, DensityData, DensityTextureData } from '../common' import { Box3D } from '../../geometry' import { GaussianDensityProps, getDelta } from '../gaussian-density' import { OrderedSet } from 'mol-data/int' -import { Vec3, Tensor, Mat4 } from '../../linear-algebra' +import { Vec3, Tensor, Mat4, Vec2 } from '../../linear-algebra' import { GaussianDensityValues } from 'mol-gl/renderable/gaussian-density' import { ValueCell, defaults } from 'mol-util' -import { RenderableState } from 'mol-gl/renderable' +import { RenderableState, Renderable } from 'mol-gl/renderable' import { createRenderable, createGaussianDensityRenderObject } from 'mol-gl/render-object' import { Context, createContext, getGLContext } from 'mol-gl/webgl/context'; import { createFramebuffer } from 'mol-gl/webgl/framebuffer'; @@ -23,13 +23,16 @@ import { decodeIdRGBA } from 'mol-geo/geometry/picking'; export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> { const webgl = defaults(props.webgl, getWebGLContext()) + // always use texture2d when the gaussian density needs to be downloaded from the GPU, + // it's faster than texture3d + console.time('GaussianDensityTexture2d') + const { scale, bbox, texture, dim } = await GaussianDensityTexture2d(ctx, webgl, position, box, radius, props) + console.timeEnd('GaussianDensityTexture2d') + const { field, idField } = fieldFromTexture2d(webgl, texture, dim) - const { transform, texture, gridDimension } = await GaussianDensityTexture(ctx, webgl, position, box, radius, props) - - const { field, idField } = webgl.isWebGL2 ? - fieldFromTexture3d(webgl, texture, gridDimension) : - fieldFromTexture2d(webgl, texture, gridDimension) - + const transform = Mat4.identity() + Mat4.fromScaling(transform, scale) + Mat4.setTranslation(transform, bbox.min) console.log(idField) return { field, idField, transform } @@ -56,63 +59,56 @@ async function GaussianDensityTexture2d(ctx: RuntimeContext, webgl: Context, pos const { drawCount, positions, radii, groups, delta, expandedBox, dim } = await prepareGaussianDensityData(ctx, position, box, radius, props) const [ dx, dy, dz ] = dim - const minDistanceTexture = getMinDistanceTexture(webgl, dx, dy) + const { texDimX, texDimY, texCols } = getTexture2dSize(webgl.maxTextureSize, dim) + + const minDistanceTexture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'nearest') + minDistanceTexture.define(texDimX, texDimY) const renderObject = getGaussianDensityRenderObject(webgl, drawCount, positions, radii, groups, minDistanceTexture, expandedBox, dim, smoothness) const renderable = createRenderable(webgl, renderObject) // - const maxTexSize = webgl.maxTextureSize - let fboTexDimX = 0 - let fboTexDimY = dim[1] - let fboTexRows = 1 - let fboTexCols = dim[0] - if (maxTexSize < dim[0] * dim[2]) { - fboTexCols = Math.floor(maxTexSize / dim[0]) - fboTexRows = Math.ceil(dim[2] / fboTexCols) - fboTexDimX = fboTexCols * dim[0] - fboTexDimY *= fboTexRows - } else { - fboTexDimX = dim[0] * dim[2] - } - - // - const { gl } = webgl const { uCurrentSlice, uCurrentX, uCurrentY } = renderObject.values const framebuffer = createFramebuffer(webgl) framebuffer.bind() - - if (!texture) { - texture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'linear') - } - texture.define(fboTexDimX, fboTexDimY) - - const program = renderable.getProgram('draw') - program.use() setRenderingDefaults(gl) - texture.attachFramebuffer(framebuffer, 0) - let currCol = 0 - let currY = 0 - let currX = 0 - for (let i = 0; i < dz; ++i) { - if (currCol >= fboTexCols) { - currCol -= fboTexCols - currY += dy - currX = 0 + if (!texture) texture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'linear') + texture.define(texDimX, texDimY) + + function render(fbTex: Texture) { + fbTex.attachFramebuffer(framebuffer, 0) + let currCol = 0 + let currY = 0 + let currX = 0 + for (let i = 0; i < dz; ++i) { + if (currCol >= texCols) { + currCol -= texCols + currY += dy + currX = 0 + } + gl.viewport(currX, currY, dx, dy) + ValueCell.update(uCurrentSlice, i) + ValueCell.update(uCurrentX, currX) + ValueCell.update(uCurrentY, currY) + renderable.render('draw') + ++currCol + currX += dx } - gl.viewport(currX, currY, dx, dy) - ValueCell.update(uCurrentSlice, i) - ValueCell.update(uCurrentX, currX) - ValueCell.update(uCurrentY, currY) - renderable.render('draw') - ++currCol - currX += dx } + setupMinDistanceRendering(webgl, renderable) + render(minDistanceTexture) + + setupDensityRendering(webgl, renderable) + render(texture) + + setupGroupIdRendering(webgl, renderable) + render(texture) + framebuffer.destroy() // clean up await ctx.update({ message: 'gpu gaussian density calculation' }); @@ -134,55 +130,33 @@ async function GaussianDensityTexture3d(ctx: RuntimeContext, webgl: Context, pos // - const gl = webgl.gl as WebGL2RenderingContext + const { gl } = webgl const { uCurrentSlice } = renderObject.values const framebuffer = createFramebuffer(webgl) framebuffer.bind() - - gl.viewport(0, 0, dx, dy) setRenderingDefaults(gl) + gl.viewport(0, 0, dx, dy) - if (!texture) { - texture = createTexture(webgl, 'volume-uint8', 'rgba', 'ubyte', 'linear') - } + if (!texture) texture = createTexture(webgl, 'volume-uint8', 'rgba', 'ubyte', 'linear') texture.define(dx, dy, dz) - ValueCell.update(renderable.values.dCalcType, 'minDistance') - renderable.update() - const programMinDistance = renderable.getProgram('draw') - programMinDistance.use() - gl.blendFunc(gl.ONE, gl.ONE) - gl.blendEquation(gl.MAX) - for (let i = 0; i < dz; ++i) { - ValueCell.update(uCurrentSlice, i) - minDistanceTexture.attachFramebuffer(framebuffer, 0, i) - renderable.render('draw') + function render(fbTex: Texture) { + for (let i = 0; i < dz; ++i) { + ValueCell.update(uCurrentSlice, i) + fbTex.attachFramebuffer(framebuffer, 0, i) + renderable.render('draw') + } } - ValueCell.update(renderable.values.dCalcType, 'density') - renderable.update() - const programDensity = renderable.getProgram('draw') - programDensity.use() - gl.blendFunc(gl.ONE, gl.ONE) - gl.blendEquation(gl.FUNC_ADD) - for (let i = 0; i < dz; ++i) { - ValueCell.update(uCurrentSlice, i) - texture.attachFramebuffer(framebuffer, 0, i) - renderable.render('draw') - } + setupMinDistanceRendering(webgl, renderable) + render(minDistanceTexture) - ValueCell.update(renderable.values.dCalcType, 'groupId') - renderable.update() - const programGroupId = renderable.getProgram('draw') - programGroupId.use() - gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ZERO, gl.ONE) - gl.blendEquation(gl.FUNC_ADD) - for (let i = 0; i < dz; ++i) { - ValueCell.update(uCurrentSlice, i) - texture.attachFramebuffer(framebuffer, 0, i) - renderable.render('draw') - } + setupDensityRendering(webgl, renderable) + render(texture) + + setupGroupIdRendering(webgl, renderable) + render(texture) framebuffer.destroy() // clean up @@ -249,14 +223,9 @@ async function prepareGaussianDensityData(ctx: RuntimeContext, position: Positio return { drawCount: n, positions, radii, groups, delta, expandedBox, dim } } -function getMinDistanceTexture(webgl: Context, width: number, height: number) { - const minDistanceTexture = createTexture(webgl, 'image-uint8', 'rgba', 'ubyte', 'nearest') - minDistanceTexture.define(width, height) - return minDistanceTexture -} - function getGaussianDensityRenderObject(webgl: Context, drawCount: number, positions: Float32Array, radii: Float32Array, groups: Float32Array, minDistanceTexture: Texture, box: Box3D, dimensions: Vec3, smoothness: number) { const extent = Vec3.sub(Vec3.zero(), box.max, box.min) + const { texDimX, texDimY } = getTexture2dSize(webgl.maxTextureSize, dimensions) const values: GaussianDensityValues = { drawCount: ValueCell.create(drawCount), @@ -273,9 +242,11 @@ function getGaussianDensityRenderObject(webgl: Context, drawCount: number, posit uBboxMax: ValueCell.create(box.max), uBboxSize: ValueCell.create(extent), uGridDim: ValueCell.create(dimensions), + uGridTexDim: ValueCell.create(Vec2.create(texDimX, texDimY)), uAlpha: ValueCell.create(smoothness), tMinDistanceTex: ValueCell.create(minDistanceTexture), + dGridTexType: ValueCell.create(minDistanceTexture.depth > 0 ? '3d' : '2d'), dCalcType: ValueCell.create('density'), } const state: RenderableState = { @@ -292,10 +263,52 @@ function setRenderingDefaults(gl: GLRenderingContext) { gl.disable(gl.CULL_FACE) gl.frontFace(gl.CCW) gl.cullFace(gl.BACK) + gl.enable(gl.BLEND) +} +function setupMinDistanceRendering(webgl: Context, renderable: Renderable<any>) { + const { gl } = webgl + ValueCell.update(renderable.values.dCalcType, 'minDistance') + renderable.update() + renderable.getProgram('draw').use() + gl.blendFunc(gl.ONE, gl.ONE) + // the shader writes 1 - dist so we set blending to MAX + gl.blendEquation(webgl.extensions.blendMinMax.MAX) +} + +function setupDensityRendering(webgl: Context, renderable: Renderable<any>) { + const { gl } = webgl + ValueCell.update(renderable.values.dCalcType, 'density') + renderable.update() + renderable.getProgram('draw').use() gl.blendFunc(gl.ONE, gl.ONE) gl.blendEquation(gl.FUNC_ADD) - gl.enable(gl.BLEND) +} + +function setupGroupIdRendering(webgl: Context, renderable: Renderable<any>) { + const { gl } = webgl + ValueCell.update(renderable.values.dCalcType, 'groupId') + renderable.update() + renderable.getProgram('draw').use() + // overwrite color, don't change alpha + gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ZERO, gl.ONE) + gl.blendEquation(gl.FUNC_ADD) +} + +function getTexture2dSize(maxTexSize: number, gridDim: Vec3) { + let texDimX = 0 + let texDimY = gridDim[1] + let texRows = 1 + let texCols = gridDim[0] + if (maxTexSize < gridDim[0] * gridDim[2]) { + texCols = Math.floor(maxTexSize / gridDim[0]) + texRows = Math.ceil(gridDim[2] / texCols) + texDimX = texCols * gridDim[0] + texDimY *= texRows + } else { + texDimX = gridDim[0] * gridDim[2] + } + return { texDimX, texDimY, texRows, texCols } } function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) { @@ -319,7 +332,7 @@ function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) { texture.attachFramebuffer(framebuffer, 0) gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, image) - let idx = 0 + let j = 0 let tmpCol = 0 let tmpRow = 0 for (let iz = 0; iz < dz; ++iz) { @@ -329,8 +342,10 @@ function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) { } for (let iy = 0; iy < dy; ++iy) { for (let ix = 0; ix < dx; ++ix) { - data[idx] = image[4 * (tmpCol * dx + (iy + tmpRow) * width + ix) + 3] / 255 - idx++ + const idx = 4 * (tmpCol * dx + (iy + tmpRow) * width + ix) + data[j] = image[idx + 3] / 255 + idData[j] = decodeIdRGBA(image[idx], image[idx + 1], image[idx + 2]) + j++ } } tmpCol++ @@ -342,38 +357,38 @@ function fieldFromTexture2d(ctx: Context, texture: Texture, dim: Vec3) { return { field, idField } } -function fieldFromTexture3d(ctx: Context, texture: Texture, dim: Vec3) { - console.time('fieldFromTexture3d') - const { gl } = ctx - const { width, height, depth } = texture - - const space = Tensor.Space(dim, [2, 1, 0], Float32Array) - const data = space.create() - const field = Tensor.create(space, data) - const idData = space.create() - const idField = Tensor.create(space, idData) - - const slice = new Uint8Array(width * height * 4) - - const framebuffer = createFramebuffer(ctx) - framebuffer.bind() - - let j = 0 - for (let i = 0; i < depth; ++i) { - texture.attachFramebuffer(framebuffer, 0, i) - gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, slice) - for (let iy = 0; iy < height; ++iy) { - for (let ix = 0; ix < width; ++ix) { - const idx = 4 * (iy * width + ix) - data[j] = slice[idx + 3] / 255 - idData[j] = decodeIdRGBA(slice[idx], slice[idx + 1], slice[idx + 2]) - ++j - } - } - } - - framebuffer.destroy() - console.timeEnd('fieldFromTexture3d') - - return { field, idField } -} \ No newline at end of file +// function fieldFromTexture3d(ctx: Context, texture: Texture, dim: Vec3) { +// console.time('fieldFromTexture3d') +// const { gl } = ctx +// const { width, height, depth } = texture + +// const space = Tensor.Space(dim, [2, 1, 0], Float32Array) +// const data = space.create() +// const field = Tensor.create(space, data) +// const idData = space.create() +// const idField = Tensor.create(space, idData) + +// const slice = new Uint8Array(width * height * 4) + +// const framebuffer = createFramebuffer(ctx) +// framebuffer.bind() + +// let j = 0 +// for (let i = 0; i < depth; ++i) { +// texture.attachFramebuffer(framebuffer, 0, i) +// gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, slice) +// for (let iy = 0; iy < height; ++iy) { +// for (let ix = 0; ix < width; ++ix) { +// const idx = 4 * (iy * width + ix) +// data[j] = slice[idx + 3] / 255 +// idData[j] = decodeIdRGBA(slice[idx], slice[idx + 1], slice[idx + 2]) +// ++j +// } +// } +// } + +// framebuffer.destroy() +// console.timeEnd('fieldFromTexture3d') + +// return { field, idField } +// } \ No newline at end of file