Public
Edited
Apr 10, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
grayScale;
topLimit;
while (true) {
if (linkView) {
cube.material.uniforms.cameraPosition.value.copy(camera.position);
cube2.material.uniforms.cameraPosition.value.copy(camera.position);
}

animationProps.rotations.forEach((e) => {
cube.rotation[e] += animationProps.speed;
cube2.rotation[e] += animationProps.speed;
});

renderer.render(scene, camera);
yield null;
}
}
Insert cell
Insert cell
Insert cell
vertexShader = glsl(`

in vec3 position;

uniform mat4 modelMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 cameraPosition;

out vec3 vOrigin;
out vec3 vDirection;

mat4 affine;

void main() {
affine[0] = vec4(-1, 0, 0, 0);
affine[1] = vec4(0, 1, 0, 0);
affine[2] = vec4(0, 0, -1, 0);
affine[3] = vec4(0, 0, 0, 1);

vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;
vDirection = position - vOrigin;

gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}`)
Insert cell
Insert cell
fragmentShader = glsl(`precision highp float;
precision highp sampler3D;

uniform sampler3D dataTexture;

in vec3 vOrigin;
in vec3 vDirection;

out vec4 frag_color;

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);
}

float sampleData(vec3 coord) {
coord.y = 1.0 - coord.y;
return textureLod(dataTexture, coord, 0.0).x;
}

vec4 maximumIntensity(vec4 color, vec3 entryPoint, vec3 rayDir, float samples, float tStart, float tEnd, float tIncr) {
float density = 0.0;
vec3 maxP = vec3(0.0);
for (float i = 0.0; i < samples; i += 1.0) {
float t = tStart + tIncr * i;
vec3 p = entryPoint + rayDir * t;

float value = sampleData(p);
if (value > density) {
density = value;
maxP = p;
}

if (density >= 1.0 || t > tEnd) {
break;
}
}

density *= 2.0;

if (density < ${topLimit}) density = 0.0;

color.rgb = vec3(1.0 - pow(density, ${grayScale.toFixed(2)}));
color.a = pow(density, ${grayScale.toFixed(2)});

return color;
}

void main() {
vec4 color = vec4(0.0);

vec3 rayDir = normalize(vDirection);
vec3 aabbmin = vec3(-0.5);
vec3 aabbmax = vec3(0.5);
vec2 intersection = intersectAABB(vOrigin, rayDir, aabbmin, aabbmax);
float samplingRate = 1.0;

if (intersection.x <= intersection.y) {
intersection.x = max(intersection.x, 0.0); // Clamp if camera is inside box.
vec3 entryPoint = vOrigin + rayDir * intersection.x;
vec3 exitPoint = vOrigin + rayDir * intersection.y;

// Entry Exit Align Corner sampling as described in
// Volume Raycasting Sampling Revisited by Steneteg et al. 2019
vec3 dimensions = vec3(textureSize(dataTexture, 0));
vec3 ray = exitPoint - entryPoint;
float samples = ceil(samplingRate * length(ray * (dimensions - vec3(1.0))));
float tEnd = length(ray);
float tIncr = tEnd / samples;
float tStart = 0.5 * tIncr;

vec3 texEntry = (entryPoint - aabbmin) / (aabbmax - aabbmin);
color = maximumIntensity(color, texEntry, rayDir, samples, tStart, tEnd, tIncr);
}

frag_color = color;
}`)
Insert cell
Insert cell
Insert cell
Insert cell
width = 800
Insert cell
height = 600
Insert cell
cube = {
// Cube.
const geometry = new THREE.BoxGeometry(1, 1, 1);

// Brain.
const material = new THREE.RawShaderMaterial({
glslVersion: THREE.GLSL3,
uniforms: {
dataTexture: { value: volumeTexture },
cameraPosition: { value: new THREE.Vector3() }
},
vertexShader: vertexShader.innerText,
fragmentShader: fragmentShader.innerText,
side: THREE.BackSide,
transparent: true
});

const cube = new THREE.Mesh(geometry, material);
cube.scale.set(data.aspect[0], data.aspect[1], data.aspect[2]);

// Cube outline.
const line = new THREE.LineSegments(
new THREE.EdgesGeometry(geometry),
new THREE.LineBasicMaterial({ color: 0xeeeeee })
);
cube.add(line);

return cube;
}
Insert cell
cube2 = {
// Cube.
const geometry = new THREE.BoxGeometry(1, 1, 1);

// Brain.
const material = new THREE.RawShaderMaterial({
glslVersion: THREE.GLSL3,
uniforms: {
dataTexture: { value: volumeTexture2 },
cameraPosition: { value: new THREE.Vector3() }
},
vertexShader: vertexShader.innerText,
fragmentShader: fragmentShader2.innerText,
side: THREE.BackSide,
transparent: true
});

const cube = new THREE.Mesh(geometry, material);
cube.scale.set(data.aspect[0], data.aspect[1], data.aspect[2]);

// Cube outline.
const line = new THREE.LineSegments(
new THREE.EdgesGeometry(geometry),
new THREE.LineBasicMaterial({ color: 0xeeeeee })
);
cube.add(line);

return cube;
}
Insert cell
scene = {
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(cube);
scene.add(cube2);
return scene;
}
Insert cell
camera = {
const fov = 45;
const aspect = width / height;
const near = 1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 0, -2)
camera.lookAt(new THREE.Vector3(0, 0, 0));
return camera;
}
Insert cell
renderer = {
const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(width, height);
renderer.setPixelRatio(devicePixelRatio);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener("change", () => renderer.render(scene, camera));
invalidation.then(() => (controls.dispose(), renderer.dispose()));
return renderer;
}
Insert cell
THREE = {
const THREE = window.THREE = await require("three@0.150.1");
await require("three@0.132.2/examples/js/controls/OrbitControls.js").catch(() => {});
return THREE;
}
Insert cell
Insert cell
volumeTexture2 = {
const { ravelFilled, xyz } = data,
h = 2;

const texture = new THREE.Data3DTexture(
ravelFilled.map((d) => (d > statZ * 10 ? d : 0)),
data.size[0],
data.size[1],
data.size[2]
);

texture.format = THREE.RedFormat;
texture.type = THREE.UnsignedByteType;
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
texture.wrapR = THREE.ClampToEdgeWrapping;
texture.unpackAlignment = 1;
texture.needsUpdate = true;

return texture;
}
Insert cell
volumeTexture = {
const { ravel, xyz } = data,
h = 2;

function insideSelectedSlices(e) {
var { x, y, z } = e,
b1 = true && x < sliderX + h && x > sliderX - h,
b2 = true && y < sliderY + h && y > sliderY - h,
b3 = true && z < sliderZ + h && z > sliderZ - h;

return b1 || b2 || b3;
}

const texture = new THREE.Data3DTexture(
ravel.map((d, i) => {
if (viewToggle || insideSelectedSlices(xyz[i])) return d;
return 0;
}),
data.size[0],
data.size[1],
data.size[2]
);

texture.format = THREE.RedFormat;
texture.type = THREE.UnsignedByteType;
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
texture.wrapR = THREE.ClampToEdgeWrapping;
texture.unpackAlignment = 1;
texture.needsUpdate = true;

return texture;
}
Insert cell
Insert cell
data = {
const zip = await FileAttachment("data-256x256x256@2.zip").zip(),
{ filenames } = zip;

var buffer, file;

file = zip.file("ravel");
buffer = await file.arrayBuffer();
const ravel = new Uint8Array(buffer);

file = zip.file("ravelFilled");
buffer = await file.arrayBuffer();
const ravelFilled = new Uint8Array(buffer);

file = zip.file("affine");
buffer = await file.arrayBuffer();
const affine = new Float64Array(buffer);
const aspect = [1, 1, 1];

file = zip.file("shape");
buffer = await file.arrayBuffer();
const shape = new Int32Array(buffer);
const size = [shape[0], shape[2], shape[4]];

let x = 0,
y = 0,
z = 0;
const xyz = [];
for (let i = 0; i < ravel.length; ++i) {
xyz.push(Object.assign({}, { x, y, z }));

z += 1;
if (z === size[0]) {
y += 1;
z = 0;
}
if (y === size[1]) {
x += 1;
y = 0;
}
}

return { filenames, ravel, ravelFilled, affine, size, xyz, aspect };
}
Insert cell
Insert cell
import {glsl} from "@lsei/advanced-syntax-highlighting"
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