In the previous guide, we learned how to use the
<set-view-volumetric> component to render a CT (computed tomography) in 3D directly in the browser.
This component comes with 4 rendering modes (or shaders):
Even though this 4 shaders should cover most of the use cases, it's possible to go low level and write a custom one for special use cases or just experimenting new ways of rendering your data.
Sethealth API makes writing this volumetric shaders extremelly easy, by abstracting away all the setup required to get a production-ready ray caster.
A computed tomography can be seen as a 3D image, a volume or a tensor in ℝ3. The CT is converted and loaded by Sethealth into the GPU, then a volume ray casting algorithm renders a 2D projection into the screen.
Notice that this algoritm needs to "simulate" a light ray going from the camera all the way to the end of the volume, and that needs to be computed for every pixel of the screen.
A fragment shader is a small program that runs for each pixel, taking variables (uniforms), textures and the screen position as input and returning a color (RGBA) as output.
Alright! let's look at the max-intensity shader that comes with Sethealth:
Sethealth provides built-in functions to easily create your own volumetric ray caster on top of the medical data. You can focus in writing the logic of your ray caster instead of getting lost in the details.
Return a Ray struct, containing all the ray properties required to perform a ray casting.
Returns the normalized pixel value (density) of our volume at the specified 3D point (cursor).
Returns the normalized normal vector at the surface of the specified point (cursor). Since it's a volumetric render, there is not a surface strictly speaking, so the normal is the gradient of the volume.
It uses the provided "colormap", resolving a volume value to a vector color (RGBA).
This function uses
readColormap() under the hood to return the resolve the color of
a specific 3D point (cursor) using the provided colormap.
Returns the non-linear depth at the specified 3D point.
This value can be passed directly to
gl_FragDepth. This is used to give our shader depth so it can be integrated with
normal rasterized geometry.