Skip to content

Commit

Permalink
Merge pull request #18 from NLeSC/substepping
Browse files Browse the repository at this point in the history
Substepping
  • Loading branch information
goord authored Jun 12, 2024
2 parents afa8722 + ec29a67 commit 36c14ad
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 73 deletions.
3 changes: 2 additions & 1 deletion src/lib/shaders/volume.frag
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ uniform float gHG;
uniform float dataEpsilon;
uniform vec3 bottomColor;
uniform float bottomHeight;
uniform vec3 displacement;

// Optional parameters, for when a solid surface is being drawn along with
// the volume data.
Expand Down Expand Up @@ -163,7 +164,7 @@ void main(void){
vec3 dg=vec3(1)/vec3(volumeTexSize);
for(float t=tBox.x;t<tBox.y;t+=dt){

float v=texture(volumeTex,pSized).r;
float v=texture(volumeTex,pSized - displacement).r;
float ql=(v==0.0)?0.:(dataScale*pow(dataEpsilon/dataScale,1.0-v)-dataEpsilon);
if(ql==0.0)
{
Expand Down
18 changes: 8 additions & 10 deletions src/lib/shaders/volume_transfer.frag
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
// This code is based upon Janelia's web-vol-viewer
// https://github.com/JaneliaSciComp/web-vol-viewer

precision highp float;
precision mediump float;
in vec3 rayDirUnnorm;
in vec3 lightDir;

uniform sampler2D transferTex;
// uniform lowp sampler3D volumeTex;
uniform lowp sampler3D volumeTex;
uniform lowp sampler3D coarseVolumeTex;
uniform float dtScale;
uniform float finalGamma;
uniform vec3 sunLightColor;
uniform highp vec3 boxSize;
uniform float dataScale;
uniform float alphaNorm;
uniform bool useLighting;
uniform vec3 displacement;

// Optional parameters, for when a solid surface is being drawn along with
// the volume data.
Expand Down Expand Up @@ -66,10 +67,9 @@ void main(void){

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

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

// Prevents a lost WebGL context.
if(dt<.00001){
Expand Down Expand Up @@ -100,13 +100,13 @@ void main(void){
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;
// look 8 steps ahead
float value=texture(coarseVolumeTex, pSized - displacement).r;
if(value != 0.0){
#pragma unroll_loop_start
for(int i = 0; i < 8; ++i)
{
float fineValue = texture(volumeTex, pSized + random).r;
float fineValue = texture(volumeTex, pSized - displacement + 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;
Expand All @@ -132,8 +132,6 @@ void main(void){
}
else
{
// float g=1./finalGamma;
// gl_FragColor=pow(vec4(illumination,1.0-transmittance),vec4(g,g,g,1));
float g=1./finalGamma;
vec4 finalColor = pow(vec4(illumination,1.0-transmittance),vec4(g,g,g,1));
// Apply uTransparency to the alpha component
Expand Down
9 changes: 6 additions & 3 deletions src/lib/utils/makeRainTransferTex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ export function makeRainTransferTex() {
const size = width * height;
const data = new Uint8Array(4 * size);

const rstart = 0;
const rstart = 2;
const rend = 60;
const astart = 1;
const bstart = 16;
const bend = 60;
const astart = 0;
const aend = 64;
const imid = 32;
const amid = 32;

for (let i = 0; i < width; i += 1) {
const r = rstart + (i * (rend - rstart)) / (width - 1);
const b = bstart + (i * (bend - bstart)) / (width - 1);
let alpha = 0;
if (i < imid) {
alpha = astart + (i * (amid - astart)) / (imid - 1);
Expand All @@ -24,7 +27,7 @@ export function makeRainTransferTex() {

data[4 * i] = r;
data[4 * i + 1] = 0;
data[4 * i + 2] = r;
data[4 * i + 2] = b;
data[4 * i + 3] = alpha;
}
// console.log(data);
Expand Down
4 changes: 3 additions & 1 deletion src/routes/viewer/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<script lang="ts">
import TimeLine from './components/TimeLine.svelte';
import { dataSlices, currentTimeIndex, totalSlices } from './stores/allSlices.store';
import { dataSlices, totalSlices } from './stores/allSlices.store';
import {
cloudLayerSettings,
rainLayerSettings,
slicesToRender,
temperatureLayerSettings
} from './stores/viewer.store';
import { currentTimeIndex } from './sceneSetup/updateMaterial';
import Stats from '$lib/components/Stats.svelte';
import Viewer from './components/Viewer.svelte';
// import Viewer from '$lib/components/viewerExample.svelte';
Expand Down
31 changes: 25 additions & 6 deletions src/routes/viewer/components/TimeLine.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<script lang="ts">
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import { get } from 'svelte/store';
import { dataSlices, currentTimeIndex, downloadedTime } from '../stores/allSlices.store';
import { dataSlices, coarseDataSlices, currentTimeIndex, downloadedTime } from '../stores/allSlices.store';
import { data_layers } from '../sceneSetup/boxSetup';
import { updateMaterial } from '../sceneSetup/updateMaterial';
import { updateMaterial,
refreshMaterial,
currentStepIndex } from '../sceneSetup/updateMaterial';
export let playAnimation = false;
export let length = 10;
export let positionIndex = 0;
export let playSpeedInMiliseconds = 300;
export let subStepsPerFrame = 10;
let interval;
const dispatch = createEventDispatcher();
Expand All @@ -18,10 +21,14 @@
if (playAnimation) {
interval = setInterval(() => {
if (get(dataSlices)[get(currentTimeIndex)]) {
const next = (get(currentTimeIndex) + 1) % get(dataSlices).length;
currentTimeIndex.set(next);
const nextStep = (get(currentStepIndex) + 1) % subStepsPerFrame;
currentStepIndex.set(nextStep);
if(nextStep == 0) {
const nextTimeIndex = (get(currentTimeIndex) + 1) % get(dataSlices).length;
currentTimeIndex.set(nextTimeIndex);
}
}
}, playSpeedInMiliseconds);
}, playSpeedInMiliseconds/subStepsPerFrame);
} else {
clearInterval(interval);
}
Expand All @@ -31,12 +38,24 @@
// Update the material when the currentTimeIndex changes
currentTimeIndex.subscribe((index: number) => {
const data = get(dataSlices)[index];
const coarseData = get(coarseDataSlices)[index];
if (data) {
for (const variable of data_layers) {
updateMaterial({ variable, dataUint8: data[variable] });
let variableCoarseData = null;
if (coarseData && variable in coarseData){
variableCoarseData = coarseData[variable];
}
updateMaterial({ variable, dataUint8: data[variable], coarseData: variableCoarseData });
}
}
});
currentStepIndex.subscribe((index: number) => {
if(index != 0){
for (const variable of data_layers) {
refreshMaterial({variable, index, maxIndex: subStepsPerFrame});
}
}
})
});
onDestroy(() => {
Expand Down
4 changes: 3 additions & 1 deletion src/routes/viewer/components/Viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import * as THREE from 'three';
import DebugButtons from './DebugButtons.svelte';
import { dataSlices, currentTimeIndex, downloadedTime } from '../stores/allSlices.store';
import { dataSlices, downloadedTime } from '../stores/allSlices.store';
import { cloudLayerSettings, rainLayerSettings, temperatureLayerSettings, showGrid } from '../stores/viewer.store';
import { create3DScene, scene } from '../sceneSetup/create3DScene';
Expand All @@ -12,6 +12,8 @@
import { boxes, data_layers } from '../sceneSetup/boxSetup';
// import examplePoints from '../fetchAndPrepareData/examplePoints';
import { currentTimeIndex } from '../sceneSetup/updateMaterial';
let canvas: HTMLElement;
let gridHelper: THREE.GridHelper;
Expand Down
38 changes: 23 additions & 15 deletions src/routes/viewer/fetchAndPrepareData/coarseData.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
//
// Coarse data to compress the ammount of data for the qr dataset
//
export function coarseData(shape, data) {
export function coarseData(data, shape) {
// Coarse data to compress the ammount of data
let dataCoarse = null;
// console.log("Coarse graining...");
dataCoarse = 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 blockSize = 8;
const s0 = Math.ceil(shape[0] / blockSize);
const s1 = Math.ceil(shape[1] / blockSize);
const s2 = Math.ceil(shape[2] / blockSize);
console.log("Coarse graining array of shape ",shape," to ",[s0, s1, s2],"...");
dataCoarse = new Uint8ClampedArray(s0 * s1 * s2);
let index2 = 0;
let x = 0;
for (let i = 0; i < shape[2]; i += blockSize) {
for (let j = 0; j < shape[0]; j += blockSize) {
for (let k = 0; k < shape[1]; k += blockSize) {
x = 0;
for (let l = i; l < (i + blockSize) && l < shape[2]; l++) {
for (let m = j; m < (j + blockSize) && m < shape[0]; m++) {
for (let n = k; n < (k + blockSize) && n < shape[1]; n++) {
const value = data[n + m * shape[1] + l * shape[0] * shape[1]];
if (value > x){
x = value;
}
}
}
}
const index2 = k + j * shape[2] / 8 + i * (shape[2] / 8) * (shape[1] / 8);
dataCoarse[index2] = x
dataCoarse[index2] = x;
index2++;
}
}
}
// console.log("...Done");
console.log("...Done");

return dataCoarse;
}
33 changes: 28 additions & 5 deletions src/routes/viewer/fetchAndPrepareData/dataSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import {
import {
getVoxelAndVolumeSize,
getVoxelAndVolumeSize2D,
totalSlices
totalSlices,
coarseDataSlices
} from "../stores/allSlices.store";
import { fetchAllSlices } from "./fetchAllSlices";
import { fetchAllSlices, fetchAllSlicesAtOnce } from "./fetchAllSlices";
import { fetchSlice } from "./fetchSlice";
import { openArray, HTTPStore } from 'zarr';
import { get } from "svelte/store";
import { slicesToRender } from "../stores/viewer.store";
import { coarseData } from "./coarseData";


export const zarrdata = []
Expand All @@ -27,18 +31,37 @@ export async function dataSetup(visible_data, scene) {
zarrdata[variable] = await openArray({ store, path: variable, mode: 'r' });
const dimensions = variable === 'thetavmix' ? 3 : 4;
const { dataUint8, shape } = await fetchSlice({ currentTimeIndex: 0, path: variable, dimensions });
let coarseSlice = null;
if (variable == 'qr'){
coarseSlice = coarseData(dataUint8, shape);
coarseDataSlices.update((timeSlices) => {
if(timeSlices[0]){
timeSlices[0][variable] = coarseSlice;
}
else{
timeSlices[0] = {variable: coarseSlice};
}
return timeSlices
})
}

variable === 'thetavmix'
? await getVoxelAndVolumeSize2D(store, shape, variable)
: await getVoxelAndVolumeSize(store, shape, variable);

totalSlices.set(zarrdata[variable].length);
await createVolumetricRenderingBox({ scene, variable, dataUint8 });
await createVolumetricRenderingBox({ scene, variable, dataUint8, coarseData: coarseSlice });
}

// Fetch all slices after shwing the first one
//fetchAllData(visible_data, [1, get(slicesToRender)]);
fetchAllSlicesAtOnce(visible_data, [1, Math.floor(get(slicesToRender))]);
}

function fetchAllData(visible_data, timeRange) {
for (const variable of visible_data) {
const dimensions = variable === 'thetavmix' ? 3 : 4;
fetchAllSlices({ path: variable, dimensions }); // todo run this in parallel
fetchAllSlices({ path: variable, dimensions, timeRange });
}
}
}

27 changes: 23 additions & 4 deletions src/routes/viewer/fetchAndPrepareData/fetchAllSlices.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// import { Queue } from "async-await-queue";
import { get } from "svelte/store";
import { fetchSlice } from "./fetchSlice";
import { fetchSlice, fetchRange } from "./fetchSlice";
import { downloadedTime } from "../stores/allSlices.store";
import { slicesToRender } from "../stores/viewer.store";

/**
* Creates a new Queue instance with a concurrency of 1 and a timeout of 5000ms.
*/
export async function fetchAllSlices({ path = 'ql', dimensions = 4 }) {
export async function fetchAllSlices({ path = 'ql', dimensions = 4, timeRange = [1, 10] }) {

/**
* Creates a new Queue instance with a concurrency of 1 and a timeout of 5000ms.
Expand All @@ -18,7 +17,7 @@ export async function fetchAllSlices({ path = 'ql', dimensions = 4 }) {

console.log('📕 Downloading all slices');
// Start loop at 1 because the first slice was already fetched at mounted
for (let i = 1; i < get(slicesToRender); ++i) { // start with 1 because 0 was already fetched at mounted
for (let i = timeRange[0]; i < timeRange[1]; ++i) { // start with 1 because 0 was already fetched at mounted
// const me = Symbol();
// await q.wait(me, 10 - i);
try {
Expand All @@ -39,3 +38,23 @@ export async function fetchAllSlices({ path = 'ql', dimensions = 4 }) {

// return await q.flush();
}

export async function fetchAllSlicesAtOnce(visible_data, timeRange = [1, 10]) {
const timing = performance.now()
const promises = [];
for (const variable of visible_data) {
const dimensions = variable === 'thetavmix' ? 3 : 4;
try {
promises.push(fetchRange({ timeRange, path: variable, dimensions }));
} catch (e) {
console.error(e);
} finally {
// q.end(me);
}
}
await Promise.all(promises);
downloadedTime.set(get(downloadedTime) + Math.round(performance.now() - timing))
console.log('🎹 all data downladed in', get(downloadedTime), ' + ', Math.round(performance.now() - timing), 'ms');
}


Loading

0 comments on commit 36c14ad

Please sign in to comment.