mirror of
https://github.com/TomHodson/tomhodson.github.com.git
synced 2025-06-26 10:01:18 +02:00
141 lines
5.1 KiB
JavaScript
141 lines
5.1 KiB
JavaScript
export const vertexShader = `
|
|
// Attributes.
|
|
in vec3 position;
|
|
|
|
// Uniforms.
|
|
uniform mat4 modelMatrix;
|
|
uniform mat4 modelViewMatrix;
|
|
uniform mat4 projectionMatrix;
|
|
uniform vec3 cameraPosition;
|
|
|
|
// Output.
|
|
out vec3 vOrigin; // Output ray origin.
|
|
out vec3 vDirection; // Output ray direction.
|
|
|
|
void main() {
|
|
// Compute the ray origin in model space.
|
|
vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;
|
|
// Compute ray direction in model space.
|
|
vDirection = position - vOrigin;
|
|
|
|
// Compute vertex position in clip space.
|
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
}
|
|
`;
|
|
|
|
export const fragmentShader = `
|
|
precision highp sampler3D; // Precision for 3D texture sampling.
|
|
precision highp float; // Precision for floating point numbers.
|
|
|
|
uniform sampler3D dataTexture; // Sampler for the volume data texture.
|
|
// uniform sampler2D colorTexture; // Sampler for the color palette texture.
|
|
uniform float samplingRate; // The sampling rate.
|
|
uniform float threshold; // Threshold to use for isosurface-style rendering.
|
|
uniform float alphaScale; // Scaling of the color alpha value.
|
|
uniform bool invertColor; // Option to invert the color palette.
|
|
|
|
in vec3 vOrigin; // The interpolated ray origin from the vertex shader.
|
|
in vec3 vDirection; // The interpolated ray direction from the vertex shader.
|
|
|
|
out vec4 frag_color; // Output fragment color.
|
|
|
|
// Sampling of the volume data texture.
|
|
float sampleData(vec3 coord) {
|
|
return texture(dataTexture, coord).x;
|
|
}
|
|
|
|
// Sampling of the color palette texture.
|
|
vec4 sampleColor(float value) {
|
|
// In case the color palette should be inverted, invert the texture coordinate to sample the color texture.
|
|
float x = invertColor ? value : 1.0 - value;
|
|
// return texture(colorTexture, vec2(x, 0.5));
|
|
return vec4(x, x, x, 1.0);
|
|
}
|
|
|
|
// Intersection of a ray and an axis-aligned bounding box.
|
|
// Returns the intersections as the minimum and maximum distance along the ray direction.
|
|
vec2 intersectAABB(vec3 rayOrigin, vec3 rayDir, vec3 boxMin, vec3 boxMax) {
|
|
vec3 tMin = (boxMin - rayOrigin) / rayDir;
|
|
vec3 tMax = (boxMax - rayOrigin) / rayDir;
|
|
vec3 t1 = min(tMin, tMax);
|
|
vec3 t2 = max(tMin, tMax);
|
|
float tNear = max(max(t1.x, t1.y), t1.z);
|
|
float tFar = min(min(t2.x, t2.y), t2.z);
|
|
|
|
return vec2(tNear, tFar);
|
|
}
|
|
|
|
// Volume sampling and composition.
|
|
// Note that the code is inserted based on the selected algorithm in the user interface.
|
|
vec4 compose(vec4 color, vec3 entryPoint, vec3 rayDir, float samples, float tStart, float tEnd, float tIncr) {
|
|
// Composition of samples using maximum intensity projection.
|
|
// Loop through all samples along the ray.
|
|
float density = 0.0;
|
|
for (float i = 0.0; i < samples; i += 1.0) {
|
|
// Determine the sampling position.
|
|
float t = tStart + tIncr * i; // Current distance along ray.
|
|
vec3 p = entryPoint + rayDir * t; // Current position.
|
|
|
|
// Sample the volume data at the current position.
|
|
float value = sampleData(p);
|
|
|
|
// Keep track of the maximum value.
|
|
if (value > density) {
|
|
// Store the value if it is greater than the previous values.
|
|
density = value;
|
|
}
|
|
|
|
// Early exit the loop when the maximum possible value is found or the exit point is reached.
|
|
if (density >= 1.0 || t > tEnd) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Convert the found value to a color by sampling the color palette texture.
|
|
color.rgb = sampleColor(density).rgb;
|
|
// Modify the alpha value of the color to make lower values more transparent.
|
|
color.a = alphaScale * (invertColor ? 1.0 - density : density);
|
|
|
|
// Return the color for the ray.
|
|
return color;
|
|
}
|
|
|
|
void main() {
|
|
// Determine the intersection of the ray and the box.
|
|
vec3 rayDir = normalize(vDirection);
|
|
vec3 aabbmin = vec3(-0.5);
|
|
vec3 aabbmax = vec3(0.5);
|
|
vec2 intersection = intersectAABB(vOrigin, rayDir, aabbmin, aabbmax);
|
|
|
|
// Initialize the fragment color.
|
|
vec4 color = vec4(0.0);
|
|
|
|
// Check if the intersection is valid, i.e., if the near distance is smaller than the far distance.
|
|
if (intersection.x <= intersection.y) {
|
|
// Clamp the near intersection distance when the camera is inside the box so we do not start sampling behind the camera.
|
|
intersection.x = max(intersection.x, 0.0);
|
|
// Compute the entry and exit points for the ray.
|
|
vec3 entryPoint = vOrigin + rayDir * intersection.x;
|
|
vec3 exitPoint = vOrigin + rayDir * intersection.y;
|
|
|
|
// Determine the sampling rate and step size.
|
|
// Entry Exit Align Corner sampling as described in
|
|
// Volume Raycasting Sampling Revisited by Steneteg et al. 2019
|
|
vec3 dimensions = vec3(textureSize(dataTexture, 0));
|
|
vec3 entryToExit = exitPoint - entryPoint;
|
|
float samples = ceil(samplingRate * length(entryToExit * (dimensions - vec3(1.0))));
|
|
float tEnd = length(entryToExit);
|
|
float tIncr = tEnd / samples;
|
|
float tStart = 0.5 * tIncr;
|
|
|
|
// Determine the entry point in texture space to simplify texture sampling.
|
|
vec3 texEntry = (entryPoint - aabbmin) / (aabbmax - aabbmin);
|
|
|
|
// Sample the volume along the ray and convert samples to color.
|
|
color = compose(color, texEntry, rayDir, samples, tStart, tEnd, tIncr);
|
|
}
|
|
|
|
// Return the fragment color.
|
|
frag_color = color;
|
|
}`;
|