Skip to content

Commit

Permalink
feat(VolumeMapper): Add forceNearestInterpolation
Browse files Browse the repository at this point in the history
  • Loading branch information
bruyeret authored and finetjul committed Mar 20, 2024
1 parent c7ba534 commit 7317248
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
</fieldset>
<select id='volume' style="margin: 5px;"></select>
<select id='preset' style="margin: 5px; display: none;"></select>
<div id="forceNearest"></div>
</form>
38 changes: 30 additions & 8 deletions Sources/Rendering/Core/VolumeMapper/example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true });
const volumeOptions = {};
const volumeSelectElem = document.getElementById('volume');
const presetSelectElem = document.getElementById('preset');
const forceNearestElem = document.getElementById('forceNearest');

const actor = vtkVolume.newInstance();
const mapper = vtkVolumeMapper.newInstance();
Expand Down Expand Up @@ -144,6 +145,14 @@ if (light.getPositional()) {
renderer.addActor(lca);
}

{
const optionElem = document.createElement('option');
optionElem.label = 'Default';
optionElem.value = '';
presetSelectElem.appendChild(optionElem);
presetSelectElem.value = optionElem.value;
}

Object.keys(ColorMixPreset).forEach((key) => {
if (key === 'CUSTOM') {
// Don't enable custom mode
Expand All @@ -157,20 +166,32 @@ Object.keys(ColorMixPreset).forEach((key) => {
presetSelectElem.appendChild(optionElem);
});

{
const optionElem = document.createElement('option');
optionElem.label = 'Default';
optionElem.value = '';
presetSelectElem.appendChild(optionElem);
presetSelectElem.value = optionElem.value;
}

const setColorMixPreset = (presetKey) => {
const preset = presetKey ? ColorMixPreset[presetKey] : null;
actor.getProperty().setColorMixPreset(preset);
presetSelectElem.value = presetKey;
};

function updateForceNearestElem(comp) {
forceNearestElem.replaceChildren();
for (let c = 0; c < comp; ++c) {
const checkboxElem = document.createElement('input');
checkboxElem.type = 'checkbox';
checkboxElem.checked = actor.getProperty().getForceNearestInterpolation(c);
checkboxElem.addEventListener('change', () => {
actor.getProperty().setForceNearestInterpolation(c, checkboxElem.checked);
renderWindow.render();
});
forceNearestElem.appendChild(checkboxElem);
const labelElem = document.createElement('label');
labelElem.innerText = `Force linear interpolation for component ${c}`;
forceNearestElem.appendChild(labelElem);
forceNearestElem.appendChild(document.createElement('br'));
}
}

updateForceNearestElem(1);

volumeSelectElem.addEventListener('change', () => {
const { comp, data } = volumeOptions[volumeSelectElem.value];
if (comp === 1) {
Expand All @@ -179,6 +200,7 @@ volumeSelectElem.addEventListener('change', () => {
} else {
presetSelectElem.style.display = 'block';
}
updateForceNearestElem(comp);
const array = mapper.getInputData().getPointData().getArray(0);
array.setData(data);
array.setNumberOfComponents(comp);
Expand Down
15 changes: 15 additions & 0 deletions Sources/Rendering/Core/VolumeProperty/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ export interface vtkVolumeProperty extends vtkObject {
*/
getUseGradientOpacity(index: number): boolean;

/**
* @see setForceNearestInterpolation
* @param {Number} index
*/
getForceNearestInterpolation(index: number): boolean;

/**
*
*/
Expand Down Expand Up @@ -315,6 +321,15 @@ export interface vtkVolumeProperty extends vtkObject {
*/
setComponentWeight(index: number, value: number): boolean;

/**
* Force the nearest neighbor interpolation of one or more of the components
* The interpolation for the rest of the volume is set using `setInterpolationType`
* @see setInterpolationType
* @param {Number} index
* @param {Boolean} value
*/
setForceNearestInterpolation(index: number, value: boolean): boolean;

/**
* Get the scalar component weights.
* @param {Number} index
Expand Down
3 changes: 3 additions & 0 deletions Sources/Rendering/Core/VolumeProperty/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ function vtkVolumeProperty(publicAPI, model) {
'gradientOpacityMaximumValue',
'gradientOpacityMaximumOpacity',
'opacityMode',
'forceNearestInterpolation',
];
sets.forEach((val) => {
const cap = macro.capitalize(val);
Expand All @@ -232,6 +233,7 @@ function vtkVolumeProperty(publicAPI, model) {
'gradientOpacityMaximumValue',
'gradientOpacityMaximumOpacity',
'opacityMode',
'forceNearestInterpolation',
];
gets.forEach((val) => {
const cap = macro.capitalize(val);
Expand Down Expand Up @@ -283,6 +285,7 @@ export function extend(publicAPI, model, initialValues = {}) {
useGradientOpacity: false,

componentWeight: 1.0,
forceNearestInterpolation: false,
});
}
}
Expand Down
53 changes: 31 additions & 22 deletions Sources/Rendering/OpenGL/VolumeMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,11 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
};

publicAPI.replaceShaderValues = (shaders, ren, actor) => {
const actorProps = actor.getProperty();
let FSSource = shaders.Fragment;

// define some values in the shader
const iType = actor.getProperty().getInterpolationType();
const iType = actorProps.getInterpolationType();
if (iType === InterpolationType.LINEAR) {
FSSource = vtkShaderProgram.substitute(
FSSource,
Expand All @@ -202,7 +203,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
).result;
}

const vtkImageLabelOutline = actor.getProperty().getUseLabelOutline();
const vtkImageLabelOutline = actorProps.getUseLabelOutline();
if (vtkImageLabelOutline === true) {
FSSource = vtkShaderProgram.substitute(
FSSource,
Expand All @@ -225,27 +226,33 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
'//VTK::IndependentComponentsOn',
'#define UseIndependentComponents'
).result;
}

// Define any proportional components
const proportionalComponents = [];
for (let nc = 0; nc < numComp; nc++) {
if (
actor.getProperty().getOpacityMode(nc) === OpacityMode.PROPORTIONAL
) {
proportionalComponents.push(`#define vtkComponent${nc}Proportional`);
}
// Define any proportional components
const proportionalComponents = [];
const forceNearestComponents = [];
for (let nc = 0; nc < numComp; nc++) {
if (actorProps.getOpacityMode(nc) === OpacityMode.PROPORTIONAL) {
proportionalComponents.push(`#define vtkComponent${nc}Proportional`);
}

if (proportionalComponents.length > 0) {
FSSource = vtkShaderProgram.substitute(
FSSource,
'//VTK::vtkProportionalComponents',
proportionalComponents.join('\n')
).result;
if (actorProps.getForceNearestInterpolation(nc)) {
forceNearestComponents.push(`#define vtkComponent${nc}ForceNearest`);
}
}

const colorMixPreset = actor.getProperty().getColorMixPreset();
FSSource = vtkShaderProgram.substitute(
FSSource,
'//VTK::vtkProportionalComponents',
proportionalComponents.join('\n')
).result;

FSSource = vtkShaderProgram.substitute(
FSSource,
'//VTK::vtkForceNearestComponents',
forceNearestComponents.join('\n')
).result;

const colorMixPreset = actorProps.getColorMixPreset();
const colorMixCode = getColorCodeFromPreset(colorMixPreset);
if (colorMixCode) {
FSSource = vtkShaderProgram.substitute(
Expand Down Expand Up @@ -310,7 +317,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
}
if (
model.renderable.getLocalAmbientOcclusion() &&
actor.getProperty().getAmbient() > 0.0
actorProps.getAmbient() > 0.0
) {
FSSource = vtkShaderProgram.substitute(
FSSource,
Expand All @@ -324,7 +331,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
const numIComps = useIndependentComps ? numComp : 1;
model.gopacity = false;
for (let nc = 0; !model.gopacity && nc < numIComps; ++nc) {
model.gopacity ||= actor.getProperty().getUseGradientOpacity(nc);
model.gopacity ||= actorProps.getUseGradientOpacity(nc);
}
if (model.gopacity) {
FSSource = vtkShaderProgram.substitute(
Expand Down Expand Up @@ -544,10 +551,11 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
recomputeLightComplexity(actor, ren.getLights());

const numComp = model.scalarTexture.getComponents();
const iComps = actorProps.getIndependentComponents();
const opacityModes = [];
const forceNearestInterps = [];
for (let nc = 0; nc < numComp; nc++) {
opacityModes.push(actorProps.getOpacityMode(nc));
forceNearestInterps.push(actorProps.getForceNearestInterpolation(nc));
}

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

const state = {
iComps: actorProps.getIndependentComponents(),
colorMixPreset: actorProps.getColorMixPreset(),
interpolationType: actorProps.getInterpolationType(),
useLabelOutline: actorProps.getUseLabelOutline(),
numComp,
iComps,
maxSamples,
useGradientOpacity: actorProps.getUseGradientOpacity(0),
blendMode: model.renderable.getBlendMode(),
hasZBufferTexture,
opacityModes,
forceNearestInterps,
};

// We need to rebuild the shader if one of these variables has changed,
Expand Down
43 changes: 34 additions & 9 deletions Sources/Rendering/OpenGL/glsl/vtkVolumeFS.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ varying vec3 vertexVCVSOutput;
// possibly define any "proportional" components
//VTK::vtkProportionalComponents

// possibly define any components that are forced to nearest interpolation
//VTK::vtkForceNearestComponents

// Define the blend mode to use
#define vtkBlendMode //VTK::BlendMode

Expand Down Expand Up @@ -232,17 +235,39 @@ uniform highp sampler3D texture1;
vec4 getTextureValue(vec3 pos)
{
vec4 tmp = texture(texture1, pos);
#ifndef UseIndependentComponents
#if vtkNumComponents == 1
tmp.a = tmp.r;
#endif
#if vtkNumComponents == 2
tmp.a = tmp.g;

#if defined(vtkComponent0ForceNearest) || \
defined(vtkComponent1ForceNearest) || \
defined(vtkComponent2ForceNearest) || \
defined(vtkComponent3ForceNearest)
vec3 nearestPos = (floor(pos * vec3(volumeDimensions)) + 0.5) / vec3(volumeDimensions);
vec4 nearestValue = texture(texture1, nearestPos);
#ifdef vtkComponent0ForceNearest
tmp[0] = nearestValue[0];
#endif
#ifdef vtkComponent1ForceNearest
tmp[1] = nearestValue[1];
#endif
#ifdef vtkComponent2ForceNearest
tmp[2] = nearestValue[2];
#endif
#ifdef vtkComponent3ForceNearest
tmp[3] = nearestValue[3];
#endif
#endif
#if vtkNumComponents == 3
tmp.a = length(tmp.rgb);

#ifndef UseIndependentComponents
#if vtkNumComponents == 1
tmp.a = tmp.r;
#endif
#if vtkNumComponents == 2
tmp.a = tmp.g;
#endif
#if vtkNumComponents == 3
tmp.a = length(tmp.rgb);
#endif
#endif
#endif

return tmp;
}

Expand Down

0 comments on commit 7317248

Please sign in to comment.