Skip to content
Snippets Groups Projects
Commit d6ffbae8 authored by Alexander Rose's avatar Alexander Rose
Browse files

gaussian density groupid calc for via 2d textures

parent 2f6b2989
No related branches found
No related tags found
No related merge requests found
......@@ -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
......
......@@ -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
......
......@@ -10,12 +10,19 @@ precision highp float;
varying vec3 vPosition;
varying float vRadius;
#if defined(dCalcType_groupId)
#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
......
/**
* 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) {
......
/**
* 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
/**
* 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
/**
* 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
/**
* 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
......@@ -107,3 +107,18 @@ 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
......@@ -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,
......
......@@ -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,51 +59,34 @@ 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)
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 >= fboTexCols) {
currCol -= fboTexCols
if (currCol >= texCols) {
currCol -= texCols
currY += dy
currX = 0
}
......@@ -112,6 +98,16 @@ async function GaussianDensityTexture2d(ctx: RuntimeContext, webgl: Context, pos
++currCol
currX += dx
}
}
setupMinDistanceRendering(webgl, renderable)
render(minDistanceTexture)
setupDensityRendering(webgl, renderable)
render(texture)
setupGroupIdRendering(webgl, renderable)
render(texture)
framebuffer.destroy() // clean up
......@@ -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)
function render(fbTex: Texture) {
for (let i = 0; i < dz; ++i) {
ValueCell.update(uCurrentSlice, i)
minDistanceTexture.attachFramebuffer(framebuffer, 0, 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')
}
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')
}
setupMinDistanceRendering(webgl, renderable)
render(minDistanceTexture)
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment