@thewtex/vtk.js-esm
Version:
Visualization Toolkit for the Web
499 lines (389 loc) • 23.4 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import macro from '../../macro.js';
import vtkWebGPUFullScreenQuad from './FullScreenQuad.js';
import vtkWebGPUUniformBuffer from './UniformBuffer.js';
import vtkWebGPUShaderCache from './ShaderCache.js';
import vtkWebGPUStorageBuffer from './StorageBuffer.js';
import vtkWebGPUSampler from './Sampler.js';
import { BlendMode } from '../Core/VolumeMapper/Constants.js';
import { i as identity, t as translate, j as transpose, g as invert, m as multiply, s as scale } from '../../vendor/gl-matrix/esm/mat4.js';
var volFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n// dummy for now till they support 3d textures\nfn getTextureValue(vTex: texture_3d<f32>, tpos: vec4<f32>, vNum: i32) -> f32\n{\n return textureSampleLevel(vTex, clampSampler, tpos.xyz, 0.0).a;\n // 512.0 * f32(vNum) + 20.0 * trunc(10.0 * tpos.z);\n}\n\nfn getGradient(vTex: texture_3d<f32>, tpos: vec4<f32>, vNum: i32, scalar: f32) -> vec4<f32>\n{\n var result: vec4<f32>;\n\n var tstep: vec4<f32> = volumeSSBO.values[vNum].tstep;\n result.x = getTextureValue(vTex, tpos + vec4<f32>(tstep.x, 0.0, 0.0, 1.0), vNum) - scalar;\n result.y = getTextureValue(vTex, tpos + vec4<f32>(0.0, tstep.y, 0.0, 1.0), vNum) - scalar;\n result.z = getTextureValue(vTex, tpos + vec4<f32>(0.0, 0.0, tstep.z, 1.0), vNum) - scalar;\n\n // divide by spacing\n result = result / volumeSSBO.values[vNum].spacing;\n\n var grad: f32 = length(result.xyz);\n\n // // rotate to View Coords\n // result.xyz =\n // result.x * vPlaneNormal0 +\n // result.y * vPlaneNormal2 +\n // result.z * vPlaneNormal4;\n\n if (grad > 0.0)\n {\n result = result * (1.0 / grad);\n }\n\n result.w = grad;\n\n return result;\n}\n\nfn processVolume(vTex: texture_3d<f32>, vNum: i32, cNum: i32, posSC: vec4<f32>, tfunRows: f32) -> vec4<f32>\n{\n var outColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n\n // convert to tcoords and reject if outside the volume\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*posSC;\n // var tpos: vec4<f32> = posSC*0.003;\n if (tpos.x < 0.0 || tpos.y < 0.0 || tpos.z < 0.0 ||\n tpos.x > 1.0 || tpos.y > 1.0 || tpos.z > 1.0) { return outColor; }\n\n var scalar: f32 = getTextureValue(vTex, tpos, vNum);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n coord.x = scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift;\n\n var gofactor: f32 = 1.0;\n if (componentSSBO.values[cNum].gomin < 1.0)\n {\n var normal: vec4<f32> = getGradient(vTex, tpos, vNum, scalar);\n gofactor = clamp(normal.a*componentSSBO.values[cNum].goScale + componentSSBO.values[cNum].goShift,\n componentSSBO.values[cNum].gomin, componentSSBO.values[cNum].gomax);\n }\n var opacity: f32 = gofactor*textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n outColor = vec4<f32>(color.rgb, opacity);\n\n //VTK::Volume::Process\n\n return outColor;\n}\n\nfn composite(rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>) -> vec4<f32>\n{\n // initial ray position is at the beginning\n var rayPosSC: vec4<f32> = minPosSC;\n\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var curDist: f32 = 0.0;\n var computedColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n var sampleColor: vec4<f32>;\n loop\n {\n // for each volume, sample and accumulate color\n\n//VTK::Volume::Calls\n\n // increment position\n curDist = curDist + mapperUBO.SampleDistance;\n rayPosSC = rayPosSC + rayStepSC;\n\n // check if we have reached a terminating condition\n if (curDist > rayLengthSC) { break; }\n if (computedColor.a > 0.98) { break; }\n }\n return computedColor;\n}\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var rayMax: f32 = textureSampleLevel(maxTexture, clampSampler, input.tcoordVS, 0.0).r;\n var rayMin: f32 = textureSampleLevel(minTexture, clampSampler, input.tcoordVS, 0.0).r;\n\n // discard empty rays\n if (rayMax <= rayMin) { discard; }\n else\n {\n var winDimsI32: vec2<i32> = textureDimensions(minTexture);\n var winDims: vec2<f32> = vec2<f32>(f32(winDimsI32.x), f32(winDimsI32.y));\n\n // compute start and end ray positions in view coordinates\n var minPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0*input.fragPos.x/winDims.x - 1.0, 1.0 - 2.0 * input.fragPos.y/winDims.y, rayMin, 1.0);\n minPosSC = minPosSC * (1.0 / minPosSC.w);\n var maxPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0*input.fragPos.x/winDims.x - 1.0, 1.0 - 2.0 * input.fragPos.y/winDims.y, rayMax, 1.0);\n maxPosSC = maxPosSC * (1.0 / maxPosSC.w);\n\n var rayLengthSC: f32 = distance(minPosSC.xyz, maxPosSC.xyz);\n var rayStepSC: vec4<f32> = (maxPosSC - minPosSC)*(mapperUBO.SampleDistance/rayLengthSC);\n rayStepSC.w = 0.0;\n\n //VTK::Volume::Loop\n\n // var computedColor: vec4<f32> = vec4<f32>(rayMin, rayMax, 0.0, min(100.0*(rayMax - rayMin), 1.0));\n // computedColor = vec4<f32>(rayLengthSC / 500.0, 1.0, 0.0, 1.0);\n // computedColor = vec4<f32>(maxPosSC.xyz*0.01, 1.0);\n\n //VTK::RenderEncoder::Impl\n }\n\n return output;\n}\n";
var tmpMat4 = new Float64Array(16);
var tmp2Mat4 = new Float64Array(16); // ----------------------------------------------------------------------------
// vtkWebGPUVolumePassFSQ methods
// ----------------------------------------------------------------------------
function vtkWebGPUVolumePassFSQ(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkWebGPUVolumePassFSQ');
publicAPI.replaceShaderPosition = function (hash, pipeline, vertexInput) {
var vDesc = pipeline.getShaderDescription('vertex');
vDesc.addBuiltinOutput('vec4<f32>', '[[builtin(position)]] Position');
var code = vDesc.getCode();
code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', ['output.tcoordVS = vec2<f32>(vertexBC.x * 0.5 + 0.5, 1.0 - vertexBC.y * 0.5 - 0.5);', 'output.Position = vec4<f32>(vertexBC, 1.0);']).result;
vDesc.setCode(code);
var fDesc = pipeline.getShaderDescription('fragment');
fDesc.addBuiltinInput('vec4<f32>', '[[builtin(position)]] fragPos');
};
model.shaderReplacements.set('replaceShaderPosition', publicAPI.replaceShaderPosition);
publicAPI.replaceShaderVolume = function (hash, pipeline, vertexInput) {
var fDesc = pipeline.getShaderDescription('fragment');
var code = fDesc.getCode();
var calls = [];
for (var i = 0; i < model.volumes.length; i++) {
// todo pass rowPos
calls.push(" sampleColor = processVolume(volTexture".concat(i, ", ").concat(i, ", ").concat(model.rowStarts[i], ", rayPosSC, tfunRows);"));
calls.push(" computedColor = vec4<f32>(\n sampleColor.a * sampleColor.rgb * (1.0 - computedColor.a) + computedColor.rgb,\n (1.0 - computedColor.a)*sampleColor.a + computedColor.a);");
}
code = vtkWebGPUShaderCache.substitute(code, '//VTK::Volume::Calls', calls).result;
if (model.blendMode === BlendMode.COMPOSITE_BLEND) {
code = vtkWebGPUShaderCache.substitute(code, '//VTK::Volume::Loop', ['var computedColor: vec4<f32> = composite(rayLengthSC, minPosSC, rayStepSC);']).result;
}
fDesc.setCode(code);
};
model.shaderReplacements.set('replaceShaderVolume', publicAPI.replaceShaderVolume);
publicAPI.updateLUTImage = function (device) {
// depends on
// - volumes array (length and values) - mtime
// - tfun arrays - renderable/property mtime
var mtime = publicAPI.getMTime();
for (var i = 0; i < model.volumes.length; i++) {
var vol = model.volumes[i].getRenderable();
var image = vol.getMapper().getInputData();
mtime = Math.max(mtime, vol.getMTime(), image.getMTime());
}
if (mtime < model.lutBuildTime.getMTime()) {
return;
} // first determine how large the image should be
model.numRows = 0;
model.rowStarts = [];
for (var vidx = 0; vidx < model.volumes.length; vidx++) {
model.rowStarts.push(model.numRows);
var webgpuvol = model.volumes[vidx];
var actor = webgpuvol.getRenderable();
var volMapr = actor.getMapper();
var vprop = actor.getProperty();
var _image = volMapr.getInputData();
var scalars = _image.getPointData() && _image.getPointData().getScalars();
var numComp = scalars.getNumberOfComponents();
var iComps = vprop.getIndependentComponents();
var numIComps = iComps ? numComp : 1;
model.numRows += numIComps;
} // allocate the image array
var colorArray = new Uint8Array(model.numRows * 2 * model.rowLength * 4);
var opacityArray = new Float32Array(model.numRows * 2 * model.rowLength);
var imgRow = 0;
var tmpTable = new Float32Array(model.rowLength * 3);
var rowLength = model.rowLength;
for (var _vidx = 0; _vidx < model.volumes.length; _vidx++) {
var _webgpuvol = model.volumes[_vidx];
var _actor = _webgpuvol.getRenderable();
var _volMapr = _actor.getMapper();
var _vprop = _actor.getProperty();
var _image2 = _volMapr.getInputData();
var _scalars = _image2.getPointData() && _image2.getPointData().getScalars();
var _numComp = _scalars.getNumberOfComponents();
var _iComps = _vprop.getIndependentComponents();
var _numIComps = _iComps ? _numComp : 1;
for (var c = 0; c < _numIComps; ++c) {
var cfun = _vprop.getRGBTransferFunction(c);
var cRange = cfun.getRange();
cfun.getTable(cRange[0], cRange[1], rowLength, tmpTable, 1);
var ioffset = imgRow * rowLength * 4;
for (var _i = 0; _i < rowLength; ++_i) {
colorArray[ioffset + _i * 4] = 255.0 * tmpTable[_i * 3];
colorArray[ioffset + _i * 4 + 1] = 255.0 * tmpTable[_i * 3 + 1];
colorArray[ioffset + _i * 4 + 2] = 255.0 * tmpTable[_i * 3 + 2];
colorArray[ioffset + _i * 4 + 3] = 255.0;
for (var co = 0; co < 4; co++) {
colorArray[ioffset + (rowLength + _i) * 4 + co] = colorArray[ioffset + _i * 4 + co];
}
}
var ofun = _vprop.getScalarOpacity(c);
var opacityFactor = model.sampleDist / _vprop.getScalarOpacityUnitDistance(c);
var oRange = ofun.getRange();
ofun.getTable(oRange[0], oRange[1], rowLength, tmpTable, 1); // adjust for sample distance etc
ioffset = imgRow * rowLength;
for (var _i2 = 0; _i2 < rowLength; ++_i2) {
opacityArray[ioffset + _i2] = 1.0 - Math.pow(1.0 - tmpTable[_i2], opacityFactor);
opacityArray[ioffset + _i2 + rowLength] = opacityArray[ioffset + _i2];
}
imgRow += 2;
}
}
{
var treq = {
nativeArray: colorArray,
width: model.rowLength,
height: model.numRows * 2,
depth: 1,
format: 'rgba8unorm'
};
var newTex = device.getTextureManager().getTexture(treq);
var tview = newTex.createView();
tview.setName('tfunTexture');
model.textureViews[2] = tview;
}
{
var _treq = {
nativeArray: opacityArray,
width: model.rowLength,
height: model.numRows * 2,
depth: 1,
format: 'r32float'
};
var _newTex = device.getTextureManager().getTexture(_treq);
var _tview = _newTex.createView();
_tview.setName('ofunTexture');
model.textureViews[3] = _tview;
}
model.lutBuildTime.modified();
};
publicAPI.updateSSBO = function (device) {
// if any of
// - color or opacity tfun ranges changed - volume Mtime
// - any volume matrix changed - volume MTime
// - stabilized center changed - ren.stabilizedMTime
// - any volume's input data worldtoindex or dimensions changed - input's mtime
//
var mtime = Math.max(publicAPI.getMTime(), model.WebGPURenderer.getStabilizedTime());
for (var i = 0; i < model.volumes.length; i++) {
var vol = model.volumes[i].getRenderable();
var image = vol.getMapper().getInputData();
mtime = Math.max(mtime, vol.getMTime(), image.getMTime());
}
if (mtime < model.SSBO.getSendTime()) {
return;
} // create the volumeSBBO
var center = model.WebGPURenderer.getStabilizedCenterByReference();
model.SSBO.clearData();
model.SSBO.setNumberOfInstances(model.volumes.length); // create SCTC matrices SC -> world -> model -> index -> tcoord
//
// when doing coord conversions from A to C recall
// the order is mat4.mult(AtoC, BtoC, AtoB);
//
var marray = new Float64Array(model.volumes.length * 16);
var tstepArray = new Float64Array(model.volumes.length * 4);
var spacingArray = new Float64Array(model.volumes.length * 4);
for (var vidx = 0; vidx < model.volumes.length; vidx++) {
var webgpuvol = model.volumes[vidx];
var actor = webgpuvol.getRenderable();
var volMapr = actor.getMapper();
var _image3 = volMapr.getInputData();
identity(tmpMat4);
translate(tmpMat4, tmpMat4, center); // tmpMat4 is now SC->World
var _vol = model.volumes[vidx];
var mcwcmat = _vol.getRenderable().getMatrix();
transpose(tmp2Mat4, mcwcmat);
invert(tmp2Mat4, tmp2Mat4); // tmp2Mat4 is now world to model
multiply(tmpMat4, tmp2Mat4, tmpMat4); // tmp4Mat is now SC->Model
// the method on the data is world to index but the volume is in
// model coordinates so really in this context it is model to index
var modelToIndex = _image3.getWorldToIndex();
transpose(tmp2Mat4, modelToIndex);
multiply(tmpMat4, tmp2Mat4, tmpMat4); // tmpMat4 is now SC -> Index
var dims = _image3.getDimensions();
identity(tmp2Mat4);
scale(tmp2Mat4, tmp2Mat4, [1.0 / dims[0], 1.0 / dims[1], 1.0 / dims[2]]);
multiply(tmpMat4, tmp2Mat4, tmpMat4); // tmpMat4 is now SC -> Tcoord
for (var j = 0; j < 16; j++) {
marray[vidx * 16 + j] = tmpMat4[j];
}
tstepArray[vidx * 4] = 1.0 / dims[0];
tstepArray[vidx * 4 + 1] = 1.0 / dims[1];
tstepArray[vidx * 4 + 2] = 1.0 / dims[2];
tstepArray[vidx * 4 + 3] = 1.0;
var spacing = _image3.getSpacing();
spacingArray[vidx * 4] = spacing[0];
spacingArray[vidx * 4 + 1] = spacing[1];
spacingArray[vidx * 4 + 2] = spacing[2];
spacingArray[vidx * 4 + 3] = 1.0;
}
model.SSBO.addEntry('SCTCMatrix', 'mat4x4<f32>');
model.SSBO.addEntry('tstep', 'vec4<f32>');
model.SSBO.addEntry('spacing', 'vec4<f32>');
model.SSBO.setAllInstancesFromArray('SCTCMatrix', marray);
model.SSBO.setAllInstancesFromArray('tstep', tstepArray);
model.SSBO.setAllInstancesFromArray('spacing', spacingArray);
model.SSBO.send(device); // now create the componentSSBO
model.componentSSBO.clearData();
model.componentSSBO.setNumberOfInstances(model.numRows);
var cScaleArray = new Float64Array(model.numRows);
var cShiftArray = new Float64Array(model.numRows);
var oScaleArray = new Float64Array(model.numRows);
var oShiftArray = new Float64Array(model.numRows);
var gominArray = new Float64Array(model.numRows);
var gomaxArray = new Float64Array(model.numRows);
var goshiftArray = new Float64Array(model.numRows);
var goscaleArray = new Float64Array(model.numRows);
var rowIdx = 0;
for (var _vidx2 = 0; _vidx2 < model.volumes.length; _vidx2++) {
var _webgpuvol2 = model.volumes[_vidx2];
var _actor2 = _webgpuvol2.getRenderable();
var _volMapr2 = _actor2.getMapper();
var vprop = _actor2.getProperty();
var _image4 = _volMapr2.getInputData();
var scalars = _image4.getPointData() && _image4.getPointData().getScalars();
var numComp = scalars.getNumberOfComponents();
var iComps = vprop.getIndependentComponents(); // const numIComps = iComps ? numComp : 1;
var volInfo = {
scale: [255.0],
offset: [0.0]
}; // three levels of shift scale combined into one
// for performance in the fragment shader
for (var compIdx = 0; compIdx < numComp; compIdx++) {
var target = iComps ? compIdx : 0;
var sscale = volInfo.scale[compIdx];
var ofun = vprop.getScalarOpacity(target);
var oRange = ofun.getRange();
var oscale = sscale / (oRange[1] - oRange[0]);
var oshift = (volInfo.offset[compIdx] - oRange[0]) / (oRange[1] - oRange[0]);
oShiftArray[rowIdx] = oshift;
oScaleArray[rowIdx] = oscale;
var cfun = vprop.getRGBTransferFunction(target);
var cRange = cfun.getRange();
cShiftArray[rowIdx] = (volInfo.offset[compIdx] - cRange[0]) / (cRange[1] - cRange[0]);
cScaleArray[rowIdx] = sscale / (cRange[1] - cRange[0]); // todo sscale for dependent should be based off of the A channel?
// not target (which is 0 in that case)
var useGO = vprop.getUseGradientOpacity(target);
if (useGO) {
var gomin = vprop.getGradientOpacityMinimumOpacity(target);
var gomax = vprop.getGradientOpacityMaximumOpacity(target);
gominArray[rowIdx] = gomin;
gomaxArray[rowIdx] = gomax;
var goRange = [vprop.getGradientOpacityMinimumValue(target), vprop.getGradientOpacityMaximumValue(target)];
goscaleArray[rowIdx] = sscale * (gomax - gomin) / (goRange[1] - goRange[0]);
goshiftArray[rowIdx] = -goRange[0] * (gomax - gomin) / (goRange[1] - goRange[0]) + gomin;
} else {
gominArray[rowIdx] = 1.0;
gomaxArray[rowIdx] = 1.0;
goscaleArray[rowIdx] = 0.0;
goshiftArray[rowIdx] = 1.0;
}
rowIdx++;
}
}
model.componentSSBO.addEntry('cScale', 'f32');
model.componentSSBO.addEntry('cShift', 'f32');
model.componentSSBO.addEntry('oScale', 'f32');
model.componentSSBO.addEntry('oShift', 'f32');
model.componentSSBO.addEntry('goShift', 'f32');
model.componentSSBO.addEntry('goScale', 'f32');
model.componentSSBO.addEntry('gomin', 'f32');
model.componentSSBO.addEntry('gomax', 'f32');
model.componentSSBO.setAllInstancesFromArray('cScale', cScaleArray);
model.componentSSBO.setAllInstancesFromArray('cShift', cShiftArray);
model.componentSSBO.setAllInstancesFromArray('oScale', oScaleArray);
model.componentSSBO.setAllInstancesFromArray('oShift', oShiftArray);
model.componentSSBO.setAllInstancesFromArray('goScale', goscaleArray);
model.componentSSBO.setAllInstancesFromArray('goShift', goshiftArray);
model.componentSSBO.setAllInstancesFromArray('gomin', gominArray);
model.componentSSBO.setAllInstancesFromArray('gomax', gomaxArray);
model.componentSSBO.send(device);
};
publicAPI.updateBuffers = function (device) {
// compute the min step size
var sampleDist = model.volumes[0].getRenderable().getMapper().getSampleDistance();
for (var i = 0; i < model.volumes.length; i++) {
var vol = model.volumes[i];
var volMapr = vol.getRenderable().getMapper();
var sd = volMapr.getSampleDistance();
if (sd < sampleDist) {
sampleDist = sd;
}
}
if (model.sampleDist !== sampleDist) {
model.sampleDist = sampleDist;
model.UBO.setValue('SampleDistance', sampleDist);
model.UBO.sendIfNeeded(device);
}
publicAPI.updateLUTImage(device);
publicAPI.updateSSBO(device); // add in 3d volume textures
for (var vidx = 0; vidx < model.volumes.length; vidx++) {
var webgpuvol = model.volumes[vidx];
var actor = webgpuvol.getRenderable();
var _volMapr3 = actor.getMapper();
var image = _volMapr3.getInputData();
var treq = {
imageData: image,
source: image
};
var newTex = device.getTextureManager().getTexture(treq);
if (!model.textureViews[vidx + 4] || model.textureViews[vidx + 4].getTexture() !== newTex) {
var tview = newTex.createView();
tview.setName("volTexture".concat(vidx));
model.textureViews[vidx + 4] = tview;
}
}
};
publicAPI.computePipelineHash = function () {
var blendMode = model.volumes[0].getRenderable().getMapper().getBlendMode();
model.blendMode = blendMode;
model.pipelineHash = "volfsq".concat(model.volumes.length, "b").concat(model.blendMode);
}; // marks modified when needed
publicAPI.setVolumes = function (val) {
if (!model.volumes || model.volumes.length !== val.length) {
model.volumes = _toConsumableArray(val);
publicAPI.modified();
return;
}
for (var i = 0; i < val.length; i++) {
if (val[i] !== model.volumes[i]) {
model.volumes = _toConsumableArray(val);
publicAPI.modified();
return;
}
}
};
var superclassBuild = publicAPI.build;
publicAPI.build = function (renderEncoder, device) {
publicAPI.computePipelineHash();
publicAPI.updateBuffers(device);
if (!model.clampSampler) {
model.clampSampler = vtkWebGPUSampler.newInstance();
model.clampSampler.setName('clampSampler');
model.clampSampler.create(device, {
minFilter: 'linear',
maxFilter: 'linear'
});
}
superclassBuild(renderEncoder, device);
};
var superclassGetBindables = publicAPI.getBindables;
publicAPI.getBindables = function () {
var bindables = superclassGetBindables();
bindables.push(model.componentSSBO);
bindables.push(model.clampSampler);
return bindables;
};
} // ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
var DEFAULT_VALUES = {
volumes: null,
rowLength: 1024
}; // ----------------------------------------------------------------------------
function extend(publicAPI, model) {
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance
vtkWebGPUFullScreenQuad.extend(publicAPI, model, initialValues);
model.fragmentShaderTemplate = volFragTemplate;
model.UBO = vtkWebGPUUniformBuffer.newInstance();
model.UBO.setName('mapperUBO');
model.UBO.addEntry('SampleDistance', 'f32');
model.SSBO = vtkWebGPUStorageBuffer.newInstance();
model.SSBO.setName('volumeSSBO');
model.componentSSBO = vtkWebGPUStorageBuffer.newInstance();
model.componentSSBO.setName('componentSSBO');
model.lutBuildTime = {};
macro.obj(model.lutBuildTime, {
mtime: 0
}); // Object methods
vtkWebGPUVolumePassFSQ(publicAPI, model);
} // ----------------------------------------------------------------------------
var newInstance = macro.newInstance(extend, 'vtkWebGPUVolumePassFSQ'); // ----------------------------------------------------------------------------
var vtkWebGPUVolumePassFSQ$1 = {
newInstance: newInstance,
extend: extend
};
export default vtkWebGPUVolumePassFSQ$1;
export { extend, newInstance };