Published
Edited
Feb 17, 2021
2 forks
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
volume
Insert cell
Insert cell
Insert cell
Insert cell
pixelColorCode = [
`
struct Ray
{
vec3 origin;
vec3 direction;
float tMin, tMax;
};

vec3 pointOnRay(in Ray ray,float t){
return (ray.origin+t*ray.direction);
}

struct AABB{
vec3 minP;
vec3 maxP;
};
const float EPSILON = 1e-5;
const float INFINITY = 1e5;

//uniform vec3 isoColor;
uniform vec2 resolution;
uniform vec3 cameraU;
uniform vec3 cameraV;
uniform vec3 cameraW;
uniform vec3 cameraEye;
uniform float fov;

precision mediump sampler3D;
uniform sampler3D vTex;
uniform float isoValue;
uniform int maxSteps;
uniform vec3 volDim;

Ray getRay(vec2 pixel){
Ray ray;
ray.origin = cameraEye;
float height = 2.*tan(fov/2.);
float aspect = resolution.x/resolution.y;
float width = height*aspect;
vec2 windowDim = vec2(width,height);
vec2 pixelSize = windowDim/resolution;
vec2 delta = -0.5*windowDim + pixel*pixelSize;
ray.direction = normalize(-cameraW + cameraV*delta.y + cameraU*delta.x);
ray.tMin = 0.;
ray.tMax = INFINITY;
return ray;
}

float volIntensity(vec3 position)
{
return texture(vTex,position+0.5).r;
}
bool insersectsRayAABB(inout Ray ray, AABB aabb) {
vec3 tMin = (aabb.minP.xyz - ray.origin) / ray.direction;
vec3 tMax = (aabb.maxP.xyz - ray.origin) / ray.direction;
vec3 t1 = min(tMin, tMax);
vec3 t2 = max(tMin, tMax);
float tNear = max(max(max(t1.x, t1.y), t1.z),ray.tMin);
float tFar = min(min(min(t2.x, t2.y), t2.z),ray.tMax);
if (tNear < tFar){
ray.tMin = tNear;
ray.tMax = tFar;
return true;
}
return false;
}
`,
`
float traceRayThroughVolume(Ray ray){
vec3 entryPoint = pointOnRay(ray,ray.tMin);
vec3 exitPoint = pointOnRay(ray,ray.tMax);
vec3 midPoint = 0.5*(entryPoint+exitPoint);
return volIntensity(midPoint);
}
`,
`
vec3 pixelColor(vec2 pixel){
vec3 color = vec3 (0);
AABB aabb;
aabb.minP = vec3(-0.5);
aabb.maxP = vec3(0.5);
/*
Add your code here.
Step I: get ray
Step II: Intersect AABB
Step III: color = traceRayThroughVolume(ray)
*/
Ray ray = getRay(pixel);
if (insersectsRayAABB(ray, aabb)){
float intensity = traceRayThroughVolume(ray);
color = vec3(intensity);
}
return color;
}
`
]
Insert cell
volRenderer = {
let cameraUp = [0, 1, 0];

let cameraAt = [0, 0, 0];

let boxDim = [1, 1, 1];
let boxDiag = twgl.v3.length(boxDim);

let M = twgl.m4.axisRotation(cameraUp, deg2rad(1));
let cameraU, cameraV, cameraW, cameraEye;

let eyeVector = [0, 0, 1];

const computeEyePosition = () => {
return [
// scale and Add to box center
2 * boxDiag * eyeVector[0] + cameraAt[0],
2 * boxDiag * eyeVector[1] + cameraAt[1],
2 * boxDiag * eyeVector[2] + cameraAt[2]
];
};

const computeUVW = () => {
cameraEye = computeEyePosition();
cameraW = eyeVector.slice();
cameraU = twgl.v3.normalize(twgl.v3.cross(cameraUp, cameraW));
cameraV = twgl.v3.normalize(twgl.v3.cross(cameraW, cameraU));
};

const rotateCamera = () => {
eyeVector = twgl.m4.transformDirection(M, eyeVector);
computeUVW();
return cameraEye;
};

//return [boxDim, boxDiag, computeEyePosition()];
computeUVW();
function render(isoValue, isoSurfaceColor) {
gl.clear(gl.COLOR_BUFFER_BIT);
const uniforms = {
resolution: [gl.canvas.width, gl.canvas.height],
cameraU: cameraU,
cameraV: cameraV,
cameraW: cameraW,
cameraEye: cameraEye,
fov: deg2rad(20),
vTex: volume.texture,
isoColor: hex2rgb(isoSurfaceColor),
volDim: [volume.width, volume.height, volume.slices],
isoValue: isoValue
};
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);
}

return {
computeUVW: computeUVW,
render: render,
rotate: rotateCamera
};
//return md`### WebGL Render code`;
}
Insert cell
deg2rad = deg => (deg * Math.PI) / 180
Insert cell
isoValue, isoSurfaceColor
Insert cell
{
volRenderer.render(isoValue, isoSurfaceColor);
while (rotate) {
volRenderer.render(isoValue, isoSurfaceColor);
yield Promises.delay(1000 / 30, volRenderer.rotate());
}
}
Insert cell
md`### Leave the following code unchanged.`
Insert cell
bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0]
})
Insert cell
Insert cell
Insert cell
gl = {
const gl = canvas.getContext('webgl2', { antialias: true });
gl.clearColor(0, 0, 0, 1); // Choose a clear color
gl.clear(gl.COLOR_BUFFER_BIT);
const alignment = 1;
gl.pixelStorei(gl.UNPACK_ALIGNMENT, alignment);
return gl;
}
Insert cell
height = 512
Insert cell
width = 512
Insert cell
md`### Volume data and related functions`
Insert cell
volume = volumes[volChoice]
Insert cell
Insert cell
Insert cell
Insert cell
footVolume = getVolumeFromRawData(
await FileAttachment("Foot.vol").arrayBuffer(),
125,
255,
183
)
Insert cell
getVolumeFromRawData = (data, width, height, slices) => {
const vol = {
width: width,
height: height,
slices: slices,
data: new Uint8Array(data.slice(28))
};
vol.dim = [vol.width, vol.height, vol.slices];
vol.texture = getVolTexture(vol);
return vol;
}
Insert cell
getVolTexture = vol =>
twgl.createTexture(gl, {
target: gl.TEXTURE_3D,
width: vol.width,
height: vol.height,
depth: vol.nSlices,
wrap: gl.CLAMP_TO_EDGE,
minMag: gl.NEAREST,
src: vol.data,
format: gl.LUMINANCE
})
Insert cell
md`### Local Helper Functions`
Insert cell
hex2rgb = hex =>
(hex = hex.replace('#', ''))
.match(new RegExp('(.{' + hex.length / 3 + '})', 'g'))
.map(l => parseInt(hex.length % 2 ? l + l : l, 16) / 255)
Insert cell
md`### External Libraries and Functions (imports)`
Insert cell
import { slider, color, checkbox, select } from "@jashkenas/inputs"
Insert cell
twgl = require("twgl.js")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more