Skip to content

Commit 7317248

Browse files
bruyeretfinetjul
authored andcommitted
feat(VolumeMapper): Add forceNearestInterpolation
1 parent c7ba534 commit 7317248

File tree

6 files changed

+114
-39
lines changed

6 files changed

+114
-39
lines changed

Sources/Rendering/Core/VolumeMapper/example/controller.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010
</fieldset>
1111
<select id='volume' style="margin: 5px;"></select>
1212
<select id='preset' style="margin: 5px; display: none;"></select>
13+
<div id="forceNearest"></div>
1314
</form>

Sources/Rendering/Core/VolumeMapper/example/index.js

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true });
4949
const volumeOptions = {};
5050
const volumeSelectElem = document.getElementById('volume');
5151
const presetSelectElem = document.getElementById('preset');
52+
const forceNearestElem = document.getElementById('forceNearest');
5253

5354
const actor = vtkVolume.newInstance();
5455
const mapper = vtkVolumeMapper.newInstance();
@@ -144,6 +145,14 @@ if (light.getPositional()) {
144145
renderer.addActor(lca);
145146
}
146147

148+
{
149+
const optionElem = document.createElement('option');
150+
optionElem.label = 'Default';
151+
optionElem.value = '';
152+
presetSelectElem.appendChild(optionElem);
153+
presetSelectElem.value = optionElem.value;
154+
}
155+
147156
Object.keys(ColorMixPreset).forEach((key) => {
148157
if (key === 'CUSTOM') {
149158
// Don't enable custom mode
@@ -157,20 +166,32 @@ Object.keys(ColorMixPreset).forEach((key) => {
157166
presetSelectElem.appendChild(optionElem);
158167
});
159168

160-
{
161-
const optionElem = document.createElement('option');
162-
optionElem.label = 'Default';
163-
optionElem.value = '';
164-
presetSelectElem.appendChild(optionElem);
165-
presetSelectElem.value = optionElem.value;
166-
}
167-
168169
const setColorMixPreset = (presetKey) => {
169170
const preset = presetKey ? ColorMixPreset[presetKey] : null;
170171
actor.getProperty().setColorMixPreset(preset);
171172
presetSelectElem.value = presetKey;
172173
};
173174

175+
function updateForceNearestElem(comp) {
176+
forceNearestElem.replaceChildren();
177+
for (let c = 0; c < comp; ++c) {
178+
const checkboxElem = document.createElement('input');
179+
checkboxElem.type = 'checkbox';
180+
checkboxElem.checked = actor.getProperty().getForceNearestInterpolation(c);
181+
checkboxElem.addEventListener('change', () => {
182+
actor.getProperty().setForceNearestInterpolation(c, checkboxElem.checked);
183+
renderWindow.render();
184+
});
185+
forceNearestElem.appendChild(checkboxElem);
186+
const labelElem = document.createElement('label');
187+
labelElem.innerText = `Force linear interpolation for component ${c}`;
188+
forceNearestElem.appendChild(labelElem);
189+
forceNearestElem.appendChild(document.createElement('br'));
190+
}
191+
}
192+
193+
updateForceNearestElem(1);
194+
174195
volumeSelectElem.addEventListener('change', () => {
175196
const { comp, data } = volumeOptions[volumeSelectElem.value];
176197
if (comp === 1) {
@@ -179,6 +200,7 @@ volumeSelectElem.addEventListener('change', () => {
179200
} else {
180201
presetSelectElem.style.display = 'block';
181202
}
203+
updateForceNearestElem(comp);
182204
const array = mapper.getInputData().getPointData().getArray(0);
183205
array.setData(data);
184206
array.setNumberOfComponents(comp);

Sources/Rendering/Core/VolumeProperty/index.d.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ export interface vtkVolumeProperty extends vtkObject {
135135
*/
136136
getUseGradientOpacity(index: number): boolean;
137137

138+
/**
139+
* @see setForceNearestInterpolation
140+
* @param {Number} index
141+
*/
142+
getForceNearestInterpolation(index: number): boolean;
143+
138144
/**
139145
*
140146
*/
@@ -315,6 +321,15 @@ export interface vtkVolumeProperty extends vtkObject {
315321
*/
316322
setComponentWeight(index: number, value: number): boolean;
317323

324+
/**
325+
* Force the nearest neighbor interpolation of one or more of the components
326+
* The interpolation for the rest of the volume is set using `setInterpolationType`
327+
* @see setInterpolationType
328+
* @param {Number} index
329+
* @param {Boolean} value
330+
*/
331+
setForceNearestInterpolation(index: number, value: boolean): boolean;
332+
318333
/**
319334
* Get the scalar component weights.
320335
* @param {Number} index

Sources/Rendering/Core/VolumeProperty/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ function vtkVolumeProperty(publicAPI, model) {
211211
'gradientOpacityMaximumValue',
212212
'gradientOpacityMaximumOpacity',
213213
'opacityMode',
214+
'forceNearestInterpolation',
214215
];
215216
sets.forEach((val) => {
216217
const cap = macro.capitalize(val);
@@ -232,6 +233,7 @@ function vtkVolumeProperty(publicAPI, model) {
232233
'gradientOpacityMaximumValue',
233234
'gradientOpacityMaximumOpacity',
234235
'opacityMode',
236+
'forceNearestInterpolation',
235237
];
236238
gets.forEach((val) => {
237239
const cap = macro.capitalize(val);
@@ -283,6 +285,7 @@ export function extend(publicAPI, model, initialValues = {}) {
283285
useGradientOpacity: false,
284286

285287
componentWeight: 1.0,
288+
forceNearestInterpolation: false,
286289
});
287290
}
288291
}

Sources/Rendering/OpenGL/VolumeMapper/index.js

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,11 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
190190
};
191191

192192
publicAPI.replaceShaderValues = (shaders, ren, actor) => {
193+
const actorProps = actor.getProperty();
193194
let FSSource = shaders.Fragment;
194195

195196
// define some values in the shader
196-
const iType = actor.getProperty().getInterpolationType();
197+
const iType = actorProps.getInterpolationType();
197198
if (iType === InterpolationType.LINEAR) {
198199
FSSource = vtkShaderProgram.substitute(
199200
FSSource,
@@ -202,7 +203,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
202203
).result;
203204
}
204205

205-
const vtkImageLabelOutline = actor.getProperty().getUseLabelOutline();
206+
const vtkImageLabelOutline = actorProps.getUseLabelOutline();
206207
if (vtkImageLabelOutline === true) {
207208
FSSource = vtkShaderProgram.substitute(
208209
FSSource,
@@ -225,27 +226,33 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
225226
'//VTK::IndependentComponentsOn',
226227
'#define UseIndependentComponents'
227228
).result;
229+
}
228230

229-
// Define any proportional components
230-
const proportionalComponents = [];
231-
for (let nc = 0; nc < numComp; nc++) {
232-
if (
233-
actor.getProperty().getOpacityMode(nc) === OpacityMode.PROPORTIONAL
234-
) {
235-
proportionalComponents.push(`#define vtkComponent${nc}Proportional`);
236-
}
231+
// Define any proportional components
232+
const proportionalComponents = [];
233+
const forceNearestComponents = [];
234+
for (let nc = 0; nc < numComp; nc++) {
235+
if (actorProps.getOpacityMode(nc) === OpacityMode.PROPORTIONAL) {
236+
proportionalComponents.push(`#define vtkComponent${nc}Proportional`);
237237
}
238-
239-
if (proportionalComponents.length > 0) {
240-
FSSource = vtkShaderProgram.substitute(
241-
FSSource,
242-
'//VTK::vtkProportionalComponents',
243-
proportionalComponents.join('\n')
244-
).result;
238+
if (actorProps.getForceNearestInterpolation(nc)) {
239+
forceNearestComponents.push(`#define vtkComponent${nc}ForceNearest`);
245240
}
246241
}
247242

248-
const colorMixPreset = actor.getProperty().getColorMixPreset();
243+
FSSource = vtkShaderProgram.substitute(
244+
FSSource,
245+
'//VTK::vtkProportionalComponents',
246+
proportionalComponents.join('\n')
247+
).result;
248+
249+
FSSource = vtkShaderProgram.substitute(
250+
FSSource,
251+
'//VTK::vtkForceNearestComponents',
252+
forceNearestComponents.join('\n')
253+
).result;
254+
255+
const colorMixPreset = actorProps.getColorMixPreset();
249256
const colorMixCode = getColorCodeFromPreset(colorMixPreset);
250257
if (colorMixCode) {
251258
FSSource = vtkShaderProgram.substitute(
@@ -310,7 +317,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
310317
}
311318
if (
312319
model.renderable.getLocalAmbientOcclusion() &&
313-
actor.getProperty().getAmbient() > 0.0
320+
actorProps.getAmbient() > 0.0
314321
) {
315322
FSSource = vtkShaderProgram.substitute(
316323
FSSource,
@@ -324,7 +331,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
324331
const numIComps = useIndependentComps ? numComp : 1;
325332
model.gopacity = false;
326333
for (let nc = 0; !model.gopacity && nc < numIComps; ++nc) {
327-
model.gopacity ||= actor.getProperty().getUseGradientOpacity(nc);
334+
model.gopacity ||= actorProps.getUseGradientOpacity(nc);
328335
}
329336
if (model.gopacity) {
330337
FSSource = vtkShaderProgram.substitute(
@@ -544,10 +551,11 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
544551
recomputeLightComplexity(actor, ren.getLights());
545552

546553
const numComp = model.scalarTexture.getComponents();
547-
const iComps = actorProps.getIndependentComponents();
548554
const opacityModes = [];
555+
const forceNearestInterps = [];
549556
for (let nc = 0; nc < numComp; nc++) {
550557
opacityModes.push(actorProps.getOpacityMode(nc));
558+
forceNearestInterps.push(actorProps.getForceNearestInterpolation(nc));
551559
}
552560

553561
const ext = model.currentInput.getSpatialExtent();
@@ -566,16 +574,17 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
566574
const hasZBufferTexture = !!model.zBufferTexture;
567575

568576
const state = {
577+
iComps: actorProps.getIndependentComponents(),
569578
colorMixPreset: actorProps.getColorMixPreset(),
570579
interpolationType: actorProps.getInterpolationType(),
571580
useLabelOutline: actorProps.getUseLabelOutline(),
572581
numComp,
573-
iComps,
574582
maxSamples,
575583
useGradientOpacity: actorProps.getUseGradientOpacity(0),
576584
blendMode: model.renderable.getBlendMode(),
577585
hasZBufferTexture,
578586
opacityModes,
587+
forceNearestInterps,
579588
};
580589

581590
// We need to rebuild the shader if one of these variables has changed,

Sources/Rendering/OpenGL/glsl/vtkVolumeFS.glsl

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ varying vec3 vertexVCVSOutput;
3939
// possibly define any "proportional" components
4040
//VTK::vtkProportionalComponents
4141

42+
// possibly define any components that are forced to nearest interpolation
43+
//VTK::vtkForceNearestComponents
44+
4245
// Define the blend mode to use
4346
#define vtkBlendMode //VTK::BlendMode
4447

@@ -232,17 +235,39 @@ uniform highp sampler3D texture1;
232235
vec4 getTextureValue(vec3 pos)
233236
{
234237
vec4 tmp = texture(texture1, pos);
235-
#ifndef UseIndependentComponents
236-
#if vtkNumComponents == 1
237-
tmp.a = tmp.r;
238-
#endif
239-
#if vtkNumComponents == 2
240-
tmp.a = tmp.g;
238+
239+
#if defined(vtkComponent0ForceNearest) || \
240+
defined(vtkComponent1ForceNearest) || \
241+
defined(vtkComponent2ForceNearest) || \
242+
defined(vtkComponent3ForceNearest)
243+
vec3 nearestPos = (floor(pos * vec3(volumeDimensions)) + 0.5) / vec3(volumeDimensions);
244+
vec4 nearestValue = texture(texture1, nearestPos);
245+
#ifdef vtkComponent0ForceNearest
246+
tmp[0] = nearestValue[0];
247+
#endif
248+
#ifdef vtkComponent1ForceNearest
249+
tmp[1] = nearestValue[1];
250+
#endif
251+
#ifdef vtkComponent2ForceNearest
252+
tmp[2] = nearestValue[2];
253+
#endif
254+
#ifdef vtkComponent3ForceNearest
255+
tmp[3] = nearestValue[3];
256+
#endif
241257
#endif
242-
#if vtkNumComponents == 3
243-
tmp.a = length(tmp.rgb);
258+
259+
#ifndef UseIndependentComponents
260+
#if vtkNumComponents == 1
261+
tmp.a = tmp.r;
262+
#endif
263+
#if vtkNumComponents == 2
264+
tmp.a = tmp.g;
265+
#endif
266+
#if vtkNumComponents == 3
267+
tmp.a = length(tmp.rgb);
268+
#endif
244269
#endif
245-
#endif
270+
246271
return tmp;
247272
}
248273

0 commit comments

Comments
 (0)