Echo Volume Render depth coloring not working

My volume rendering of a 3D echo image does not show depth dependent coloring, like in this demo video: https://www.youtube.com/watch?v=hzxzUILNhnA&list=PLIFpHI5hhxkszTWJTaWDM-eGTR8zBubSA&index=14. I tried adjusting the various sliders, but they seems to have no effect, except for “Depth coloring” which just changes the render color uniformly.

3D Slicer 5.10.0, 2026 Macbook Pro, M5 Pro chip.

Pinging this in hopes of getting some advice!

Try narrowing the range on the depth coloring slider, it is currently stretching the depth colormap over 500 mm, and my guess is that the rendered echo region covers a small slice of that, so the color range you see is just a small slice of the colormap. Slide the whole range until the rendered region is a medium depth color, then bring in the edges until you see the range of the colormap that you would like (presumably most of it).

@mikebind Thanks for the advice. I tried increasing the “Depth range” to [-10000, 10000], and decreasing to [-10, 10]. For each, sliding the range as well as the min and max had no effect. I’ve attached screen recordings. Please let me know if I’m doing anything obviously wrong!

Hm, that does seem odd. I just confirmed that I can get sensible depth renderings using this module. I’m using Slicer 5.10.0, the same as you.

I have observed in the past that Echo Volume Render does not play well with any manual adjustment of settings in the Volume Rendering module. Any chance that you touched something there? If so, I would try just reopening Slicer and starting from scratch in the Echo Volume Render module, which will reset any confused settings. Beyond that, I would try a different echo volume to see if the issue seems specific to this dataset. Has this worked for you in the past?

Thanks Mike, I tried reopening Slicer and also using a different echo volume, but no dice. (This hasn’t worked for me in the past).

When you get a chance, could you try the following steps and let me know if it works.

  1. Open Slicer 5.12.3

  2. Install SlicerHeart extension

  3. Download the SlicerHeart “Mitral” dataset in the Sample Data module.

  1. Switch to the Echo Volume Render module and see if the volume render has depth color (it does not for me)

I got a chance to try this, and yes, I see some depth coloring before adjusting any settings (just loaded the “Mitral” dataset) and hit Apply after installing Slicer 5.12.3 and SlicerHeart. This is what I see (just rotated the 3D a bit to show the color variation a little better):

Thanks Mike, I also tried again just to be sure and I still don’t see any depth coloring. Any idea what might be happening here? I have a 2026 MacBook Pro with M5 Pro chip, if that’s relevant.

If anybody else comes across this issue, could you please download the sample “Mitral” dataset and report if you see depth coloring or not.

I’m on Windows 11. This could potentially be a Mac issue. Perhaps someone else can weigh in on whether they can reproduce this on a recent Mac.

Same here, no depth rendering on M3 Pro. I asked claude to diagnose and fix it. It seems mac specific. With the fix it renders correctly:

It should open a PR on the repo shortly.

Diagnosis: reading a VTK shader variable that no longer exists

I drove your running Slicer over the MCP server on localhost:2026, reproduced the thread’s exact steps (Mitral sample → Echo Volume Render), and instrumented the GLSL. It is not a Mac driver bug in the general sense — it’s a real bug in SlicerHeart that only manifests on your driver.

Environment: Slicer 5.12.3 (rev 34627), macOS 26.6.1, Apple M3 Pro, OpenGL 4.1 Metal, VTK 9.6.2.

Root cause

EchoVolumeRender.py:659 computes the camera position for the depth ramp from a shader global:

vec3 camInTexCoord = (ip_inverseTextureDataAdjusted * vec4(g_eyePosObj.xyz,1.0)).xyz;

g_eyePosObj was assigned by VTK’s ray-cast shader up to VTK 9.2:

g_eyePosObj = in_inverseVolumeMatrix[0] * vec4(in_cameraPos, 1.0);   // vtkVolumeShaderComposer.h, v9.2.6:435

VTK 9.3 deleted that assignment and replaced it with the uniform in_eyePosObjs[]. The bare declaration vec4 g_eyePosObj; still sits in raycasterfs.glsl:32, so the shader compiles cleanly — it just reads an uninitialized variable. I dumped the linked fragment shader from your GPU to confirm: g_eyePosObj appears exactly twice, the declaration and SlicerHeart’s read. It is never written.

Evidence from your GPU

Rendering dist (the near↔far interpolation ratio) directly as greyscale, over the volume region:

eye-position expression dist @00° (mean, std) dist@90@90°
g_eyePosObj (shipped) 0.0, 0.0 0.0, 0.0
vec3(0.0) (what a zero-init driver gives) 61.9, 102.6 47.6, 84.3
in_eyePosObjs[0] (correct) 49.8, 82.3 40.1, 71.7

dist collapses to exactly 0 everywhere, so mix(rgbNear, rgbFar, 0) returns rgbNear for every voxel. That is precisely the reported symptom: the Depth coloring slider still moves hueNear, so the whole volume shifts hue uniformly, and no other slider does anything visible.

Hue statistics confirm the shipped shader is completely camera-independent, while the fixed one tracks the camera:

camera azimuth shipped (hue mean, std) fixed
0.577, 0.059 −0.181, 0.193
45° 0.581, 0.056 −0.111, 0.209
90° 0.579, 0.060 −0.042, 0.260
135° 0.580, 0.057 −0.092, 0.245

Visual comparison written to /tmp/echo_depth_comparison.png — top row shipped (0°, 90°), bottom row fixed:

The fix produces exactly the tutorial appearance: near structures warm/tan, far structures blue, and the ramp rotates with the view.

Why Mike couldn’t reproduce it on Windows

Reading an uninitialized GLSL global is undefined behavior. NVIDIA/Windows drivers zero-initialize, so g_eyePosObj reads as (0,0,0,0) and camInTexCoord becomes a fixed point near the world origin — which still yields a plausible-looking gradient (row 2 of the table above), so it looks like it works. It’s actually anchored to the world origin rather than the camera, i.e. a latent bug there too. Apple’s compiler leaves the register with garbage far outside the volume, dist clamps to 0, and the effect vanishes entirely. Any Mac vs. Windows split here is a coin flip on driver behavior, not a designed difference.

Fix

Three lines in EchoVolumeRender.__init__, patch at /private/tmp/claude-1950572333/-Users-amaga/74cd5809-e665-448d-b0fd-43749d08a8fd/scratchpad/echovolumerender-depth-coloring.patch:

    if vtkVersion >= 903:
      self.computeColorReplacement = self.computeColorReplacement.replace(
        "g_eyePosObj.xyz", "in_eyePosObjs[0].xyz")

in_eyePosObjs[i] is the eye position in dataset space (vtkOpenGLGPUVolumeRayCastMapper.cxx:3519) — the same space the old g_eyePosObj held, so it’s a drop-in. VTK ≤ 9.2 keeps the old path. SlicerHeart master still has the bug at line 659, and I found no existing issue for it.

Two notes:

  • I left the fixed shader active in your running Slicer session only — the installed module file is untouched, so it reverts on restart.
  • Separately, your log shows vtkOpenGLVolumeOpacityTable: does not support the required texture size of 262144, falling back to 16384. That’s macOS’s max texture size and is unrelated to this — worth knowing but not the cause.