Skip to content

Commit f3714a8

Browse files
committed
feat(ImageCPRMapper): fix ImageCPRMapper in WebGPU
1 parent 4d59e2d commit f3714a8

File tree

1 file changed

+126
-26
lines changed
  • Sources/Rendering/WebGPU/ImageCPRMapper

1 file changed

+126
-26
lines changed

Sources/Rendering/WebGPU/ImageCPRMapper/index.js

Lines changed: 126 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import vtkWebGPUUniformBuffer from 'vtk.js/Sources/Rendering/WebGPU/UniformBuffe
77
import vtkWebGPUSampler from 'vtk.js/Sources/Rendering/WebGPU/Sampler';
88
import { InterpolationType } from 'vtk.js/Sources/Rendering/Core/ImageProperty/Constants';
99
import { ProjectionMode } from 'vtk.js/Sources/Rendering/Core/ImageCPRMapper/Constants';
10+
import { Resolve } from 'vtk.js/Sources/Rendering/Core/Mapper/CoincidentTopologyHelper';
1011
import { registerOverride } from 'vtk.js/Sources/Rendering/WebGPU/ViewNodeFactory';
1112

1213
const { BufferUsage } = vtkWebGPUBufferManager;
@@ -41,6 +42,8 @@ const cprFragTemplate = `
4142
4243
//VTK::Clip::Dec
4344
45+
//VTK::Coincident::Dec
46+
4447
//VTK::RenderEncoder::Dec
4548
4649
//VTK::IOStructs::Dec
@@ -152,11 +155,15 @@ fn main(
152155
{
153156
var output : fragmentOutput;
154157
155-
let interpolatedOrientation = quaternionLerpOrSlerp(
156-
input.centerlineBotOrientationVS,
157-
input.centerlineTopOrientationVS,
158-
input.quadOffsetVS.y
159-
);
158+
var interpolatedOrientation = mapperUBO.UniformOrientation;
159+
if (mapperUBO.UseUniformOrientation == 0u)
160+
{
161+
interpolatedOrientation = quaternionLerpOrSlerp(
162+
input.centerlineBottomOrientationVS,
163+
input.centerlineTopOrientationVS,
164+
input.quadOffsetVS.y
165+
);
166+
}
160167
let samplingDirection = applyQuaternionToVec(
161168
interpolatedOrientation,
162169
mapperUBO.TangentDirection.xyz
@@ -264,13 +271,16 @@ fn main(
264271
}
265272
266273
//VTK::Select::Impl
274+
//VTK::Coincident::Impl
267275
//VTK::RenderEncoder::Impl
268276
return output;
269277
}
270278
`;
271279

272280
const tmpMat4 = new Float64Array(16);
273281
const tmp2Mat4 = new Float64Array(16);
282+
const DEFAULT_ORIENTATION = [0, 0, 0, 1];
283+
const QUAD_VERTEX_ORDER = [0, 1, 3, 0, 3, 2];
274284

275285
function computeFnToString(property, fn, numberOfComponents) {
276286
const pwfun = fn.apply(property);
@@ -284,6 +294,15 @@ function computeFnToString(property, fn, numberOfComponents) {
284294
function vtkWebGPUImageCPRMapper(publicAPI, model) {
285295
model.classHierarchy.push('vtkWebGPUImageCPRMapper');
286296

297+
publicAPI.getCoincidentParameters = () => {
298+
if (
299+
model.renderable.getResolveCoincidentTopology() === Resolve.PolygonOffset
300+
) {
301+
return model.renderable.getCoincidentTopologyPolygonOffsetParameters();
302+
}
303+
return null;
304+
};
305+
287306
publicAPI.buildPass = (prepass) => {
288307
if (prepass) {
289308
model.WebGPUImageSlice = publicAPI.getFirstAncestorOfType(
@@ -327,7 +346,8 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
327346
model.renderable.getNumberOfClippingPlanes(),
328347
MAX_CLIPPING_PLANES
329348
);
330-
model.pipelineHash = `cprcp${numClipPlanes}${model.renderEncoder.getPipelineHash()}`;
349+
const useCoincident = publicAPI.getCoincidentParameters() ? 1 : 0;
350+
model.pipelineHash = `cprcp${numClipPlanes}co${useCoincident}${model.renderEncoder.getPipelineHash()}`;
331351
};
332352

333353
publicAPI.updateGeometry = () => {
@@ -351,35 +371,46 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
351371
const centerlinePositions = new Float32Array(numVerts * 3);
352372
const quadIndices = new Float32Array(numVerts);
353373
const topOrientations = new Float32Array(numVerts * 4);
354-
const botOrientations = new Float32Array(numVerts * 4);
374+
const bottomOrientations = new Float32Array(numVerts * 4);
355375

356376
const pa = [0, 0, 0];
357377
const pb = [0, 0, 0];
358-
const vertexOrder = [0, 1, 3, 0, 3, 2];
359378

360379
for (let lineIdx = 0; lineIdx < nLines; ++lineIdx) {
361380
pointsDataArray.getPoint(lineIdx, pa);
362381
pointsDataArray.getPoint(lineIdx + 1, pb);
363382

364-
const quadPoints = [
365-
[0, height - distances[lineIdx], 0],
366-
[widthMC, height - distances[lineIdx], 0],
367-
[0, height - distances[lineIdx + 1], 0],
368-
[widthMC, height - distances[lineIdx + 1], 0],
369-
];
370-
const quadCenterline = [pa, pa, pb, pb];
371-
const topQuat = orientationQuats[lineIdx] ?? [0, 0, 0, 1];
372-
const botQuat = orientationQuats[lineIdx + 1] ?? topQuat;
383+
const topY = height - distances[lineIdx];
384+
const bottomY = height - distances[lineIdx + 1];
385+
const topQuat = orientationQuats[lineIdx] ?? DEFAULT_ORIENTATION;
386+
const bottomQuat = orientationQuats[lineIdx + 1] ?? topQuat;
373387

374388
for (let localIdx = 0; localIdx < 6; ++localIdx) {
375-
const quadIdx = vertexOrder[localIdx];
389+
const quadIdx = QUAD_VERTEX_ORDER[localIdx];
376390
const vertIdx = lineIdx * 6 + localIdx;
391+
const posOffset = vertIdx * 3;
392+
const orientationOffset = vertIdx * 4;
393+
394+
positions[posOffset] = quadIdx === 1 || quadIdx === 3 ? widthMC : 0.0;
395+
positions[posOffset + 1] = quadIdx > 1 ? bottomY : topY;
396+
positions[posOffset + 2] = 0.0;
397+
398+
const centerPoint = quadIdx > 1 ? pb : pa;
399+
centerlinePositions[posOffset] = centerPoint[0];
400+
centerlinePositions[posOffset + 1] = centerPoint[1];
401+
centerlinePositions[posOffset + 2] = centerPoint[2];
377402

378-
positions.set(quadPoints[quadIdx], vertIdx * 3);
379-
centerlinePositions.set(quadCenterline[quadIdx], vertIdx * 3);
380403
quadIndices[vertIdx] = quadIdx;
381-
topOrientations.set(topQuat, vertIdx * 4);
382-
botOrientations.set(botQuat, vertIdx * 4);
404+
405+
topOrientations[orientationOffset] = topQuat[0];
406+
topOrientations[orientationOffset + 1] = topQuat[1];
407+
topOrientations[orientationOffset + 2] = topQuat[2];
408+
topOrientations[orientationOffset + 3] = topQuat[3];
409+
410+
bottomOrientations[orientationOffset] = bottomQuat[0];
411+
bottomOrientations[orientationOffset + 1] = bottomQuat[1];
412+
bottomOrientations[orientationOffset + 2] = bottomQuat[2];
413+
bottomOrientations[orientationOffset + 3] = bottomQuat[3];
383414
}
384415
}
385416

@@ -394,7 +425,7 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
394425
const centerlineHash = `cpr-centerline-${mtime}`;
395426
const quadHash = `cpr-quad-${mtime}`;
396427
const topHash = `cpr-top-${mtime}`;
397-
const botHash = `cpr-bot-${mtime}`;
428+
const bottomHash = `cpr-bottom-${mtime}`;
398429

399430
const requests = [
400431
[vertexHash, positions, 'float32x3', ['vertexMC']],
@@ -406,7 +437,12 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
406437
],
407438
[quadHash, quadIndices, 'float32', ['quadIndex']],
408439
[topHash, topOrientations, 'float32x4', ['centerlineTopOrientation']],
409-
[botHash, botOrientations, 'float32x4', ['centerlineBotOrientation']],
440+
[
441+
bottomHash,
442+
bottomOrientations,
443+
'float32x4',
444+
['centerlineBottomOrientation'],
445+
],
410446
];
411447

412448
requests.forEach(([hash, nativeArray, format, names]) => {
@@ -612,6 +648,14 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
612648
...model.renderable.getBitangentDirection(),
613649
0.0,
614650
]);
651+
model.UBO.setArray(
652+
'UniformOrientation',
653+
model.renderable.getUniformOrientation()
654+
);
655+
model.UBO.setValue(
656+
'UseUniformOrientation',
657+
model.renderable.getUseUniformOrientation() ? 1 : 0
658+
);
615659
model.UBO.setValue('Width', model.renderable.getWidth());
616660
model.UBO.setValue('Opacity', property.getOpacity());
617661
model.UBO.setValue('PropID', model.WebGPUImageSlice.getPropID());
@@ -634,6 +678,13 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
634678
model.UBO.setArray(`ClipPlane${i}`, clipPlane);
635679
}
636680

681+
const coincidentParameters = publicAPI.getCoincidentParameters();
682+
model.UBO.setValue('CoincidentFactor', coincidentParameters?.factor ?? 0.0);
683+
model.UBO.setValue(
684+
'CoincidentOffset',
685+
0.000016 * (coincidentParameters?.offset ?? 0.0)
686+
);
687+
637688
const projectionSamples =
638689
model.renderable.getProjectionSlabNumberOfSamples();
639690
const projectionThickness = model.renderable.getProjectionSlabThickness();
@@ -730,7 +781,7 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
730781
vDesc.addOutput('vec2<f32>', 'quadOffsetVS');
731782
vDesc.addOutput('vec3<f32>', 'centerlinePosVS');
732783
vDesc.addOutput('vec4<f32>', 'centerlineTopOrientationVS');
733-
vDesc.addOutput('vec4<f32>', 'centerlineBotOrientationVS');
784+
vDesc.addOutput('vec4<f32>', 'centerlineBottomOrientationVS');
734785

735786
let code = vDesc.getCode();
736787
code = vtkWebGPUShaderCache.substitute(code, '//VTK::ImageCPR::Impl', [
@@ -742,7 +793,7 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
742793
');',
743794
'output.centerlinePosVS = centerlinePosition;',
744795
'output.centerlineTopOrientationVS = centerlineTopOrientation;',
745-
'output.centerlineBotOrientationVS = centerlineBotOrientation;',
796+
'output.centerlineBottomOrientationVS = centerlineBottomOrientation;',
746797
'let posSC = mapperUBO.BCSCMatrix * vec4<f32>(vertexMC, 1.0);',
747798
'output.Position = rendererUBO.SCPCMatrix * posSC;',
748799
]).result;
@@ -789,6 +840,51 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
789840
publicAPI.replaceShaderClip
790841
);
791842

843+
publicAPI.replaceShaderCoincident = (hash, pipeline) => {
844+
const fDesc = pipeline.getShaderDescription('fragment');
845+
let code = fDesc.getCode();
846+
const coincidentParameters = publicAPI.getCoincidentParameters();
847+
848+
if (
849+
!coincidentParameters ||
850+
(coincidentParameters.factor === 0.0 &&
851+
coincidentParameters.offset === 0.0)
852+
) {
853+
code = vtkWebGPUShaderCache.substitute(
854+
code,
855+
'//VTK::Coincident::Dec',
856+
[]
857+
).result;
858+
code = vtkWebGPUShaderCache.substitute(
859+
code,
860+
'//VTK::Coincident::Impl',
861+
[]
862+
).result;
863+
fDesc.setCode(code);
864+
return;
865+
}
866+
867+
fDesc.addBuiltinInput('vec4<f32>', '@builtin(position) fragPos');
868+
fDesc.addBuiltinOutput('f32', '@builtin(frag_depth) fragDepth');
869+
code = vtkWebGPUShaderCache.substitute(
870+
code,
871+
'//VTK::Coincident::Dec',
872+
[]
873+
).result;
874+
code = vtkWebGPUShaderCache.substitute(code, '//VTK::Coincident::Impl', [
875+
'output.fragDepth = clamp(',
876+
' input.fragPos.z + mapperUBO.CoincidentOffset,',
877+
' 0.0,',
878+
' 1.0',
879+
');',
880+
]).result;
881+
fDesc.setCode(code);
882+
};
883+
model.shaderReplacements.set(
884+
'replaceShaderCoincident',
885+
publicAPI.replaceShaderCoincident
886+
);
887+
792888
publicAPI.replaceShaderRenderEncoder = (hash, pipeline) => {
793889
if (hash.includes('sel')) {
794890
const fDesc = pipeline.getShaderDescription('fragment');
@@ -833,6 +929,7 @@ export function extend(publicAPI, model, initialValues = {}) {
833929
model.UBO.addEntry('BackgroundColor', 'vec4<f32>');
834930
model.UBO.addEntry('VolumeSizeMC', 'vec4<f32>');
835931
model.UBO.addEntry('GlobalCenterPoint', 'vec4<f32>');
932+
model.UBO.addEntry('UniformOrientation', 'vec4<f32>');
836933
model.UBO.addEntry('TangentDirection', 'vec4<f32>');
837934
model.UBO.addEntry('BitangentDirection', 'vec4<f32>');
838935
model.UBO.addEntry('ComponentMix', 'vec4<f32>');
@@ -849,12 +946,15 @@ export function extend(publicAPI, model, initialValues = {}) {
849946
model.UBO.addEntry('ClipPlane5', 'vec4<f32>');
850947
model.UBO.addEntry('Width', 'f32');
851948
model.UBO.addEntry('Opacity', 'f32');
949+
model.UBO.addEntry('CoincidentFactor', 'f32');
950+
model.UBO.addEntry('CoincidentOffset', 'f32');
852951
model.UBO.addEntry('PropID', 'u32');
853952
model.UBO.addEntry('NumClipPlanes', 'u32');
854953
model.UBO.addEntry('ProjectionSamples', 'u32');
855954
model.UBO.addEntry('ProjectionMode', 'u32');
856955
model.UBO.addEntry('NumComponents', 'u32');
857956
model.UBO.addEntry('IndependentComponents', 'u32');
957+
model.UBO.addEntry('UseUniformOrientation', 'u32');
858958
model.UBO.addEntry('UseCenterPoint', 'u32');
859959

860960
vtkWebGPUImageCPRMapper(publicAPI, model);

0 commit comments

Comments
 (0)