Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3d rain fast #16

Open
wants to merge 2 commits into
base: 3d-rain
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
70 changes: 22 additions & 48 deletions src/lib/shaders/volume_transfer.frag
Expand Up @@ -7,6 +7,7 @@ in vec3 lightDir;

uniform sampler2D transferTex;
uniform lowp sampler3D volumeTex;
uniform lowp sampler3D coarseVolumeTex;
uniform float dtScale;
uniform float finalGamma;
uniform vec3 sunLightColor;
Expand Down Expand Up @@ -63,11 +64,10 @@ void main(void){

tBox.x=max(tBox.x,0.);

ivec3 volumeTexSize=textureSize(volumeTex,0);
ivec3 volumeTexSize=textureSize(coarseVolumeTex,0);
// vec3 dt0 = 1.0 / (vec3(volumeTexSize) * abs(rayDir));
vec3 dt0=1./(vec3(volumeTexSize)*abs(rayDir));
float dt1=min(dt0.x,min(dt0.y,dt0.z));
float dt=dtScale*dt1;
float dt=min(dt0.x,min(dt0.y,dt0.z)) * 0.5;

// Prevents a lost WebGL context.
if(dt<.00001){
Expand All @@ -80,73 +80,47 @@ void main(void){

// Dither to reduce banding (aliasing).
// https://www.marcusbannerman.co.uk/articles/VolumeRendering.html
float random=fract(sin(gl_FragCoord.x*12.9898+gl_FragCoord.y*78.233)*43758.5453);
random*=5.;
p+=random*dt*rayDir;

// Ray starting point, and change in ray point with each step, for the space where
// the box has been warped to a cube, for accessing the cubical data texture.
// The vec3(0.5) is necessary because rays are defined in the space where the box is
// centered at the origin, but texture look-ups have the origin at a box corner.
vec3 pSized=p/boxSize+vec3(.5);
vec3 dPSized=(rayDir*dt)/boxSize;
vec3 dPSmall = dPSized/8.0;

// Most browsers do not need this initialization, but add it to be safe.
gl_FragColor=vec4(0.);
//gl_FragColor.rgb = vec3(0.0, 0.0, 128.0);

vec3 illumination=vec3(0.,0.,0.);
float transmittance=1.;
float transmittance_threshold=0.01;
vec3 dg=vec3(1)/vec3(volumeTexSize);
float transmittance_threshold=0.05;
vec3 random=fract(sin(gl_FragCoord.x*12.9898+gl_FragCoord.y*78.233)*43758.5453)*dt*rayDir/8.0;
for(float t=tBox.x;t<tBox.y;t+=dt){

float value=texture(volumeTex,pSized).r;
vec4 vColor = value == 0.0 ? vec4(0.0) : texture(transferTex, vec2(value, 0.5));
vColor.a *= alphaNorm;
if (useLighting && vColor.a > 0.0)
{
// Gradient approximated by the central difference.
float dataDxA = texture(volumeTex, pSized + vec3(dg.x, 0.0, 0.0 )).r;
float dataDxB = texture(volumeTex, pSized - vec3(dg.x, 0.0, 0.0 )).r;
float dataDyA = texture(volumeTex, pSized + vec3(0.0, dg.y, 0.0 )).r;
float dataDyB = texture(volumeTex, pSized - vec3(0.0, dg.y, 0.0 )).r;
float dataDzA = texture(volumeTex, pSized + vec3(0.0, 0.0, dg.z)).r;
float dataDzB = texture(volumeTex, pSized - vec3(0.0, 0.0, dg.z)).r;
vec3 grad = vec3(dataDxA - dataDxB, dataDyA - dataDyB, dataDzA - dataDzB);

// When using the gradient as the surface normal for shading, we always want to
// act as if the surface is facing the camera. So flip the gradient if it points
// away from the camera (i.e., negate it if dot(grad, rayDir) > 0.0)
grad *= -sign(dot(grad, rayDir));

float gradLength = length(grad);
grad /= gradLength;
float gradStrength = (gradLength < 0.0001) ? 0.0 : 1.0;

vec3 lighting = min(max(dot(grad, lightDir), 0.0) * sunLightColor, vec3(1.0));
vColor.rgb *= lighting;
vColor *= gradStrength;

/*
// Uncomment to visualize the gradient for debugging.
gl_FragColor.rgb = (grad + vec3(1.0)) / 2.0;
gl_FragColor.a = 1.0;
gl_FragColor.a *= gradStrength;
return;
*/
float value=texture(coarseVolumeTex, pSized).r;
if(value != 0.0){
#pragma unroll_loop_start
for(int i = 0; i < 8; ++i)
{
float fineValue = texture(volumeTex, pSized + random).r;
vec4 vColor = fineValue == 0.0 ? vec4(0.0) : texture(transferTex, vec2(fineValue, 0.5));
vColor.a *= alphaNorm;
illumination.rgb += transmittance*clamp(vColor.a,0.0,1.0)*vColor.rgb;
transmittance *= ( 1.0 - clamp(vColor.a,0.0,1.0));
pSized += dPSmall;
}
#pragma unroll_loop_end
}
else{
pSized += dPSized;
}

illumination.rgb += transmittance*clamp(vColor.a,0.0,1.0)*vColor.rgb;
transmittance *= ( 1.0 - clamp(vColor.a,0.0,1.0));

// Break on opacity
if(transmittance<transmittance_threshold){
break;
}

// Move to the next point along the ray.
pSized+=dPSized;
}

// Surface
Expand Down
81 changes: 41 additions & 40 deletions src/routes/viewer/Viewer.svelte
Expand Up @@ -41,9 +41,9 @@
let renderer: THREE.WebGLRenderer;

// Be caruful with these valies, they can clip the date in the 3D scene
let cameraNear = 0.1;
let cameraFar = 10000.0;
let cameraFovDegrees = 45.0;
let cameraNear = 0.01;
let cameraFar = 1000.0;
let cameraFovDegrees = 5.0;
let dtScale: number = 0.8;
let ambientFactor: number = 0.0;
let solarFactor: number = 0.8;
Expand Down Expand Up @@ -79,7 +79,7 @@
);
const finalGamma = 6.0;
//const visible_data = ['ql', 'qr', 'thetavmix'];
const visible_data = ['thetavmix'];
const visible_data = ['qr'];

// 1 unit in the scene = 1000 meters (1 kilometer) in real life
// Meters of the bounding box of the data
Expand Down Expand Up @@ -113,7 +113,7 @@
// camera.position.z = 5; // Adjust as needed
// camera.position.set(0, -2, 1.7);
// x: 0, y: -0.935916216369971, z: 0.9359162163699711
camera.position.set(0, -0.9, 0.9); // Adjusted for scaled scene
camera.position.set(0, -10, 10); // Adjusted for scaled scene
camera.lookAt(new THREE.Vector3(0, 0, 0));
cameraControls = new CameraControls(camera, canvas);

Expand Down Expand Up @@ -191,26 +191,7 @@
console.log('🔥 rendered');
}

async function initMaterial({ variable, dataUint8 }): Promise<THREE.Material> {
let volumeTexture = null;
if (variable === 'thetavmix') {
volumeTexture = new THREE.DataTexture(dataUint8, get(volumeSizes)[variable][0], get(volumeSizes)[variable][1]);
} else {
volumeTexture = new THREE.Data3DTexture(
dataUint8,
get(volumeSizes)[variable][0],
get(volumeSizes)[variable][1],
get(volumeSizes)[variable][2]
);
}
volumeTexture.format = THREE.RedFormat;
volumeTexture.type = THREE.UnsignedByteType;
// Disabling mimpaps saves memory.
volumeTexture.generateMipmaps = false;
// Linear filtering disables LODs, which do not help with volume rendering.
volumeTexture.minFilter = THREE.LinearFilter;
volumeTexture.magFilter = THREE.LinearFilter;
volumeTexture.needsUpdate = true;
async function initMaterial({ variable }): Promise<THREE.Material> {
let boxMaterial = null;
switch (variable) {
case 'ql':
Expand All @@ -222,7 +203,7 @@
opacity: 1.0,
uniforms: {
boxSize: new THREE.Uniform(get(boxSizes)[variable]),
volumeTex: new THREE.Uniform(volumeTexture),
volumeTex: new THREE.Uniform(null),
voxelSize: new THREE.Uniform(get(voxelSizes)[variable]),
sunLightDir: new THREE.Uniform(sunLight.position),
sunLightColor: new THREE.Uniform(lightColorV),
Expand Down Expand Up @@ -252,7 +233,8 @@
opacity: 1.0,
uniforms: {
boxSize: new THREE.Uniform(get(boxSizes)[variable]),
volumeTex: new THREE.Uniform(volumeTexture),
volumeTex: new THREE.Uniform(null),
coarseVolumeTex: new THREE.Uniform(null),
sunLightDir: new THREE.Uniform(sunLight.position),
sunLightColor: new THREE.Uniform(lightColorV),
near: new THREE.Uniform(cameraNear),
Expand All @@ -278,7 +260,7 @@
opacity: 1.0,
clipping: true,
uniforms: {
volumeTex: new THREE.Uniform(volumeTexture),
volumeTex: new THREE.Uniform(null),
heightRatio: new THREE.Uniform(0),
heightBias: new THREE.Uniform(0),
//gradientTexture: {value: gradientMap}
Expand All @@ -292,14 +274,16 @@
return boxMaterial;
}

function updateMaterial({ variable, dataUint8 }) {
function updateMaterial({ variable, dataUint8, dataCoarse }) {
let localBox = boxes[variable];

if (!localBox) {
return;
}
// Dispose of the old texture to free up memory.
localBox.material.uniforms.volumeTex.value.dispose();
if(localBox.material.uniforms.volumeTex.value != null){
localBox.material.uniforms.volumeTex.value.dispose();
}

// Create a new 3D texture for the volume data.
let volumeTexture = null;
Expand All @@ -320,6 +304,25 @@
volumeTexture.magFilter = THREE.LinearFilter;
volumeTexture.needsUpdate = true;

if(localBox.material.uniforms.coarseVolumeTex.value != null){
localBox.material.uniforms.coarseVolumeTex.value.dispose();
}

let coarseVolumeTexture = null;
if(dataCoarse !== null){
coarseVolumeTexture = new THREE.Data3DTexture(
dataCoarse,
get(volumeSizes)[variable][0]/8,
get(volumeSizes)[variable][1]/8,
get(volumeSizes)[variable][2]/8);
coarseVolumeTexture.format = THREE.RedFormat;
coarseVolumeTexture.type = THREE.UnsignedByteType;
coarseVolumeTexture.generateMipmaps = false; // Saves memory.
coarseVolumeTexture.minFilter = THREE.NearestFilter; // Better for volume rendering.
coarseVolumeTexture.magFilter = THREE.NearestFilter;
coarseVolumeTexture.needsUpdate = true;
}

// Update material uniforms with new texture and parameters.
localBox.material.uniforms.volumeTex.value = volumeTexture;
switch (String(variable)) {
Expand All @@ -335,8 +338,9 @@
localBox.material.uniforms.finalGamma.value = finalGamma;
break;
case 'qr':
localBox.material.uniforms.coarseVolumeTex.value = coarseVolumeTexture;
localBox.material.uniforms.dataScale.value = qrScale;
localBox.material.uniforms.dtScale.value = dtScale * 4.0;
localBox.material.uniforms.dtScale.value = dtScale;
localBox.material.uniforms.alphaNorm.value = 2.0;
localBox.material.uniforms.finalGamma.value = finalGamma;
break;
Expand Down Expand Up @@ -406,7 +410,7 @@
* centered at the origin, with X in [-0.5, 0.5] so the width is 1, and
* Y (height) and Z (depth) scaled to match.
*/
async function addVolumetricRenderingContainer({ variable, dataUint8 }) {
async function addVolumetricRenderingContainer({ variable, dataUint8, dataCoarse }) {
//const boxGeometry = new THREE.BoxGeometry(get(volumeSize)[0], get(volumeSize)[1], get(volumeSize)[2]);
// const boxSizeInKm = 33.8; // 33.8 km
// const boxScale = boxSizeInKm; // / scaleFactor; // Convert to meters and then apply scale factor to scene units
Expand All @@ -418,11 +422,11 @@
box.position.z = 0.25 + 2000 / scaleFactor; // 570 meters above the map TODO: calculate this value from the data
box.renderOrder = 0;

box.material = await initMaterial({ variable, dataUint8 });
box.material = await initMaterial({ variable });
// const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// box.material = cubeMaterial;
boxes[variable] = box;
updateMaterial({ variable, dataUint8 });
updateMaterial({ variable, dataUint8, dataCoarse });
scene.add(box);
renderScene();
}
Expand Down Expand Up @@ -479,10 +483,11 @@
const {
dataUint8: vdata,
store: vstore,
shape: vshape
shape: vshape,
coarseData: vCoarseData
} = await fetchSlice({ currentTimeIndex: 0, path: variable });
await getVoxelAndVolumeSize(vstore, vshape, variable);
await addVolumetricRenderingContainer({ variable: variable, dataUint8: vdata });
await addVolumetricRenderingContainer({ variable: variable, dataUint8: vdata, dataCoarse: vCoarseData });
}
}
for (var variable of visible_data) {
Expand All @@ -499,10 +504,6 @@
for (var variable of visible_data) {
updateMaterial({ variable: variable, dataUint8: data[variable] });
}
/* updateMaterial({ variable: 'ql', dataUint8: data['ql'] });
updateMaterial({ variable: 'qr', dataUint8: data['qr'] });
updateMaterial({ variable: 'thetavmix', dataUint8: data['thetavmix'] });
*/
}
});

Expand Down
28 changes: 25 additions & 3 deletions src/routes/viewer/fetchSlice.ts
Expand Up @@ -3,7 +3,6 @@ import type { PersistenceMode } from 'zarr/types/types';

import { allTimeSlices } from "../../lib/components/allSlices.store";


// downloadZarrPoints
export async function fetchSlice({
currentTimeIndex = 0,
Expand All @@ -24,6 +23,30 @@ export async function fetchSlice({

const { data, strides, shape } = dims == 4 ? await zarrdata.getRaw([currentTimeIndex, null, null, null]) : await zarrdata.getRaw([currentTimeIndex, null, null]);

let coarseData = null;
if (path == 'qr'){
console.log("Coarse graining...");
coarseData = new Uint8Array(shape[0] * shape[1] * shape[2] / (8 * 8 * 8));
for (let i = 0; i < shape[0]/8; i++){
for (let j = 0; j < shape[1]/8; j++){
for (let k = 0; k < shape[2]/8; k++){
let x = 0;
for (let l = 0; l < 8; l++){
for (let m = 0; m < 8; m++){
for (let n = 0; n < 8; n++){
const index = (k * 8 + n) + (j * 8 + m) * shape[2] + (l * 8 + i) * shape[2] * shape[1];
x = Math.max(x, data[index])
}
}
}
const index2 = k + j * shape[2]/8 + i * (shape[2]/8) * (shape[1]/8);
coarseData[index2] = x
}
}
}
console.log("...Done");
}

// allSlices.set(data);
// Update the time slices store
allTimeSlices.update((timeSlices) => {
Expand All @@ -38,6 +61,5 @@ export async function fetchSlice({
});
console.log('🎹 downloaded ', currentTimeIndex);
// console.log('🎹 downloaded ', get(allTimeSlices)[currentTimeIndex]);
return { dataUint8: data, strides, shape, store };

return { dataUint8: data, shape, store, coarseData };
}