Skip to content

Commit d6630d3

Browse files
authored
Merge pull request #52 from EarthyScience/Computation
HUGE Update - Added GPU Compute pipeline for single variable data
2 parents fdfba0c + d2d5794 commit d6630d3

File tree

14 files changed

+482
-6
lines changed

14 files changed

+482
-6
lines changed

src/components/CanvasGeometry.tsx

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,15 @@ import { useEffect, useState, useMemo } from 'react';
77
import { useControls } from 'leva'
88
import { PointCloud, UVCube, PlotArea, DataCube } from './PlotObjects';
99
import { GetColorMapTexture, ArrayToTexture, DefaultCube, colormaps } from './Textures';
10-
import { Metadata } from './UI';
10+
import { Metadata, AnalysisWindow } from './UI';
1111
import { plotContext, DimCoords } from './Contexts';
12+
import ComputeModule from '@/components/Computation/ComputeModule'
13+
14+
interface Array{
15+
data:number[],
16+
shape:number[],
17+
stride:number[]
18+
}
1219

1320
const storeURL = "https://s3.bgc-jena.mpg.de:9000/esdl-esdc-v3.0.2/esdc-16d-2.5deg-46x72x1440-3.0.2.zarr"
1421

@@ -53,6 +60,7 @@ export function CanvasGeometry() {
5360
const [timeSeries, setTimeSeries] = useState<number[]>([0]);
5461
const [showLoading, setShowLoading] = useState<boolean>(false);
5562
const [metadata,setMetadata] = useState<object[] | null>(null)
63+
const [dataArray, setDataArray] = useState<Array | null>(null)
5664

5765
//Timeseries Plotting Information
5866
const [dimArrays,setDimArrays] = useState([[0],[0],[0]])
@@ -62,6 +70,12 @@ export function CanvasGeometry() {
6270
const [plotDim,setPlotDim] = useState<number>(0)
6371
const ZarrDS = useMemo(()=>new ZarrDataset(storeURL),[])
6472

73+
//Analysis variables
74+
const [reduceAxis, setReduceAxis] = useState<number>(0);
75+
const [reduceOperation, setReduceOperation] = useState<string>("Mean")
76+
const [executeReduction,setExecuteReduction] = useState<boolean>(false)
77+
const [showAnalysis, setShowAnalysis] = useState<boolean>(false)
78+
6579
useEffect(()=>{
6680
setColormap(GetColorMapTexture(colormap,cmap,1,"#000000",0,flipCmap));
6781
},[cmap, colormap,flipCmap])
@@ -77,6 +91,7 @@ export function CanvasGeometry() {
7791
data: result.data,
7892
shape: result.shape
7993
})
94+
setDataArray(result)
8095
console.log(`logging the shape since we will want to use it in the future for 2D vs 3D actions ${shape}`)
8196
if (texture instanceof THREE.DataTexture || texture instanceof THREE.Data3DTexture) {
8297
setTexture(texture)
@@ -157,15 +172,28 @@ export function CanvasGeometry() {
157172
scaling:{...valueScales,colormap}
158173
}
159174

175+
const analysisSetters = {
176+
setAxis:setReduceAxis,
177+
setOperation:setReduceOperation,
178+
setExecute:setExecuteReduction
179+
}
180+
181+
const analysisVars = {
182+
axis:reduceAxis,
183+
operation:reduceOperation,
184+
execute:executeReduction
185+
}
160186
return (
161187
<>
188+
162189
<Loading showLoading={showLoading} />
163190
<div className='canvas'>
164191
<Canvas shadows camera={{ position: [-4.5, 3, 4.5], fov: 50 }}
165192
frameloop="demand"
166193
>
194+
167195
<Center top position={[-1, 0, 1]}/>
168-
196+
{dataArray && showAnalysis && <ComputeModule array={dataArray} cmap={colormap} shape={shape.toArray()} stateVars={analysisVars}/>}
169197
{/* Volume Plots */}
170198
{plotter == "volume" && <>
171199
<DataCube volTexture={texture} shape={shape} colormap={colormap}/>
@@ -180,14 +208,25 @@ export function CanvasGeometry() {
180208

181209
</Canvas>
182210
</div>
183-
211+
{showAnalysis && <AnalysisWindow setters={analysisSetters}/>}
184212
{metadata && <Metadata data={metadata} /> }
185213

186214
<plotContext.Provider value={plotObj} >
215+
187216
{variable !== "Default" && <PlotArea />}
188217
</plotContext.Provider>
189218

190219
{/* <Leva theme={lightTheme} /> */}
220+
<button
221+
style={{
222+
position:'fixed',
223+
left:'5%',
224+
top:'50%'
225+
}}
226+
onClick={()=>setShowAnalysis(x=>!x)}
227+
>
228+
{showAnalysis ? 'Hide' : 'Show' } Analysis Stuff
229+
</button>
191230
</>
192231
)
193232
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { useEffect, useState } from 'react'
2+
import { OneArrayCompute } from './ComputeShaders'
3+
import * as THREE from 'three'
4+
import fragmentShader from '@/components/Computation/shaders/frag.glsl'
5+
import vertexShader from '@/components/Computation/shaders/vert.glsl'
6+
7+
interface Array{
8+
data:number[],
9+
shape:number[],
10+
stride:number[]
11+
}
12+
interface StateVars{
13+
axis:number,
14+
operation:string,
15+
execute:boolean
16+
}
17+
18+
19+
const ComputeModule = ({array,cmap,shape,stateVars}:{array: Array,cmap:THREE.DataTexture,shape:number[],stateVars:StateVars}) => {
20+
const {axis,operation,execute} = stateVars;
21+
const [planeShape,setPlaneShape] = useState<number[]>(shape.filter((_val,idx)=> idx !== axis))
22+
23+
24+
const GPUCompute = new OneArrayCompute(array)
25+
const [texture,setTexture] = useState<THREE.Texture | undefined>(undefined)
26+
27+
const shaderMaterial = new THREE.ShaderMaterial({
28+
glslVersion: THREE.GLSL3,
29+
uniforms:{
30+
data : {value: texture},
31+
cmap : { value : cmap},
32+
},
33+
vertexShader,
34+
fragmentShader,
35+
side: THREE.DoubleSide
36+
})
37+
38+
useEffect(()=>{
39+
if (array){
40+
let newText;
41+
switch(operation){
42+
case "Max":
43+
newText = GPUCompute.Max(axis);
44+
break
45+
case "Min":
46+
newText = GPUCompute.Min(axis);
47+
break
48+
case "Mean":
49+
newText = GPUCompute.Mean(axis);
50+
break
51+
case "StDev":
52+
newText = GPUCompute.StDev(axis);
53+
break;
54+
default:
55+
newText = texture;
56+
break
57+
}
58+
setTexture(newText)
59+
setPlaneShape(shape.filter((_val,idx)=> idx !== axis))
60+
}
61+
},[execute])
62+
63+
return (
64+
<>
65+
<mesh material={shaderMaterial}>
66+
<planeGeometry args={[planeShape[1],planeShape[0]]} />
67+
</mesh>
68+
69+
</>
70+
)
71+
}
72+
73+
export default ComputeModule
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import * as THREE from 'three'
2+
import { GPUComputationRenderer } from 'three/examples/jsm/Addons.js'
3+
import { useThree } from '@react-three/fiber'
4+
import { MeanFrag, MaxFrag, MinFrag, StDevFrag } from './shaders'
5+
6+
7+
interface Array{
8+
data:number[],
9+
shape:number[],
10+
stride:number[]
11+
}
12+
13+
export class OneArrayCompute{
14+
private data: number[];
15+
private shape: number[];
16+
private stride: number[];
17+
private renderer: THREE.WebGLRenderer;
18+
private GPUCompute: GPUComputationRenderer;
19+
private texture: THREE.Data3DTexture | null;
20+
private targetAxis: number;
21+
private renderTarget: THREE.WebGLRenderTarget<THREE.Texture>;
22+
private initTexture: THREE.DataTexture
23+
24+
constructor(array: Array){
25+
this.data = array.data;
26+
this.shape = array.shape;
27+
this.stride = array.stride;
28+
this.renderer = useThree(state => state.gl)
29+
this.GPUCompute = new GPUComputationRenderer(10,10,this.renderer)
30+
this.initTexture = this.GPUCompute.createTexture()
31+
this.targetAxis = 5;
32+
this.renderTarget = this.GPUCompute.createRenderTarget(10,10,THREE.ClampToEdgeWrapping,THREE.ClampToEdgeWrapping,1006,1006);
33+
const size = array.shape[0]*array.shape[1]*array.shape[2]
34+
const newArray = new Float32Array(size)
35+
for (let i = 0; i<size; i++){
36+
newArray[i] = this.data[i]
37+
}
38+
this.texture = new THREE.Data3DTexture(newArray,this.shape[2],this.shape[1],this.shape[0])
39+
}
40+
41+
private initAxis(axis:number){
42+
const resolution = this.shape.filter((_val,idx)=> idx !== axis)
43+
this.GPUCompute = new GPUComputationRenderer(resolution[0],resolution[1],this.renderer)
44+
this.targetAxis = axis;
45+
this.renderTarget = this.GPUCompute.createRenderTarget(resolution[0],resolution[1],THREE.ClampToEdgeWrapping,THREE.ClampToEdgeWrapping,1006,1006)
46+
}
47+
48+
Mean(axis:number){
49+
if (axis !== this.targetAxis){
50+
this.initAxis(axis)
51+
}
52+
const reducer = this.GPUCompute.addVariable("reduction", MeanFrag, this.initTexture);
53+
reducer.material.uniforms[`dataArray]`] = { value: this.texture };
54+
reducer.material.uniforms['axisSize'] = { value: this.shape[this.targetAxis]}
55+
reducer.material.uniforms['axis'] = { value: this.targetAxis}
56+
this.GPUCompute.doRenderTarget(reducer.material,this.renderTarget)
57+
return this.renderTarget.texture
58+
}
59+
60+
Max(axis:number){
61+
if (axis !== this.targetAxis){
62+
this.initAxis(axis)
63+
}
64+
const reducer = this.GPUCompute.addVariable("reduction", MaxFrag, this.initTexture);
65+
reducer.material.uniforms[`dataArray]`] = { value: this.texture };
66+
reducer.material.uniforms['axisSize'] = { value: this.shape[this.targetAxis]}
67+
reducer.material.uniforms['axis'] = { value: this.targetAxis}
68+
this.GPUCompute.doRenderTarget(reducer.material,this.renderTarget)
69+
return this.renderTarget.texture
70+
}
71+
72+
Min(axis:number){
73+
if (axis !== this.targetAxis){
74+
this.initAxis(axis)
75+
}
76+
const reducer = this.GPUCompute.addVariable("reduction", MinFrag, this.initTexture);
77+
reducer.material.uniforms[`dataArray]`] = { value: this.texture };
78+
reducer.material.uniforms['axisSize'] = { value: this.shape[this.targetAxis]}
79+
reducer.material.uniforms['axis'] = { value: this.targetAxis}
80+
this.GPUCompute.doRenderTarget(reducer.material,this.renderTarget)
81+
return this.renderTarget.texture
82+
}
83+
84+
StDev(axis:number){
85+
if (axis !== this.targetAxis){
86+
this.initAxis(axis)
87+
}
88+
const reducer = this.GPUCompute.addVariable("reduction", StDevFrag, this.initTexture);
89+
reducer.material.uniforms[`dataArray]`] = { value: this.texture };
90+
reducer.material.uniforms['axisSize'] = { value: this.shape[this.targetAxis]}
91+
reducer.material.uniforms['axis'] = { value: this.targetAxis}
92+
this.GPUCompute.doRenderTarget(reducer.material,this.renderTarget)
93+
return this.renderTarget.texture
94+
}
95+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
uniform sampler3D dataArray;
2+
uniform int axis;
3+
uniform int axisSize;
4+
5+
void main() {
6+
vec2 uv = gl_FragCoord.xy / resolution.xy;
7+
8+
vec3 sampleCoord;
9+
10+
// Initialize sampling coordinates based on reduction axis
11+
if (axis == 0) {
12+
// Reduce along depth (D0), uv maps to (s,t) = (D2,D1)
13+
sampleCoord = vec3(uv.x, uv.y, 0.0);
14+
} else if (axis == 1) {
15+
// Reduce along height (D1), uv maps to (s,r) = (D2,D0)
16+
sampleCoord = vec3(uv.x, 0.0, uv.y);
17+
} else if (axis == 2) {
18+
// Reduce along width (D2), uv maps to (t,r) = (D1,D0)
19+
sampleCoord = vec3(0.0, uv.x, uv.y);
20+
}
21+
22+
float maxVal = 0.0;
23+
for (int m = 0; m < axisSize; m++) {
24+
float coord = (float(m) + 0.5) / float(axisSize); //0.5 for center of pixel
25+
26+
// Vary the appropriate coordinate
27+
if (axis == 0) {
28+
sampleCoord.z = coord; // Vary r (depth)
29+
} else if (axis == 1) {
30+
sampleCoord.y = coord; // Vary t (height)
31+
} else if (axis == 2) {
32+
sampleCoord.x = coord; // Vary s (width)
33+
}
34+
float sampled = texture(dataArray, sampleCoord).r; // Assuming data in red channel
35+
maxVal = sampled > maxVal ? sampled : maxVal;
36+
}
37+
38+
gl_FragColor = vec4(maxVal, 0.0, 0.0, 1.0); // Output mean in red channel
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
uniform sampler3D dataArray;
2+
uniform int axis;
3+
uniform int axisSize;
4+
5+
void main() {
6+
vec2 uv = gl_FragCoord.xy / resolution.xy;
7+
8+
vec3 sampleCoord;
9+
10+
// Initialize sampling coordinates based on reduction axis
11+
if (axis == 0) {
12+
// Reduce along depth (D0), uv maps to (s,t) = (D2,D1)
13+
sampleCoord = vec3(uv.x, uv.y, 0.0);
14+
} else if (axis == 1) {
15+
// Reduce along height (D1), uv maps to (s,r) = (D2,D0)
16+
sampleCoord = vec3(uv.x, 0.0, uv.y);
17+
} else if (axis == 2) {
18+
// Reduce along width (D2), uv maps to (t,r) = (D1,D0)
19+
sampleCoord = vec3(0.0, uv.x, uv.y);
20+
}
21+
22+
float sum = 0.0;
23+
for (int m = 0; m < axisSize; m++) {
24+
float coord = (float(m) + 0.5) / float(axisSize); //0.5 for center of pixel
25+
26+
// Vary the appropriate coordinate
27+
if (axis == 0) {
28+
sampleCoord.z = coord; // Vary r (depth)
29+
} else if (axis == 1) {
30+
sampleCoord.y = coord; // Vary t (height)
31+
} else if (axis == 2) {
32+
sampleCoord.x = coord; // Vary s (width)
33+
}
34+
35+
sum += texture(dataArray, sampleCoord).r; // Assuming data in red channel
36+
}
37+
38+
float mean = sum / float(axisSize);
39+
gl_FragColor = vec4(mean, 0.0, 0.0, 1.0); // Output mean in red channel
40+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
uniform sampler3D dataArray;
2+
uniform int axis;
3+
uniform int axisSize;
4+
5+
void main() {
6+
vec2 uv = gl_FragCoord.xy / resolution.xy;
7+
8+
vec3 sampleCoord;
9+
10+
// Initialize sampling coordinates based on reduction axis
11+
if (axis == 0) {
12+
// Reduce along depth (D0), uv maps to (s,t) = (D2,D1)
13+
sampleCoord = vec3(uv.x, uv.y, 0.0);
14+
} else if (axis == 1) {
15+
// Reduce along height (D1), uv maps to (s,r) = (D2,D0)
16+
sampleCoord = vec3(uv.x, 0.0, uv.y);
17+
} else if (axis == 2) {
18+
// Reduce along width (D2), uv maps to (t,r) = (D1,D0)
19+
sampleCoord = vec3(0.0, uv.x, uv.y);
20+
}
21+
22+
float minVal = 1.0e15; //BigNum
23+
for (int m = 0; m < axisSize; m++) {
24+
float coord = (float(m) + 0.5) / float(axisSize); //0.5 for center of pixel
25+
26+
// Vary the appropriate coordinate
27+
if (axis == 0) {
28+
sampleCoord.z = coord; // Vary r (depth)
29+
} else if (axis == 1) {
30+
sampleCoord.y = coord; // Vary t (height)
31+
} else if (axis == 2) {
32+
sampleCoord.x = coord; // Vary s (width)
33+
}
34+
float sampled = texture(dataArray, sampleCoord).r; // Assuming data in red channel
35+
minVal = sampled < minVal ? sampled : minVal;
36+
}
37+
38+
gl_FragColor = vec4(minVal, 0.0, 0.0, 1.0); // Output mean in red channel
39+
}

0 commit comments

Comments
 (0)