@vitessce/spatial-accelerated 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.js +52925 -0
- package/dist-tsc/SpatialWrapper.d.ts +2 -0
- package/dist-tsc/SpatialWrapper.d.ts.map +1 -0
- package/dist-tsc/SpatialWrapper.js +45 -0
- package/dist-tsc/VolumeDataManager.d.ts +170 -0
- package/dist-tsc/VolumeDataManager.d.ts.map +1 -0
- package/dist-tsc/VolumeDataManager.js +1273 -0
- package/dist-tsc/VolumeDataManager.test.d.ts +2 -0
- package/dist-tsc/VolumeDataManager.test.d.ts.map +1 -0
- package/dist-tsc/VolumeDataManager.test.js +164 -0
- package/dist-tsc/VolumeRenderManager.d.ts +265 -0
- package/dist-tsc/VolumeRenderManager.d.ts.map +1 -0
- package/dist-tsc/VolumeRenderManager.js +396 -0
- package/dist-tsc/VolumeShader.d.ts +262 -0
- package/dist-tsc/VolumeShader.d.ts.map +1 -0
- package/dist-tsc/VolumeShader.js +73 -0
- package/dist-tsc/VolumeView.d.ts +2 -0
- package/dist-tsc/VolumeView.d.ts.map +1 -0
- package/dist-tsc/VolumeView.js +521 -0
- package/dist-tsc/index.d.ts +2 -0
- package/dist-tsc/index.d.ts.map +1 -0
- package/dist-tsc/index.js +1 -0
- package/dist-tsc/shaders/GaussianFragmentShader.d.ts +2 -0
- package/dist-tsc/shaders/GaussianFragmentShader.d.ts.map +1 -0
- package/dist-tsc/shaders/GaussianFragmentShader.js +242 -0
- package/dist-tsc/shaders/GaussianVertexShader.d.ts +2 -0
- package/dist-tsc/shaders/GaussianVertexShader.d.ts.map +1 -0
- package/dist-tsc/shaders/GaussianVertexShader.js +51 -0
- package/dist-tsc/shaders/VolumeFragmentShader.d.ts +2 -0
- package/dist-tsc/shaders/VolumeFragmentShader.d.ts.map +1 -0
- package/dist-tsc/shaders/VolumeFragmentShader.js +1817 -0
- package/dist-tsc/shaders/VolumeVertexShader.d.ts +2 -0
- package/dist-tsc/shaders/VolumeVertexShader.d.ts.map +1 -0
- package/dist-tsc/shaders/VolumeVertexShader.js +110 -0
- package/package.json +63 -0
- package/src/SpatialWrapper.js +60 -0
- package/src/VolumeDataManager.js +1515 -0
- package/src/VolumeDataManager.test.js +180 -0
- package/src/VolumeRenderManager.js +589 -0
- package/src/VolumeShader.js +74 -0
- package/src/VolumeView.js +651 -0
- package/src/index.js +1 -0
- package/src/shaders/GaussianFragmentShader.js +243 -0
- package/src/shaders/GaussianVertexShader.js +52 -0
- package/src/shaders/VolumeFragmentShader.js +1818 -0
- package/src/shaders/VolumeVertexShader.js +111 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Vector2, Vector3, Vector4 } from 'three';
|
|
2
|
+
import { volumeVertexShader } from './shaders/VolumeVertexShader.js';
|
|
3
|
+
import { volumeFragmentShader } from './shaders/VolumeFragmentShader.js';
|
|
4
|
+
export const VolumeShader = {
|
|
5
|
+
uniforms: {
|
|
6
|
+
u_size: { value: new Vector3(1, 1, 1) },
|
|
7
|
+
clim0: { value: new Vector2(0.2, 0.8) },
|
|
8
|
+
clim1: { value: new Vector2(0.2, 0.8) },
|
|
9
|
+
clim2: { value: new Vector2(0.2, 0.8) },
|
|
10
|
+
clim3: { value: new Vector2(0.2, 0.8) },
|
|
11
|
+
clim4: { value: new Vector2(0.2, 0.8) },
|
|
12
|
+
clim5: { value: new Vector2(0.2, 0.8) },
|
|
13
|
+
clim6: { value: new Vector2(0.2, 0.8) },
|
|
14
|
+
clim7: { value: new Vector2(0.2, 0.8) },
|
|
15
|
+
xClip: { value: new Vector2(0, 1000000.0) },
|
|
16
|
+
yClip: { value: new Vector2(0, 1000000.0) },
|
|
17
|
+
zClip: { value: new Vector2(0, 1000000.0) },
|
|
18
|
+
u_window_size: { value: new Vector2(1, 1) },
|
|
19
|
+
u_vol_scale: { value: new Vector3(1, 1, 1) },
|
|
20
|
+
u_renderstyle: { value: 2 },
|
|
21
|
+
brickCacheTex: { type: 'sampler3D', value: null },
|
|
22
|
+
pageTableTex: { type: 'usampler3D', value: null },
|
|
23
|
+
color0: { value: new Vector4(0, 0, 0) },
|
|
24
|
+
color1: { value: new Vector4(0, 0, 0) },
|
|
25
|
+
color2: { value: new Vector4(0, 0, 0) },
|
|
26
|
+
color3: { value: new Vector4(0, 0, 0) },
|
|
27
|
+
color4: { value: new Vector4(0, 0, 0) },
|
|
28
|
+
color5: { value: new Vector4(0, 0, 0) },
|
|
29
|
+
color6: { value: new Vector4(0, 0, 0) },
|
|
30
|
+
channelMapping: {
|
|
31
|
+
value: [-1, -1, -1, -1, -1, -1, -1],
|
|
32
|
+
},
|
|
33
|
+
resGlobal: { value: new Vector2(0, 9) },
|
|
34
|
+
res0: { value: new Vector2(0, 9) },
|
|
35
|
+
res1: { value: new Vector2(0, 9) },
|
|
36
|
+
res2: { value: new Vector2(0, 9) },
|
|
37
|
+
res3: { value: new Vector2(0, 9) },
|
|
38
|
+
res4: { value: new Vector2(0, 9) },
|
|
39
|
+
res5: { value: new Vector2(0, 9) },
|
|
40
|
+
res6: { value: new Vector2(0, 9) },
|
|
41
|
+
maxChannels: { value: 0 },
|
|
42
|
+
lodFactor: { value: 1.0 },
|
|
43
|
+
near: { value: 0.1 },
|
|
44
|
+
far: { value: 10000 },
|
|
45
|
+
opacity: { value: 1 },
|
|
46
|
+
volumeCount: { value: 0 },
|
|
47
|
+
boxSize: { value: new Vector3(1, 1, 1) },
|
|
48
|
+
renderRes: { value: 1000 },
|
|
49
|
+
voxelExtents: { value: new Vector3(1, 1, 1) },
|
|
50
|
+
anchor0: { value: new Vector3(0, 0, 0) },
|
|
51
|
+
anchor1: { value: new Vector3(0, 0, 0) },
|
|
52
|
+
anchor2: { value: new Vector3(0, 0, 0) },
|
|
53
|
+
anchor3: { value: new Vector3(0, 0, 0) },
|
|
54
|
+
anchor4: { value: new Vector3(0, 0, 0) },
|
|
55
|
+
anchor5: { value: new Vector3(0, 0, 0) },
|
|
56
|
+
anchor6: { value: new Vector3(0, 0, 0) },
|
|
57
|
+
anchor7: { value: new Vector3(0, 0, 0) },
|
|
58
|
+
anchor8: { value: new Vector3(0, 0, 0) },
|
|
59
|
+
anchor9: { value: new Vector3(0, 0, 0) },
|
|
60
|
+
scale0: { value: new Vector3(1, 1, 1) },
|
|
61
|
+
scale1: { value: new Vector3(2, 2, 2) },
|
|
62
|
+
scale2: { value: new Vector3(4, 4, 4) },
|
|
63
|
+
scale3: { value: new Vector3(8, 8, 8) },
|
|
64
|
+
scale4: { value: new Vector3(16, 16, 16) },
|
|
65
|
+
scale5: { value: new Vector3(32, 32, 32) },
|
|
66
|
+
scale6: { value: new Vector3(64, 64, 64) },
|
|
67
|
+
scale7: { value: new Vector3(128, 128, 128) },
|
|
68
|
+
scale8: { value: new Vector3(256, 256, 256) },
|
|
69
|
+
scale9: { value: new Vector3(512, 512, 512) },
|
|
70
|
+
},
|
|
71
|
+
vertexShader: volumeVertexShader,
|
|
72
|
+
fragmentShader: volumeFragmentShader,
|
|
73
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VolumeView.d.ts","sourceRoot":"","sources":["../src/VolumeView.js"],"names":[],"mappings":"AAkLA,2DAwdC"}
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* eslint-disable max-len */
|
|
3
|
+
/* eslint-disable no-underscore-dangle */
|
|
4
|
+
/* eslint-disable no-unused-vars */
|
|
5
|
+
/* eslint-disable react/no-unknown-property */
|
|
6
|
+
/**
|
|
7
|
+
* VolumeView.js
|
|
8
|
+
*/
|
|
9
|
+
import React, { useRef, useState, useEffect, useCallback, useMemo } from 'react';
|
|
10
|
+
import { useThree, useFrame } from '@react-three/fiber';
|
|
11
|
+
import { OrbitControls } from '@react-three/drei';
|
|
12
|
+
import * as THREE from 'three';
|
|
13
|
+
import { WebGLMultipleRenderTargets } from 'three';
|
|
14
|
+
import { log, atLeastLogLevel, LogLevel } from '@vitessce/globals';
|
|
15
|
+
import { VolumeDataManager } from './VolumeDataManager.js';
|
|
16
|
+
import { VolumeRenderManager } from './VolumeRenderManager.js';
|
|
17
|
+
import { gaussianVertexShader } from './shaders/GaussianVertexShader.js';
|
|
18
|
+
import { gaussianFragmentShader } from './shaders/GaussianFragmentShader.js';
|
|
19
|
+
function framebufferFor(renderer, rt) {
|
|
20
|
+
const p = renderer.properties?.get(rt);
|
|
21
|
+
return p?.framebuffer || p?.__webglFramebuffer || rt.__webglFramebuffer;
|
|
22
|
+
}
|
|
23
|
+
function logWithColor(msg) {
|
|
24
|
+
if (atLeastLogLevel(LogLevel.DEBUG)) {
|
|
25
|
+
console.warn(`%cV: ${msg}`, 'background: deeppink; color: white; padding: 2px; border-radius: 3px;');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Functions called with useFrame
|
|
29
|
+
function performGeometryPass(_gl, _camera, _scene, { mrtRef }) {
|
|
30
|
+
// logWithColor('performGeometryPass');
|
|
31
|
+
_gl.setRenderTarget(mrtRef.current);
|
|
32
|
+
_gl.clear(true, true, true);
|
|
33
|
+
_gl.render(_scene, _camera);
|
|
34
|
+
}
|
|
35
|
+
function performBlitPass(_gl, { screenSceneRef, screenCameraRef }) {
|
|
36
|
+
// logWithColor('performBlitPass');
|
|
37
|
+
_gl.setRenderTarget(null);
|
|
38
|
+
_gl.clear(true, true, true);
|
|
39
|
+
_gl.render(screenSceneRef.current, screenCameraRef.current);
|
|
40
|
+
}
|
|
41
|
+
function handleRequests(_gl, { frameRef, dataManager, mrtRef, bufRequest, bufUsage }) {
|
|
42
|
+
// logWithColor('handleRequests');
|
|
43
|
+
// log.debug('handleRequests', frameRef.current);
|
|
44
|
+
const ctx = _gl.getContext();
|
|
45
|
+
const f = frameRef.current;
|
|
46
|
+
if (dataManager.noNewRequests === true && f % 100 === 0 && f < 500) {
|
|
47
|
+
// Every 100th frame of the first 500 frames.
|
|
48
|
+
// eslint-disable-next-line no-param-reassign
|
|
49
|
+
dataManager.noNewRequests = false;
|
|
50
|
+
}
|
|
51
|
+
if (dataManager.triggerRequest === true && dataManager.noNewRequests === false) {
|
|
52
|
+
// Read the pixels of the request buffer into the width*height*RGBA bufRequest.current array.
|
|
53
|
+
ctx.bindFramebuffer(ctx.READ_FRAMEBUFFER, framebufferFor(_gl, mrtRef.current));
|
|
54
|
+
ctx.readBuffer(ctx.COLOR_ATTACHMENT1);
|
|
55
|
+
ctx.readPixels(0, 0, mrtRef.current.width, mrtRef.current.height, ctx.RGBA, ctx.UNSIGNED_BYTE, bufRequest.current);
|
|
56
|
+
// Based on the request buffer contents, process the requests
|
|
57
|
+
// (e.g., start loading the brick data and upload to the brick cache).
|
|
58
|
+
// Finally, it will set triggerUsage to true.
|
|
59
|
+
dataManager.processRequestData(bufRequest.current);
|
|
60
|
+
}
|
|
61
|
+
else if (dataManager.triggerUsage === true && dataManager.noNewRequests === false) {
|
|
62
|
+
// Read the pixels of the usage buffer into the width*height*RGBA bufUsage.current array.
|
|
63
|
+
ctx.bindFramebuffer(ctx.READ_FRAMEBUFFER, framebufferFor(_gl, mrtRef.current));
|
|
64
|
+
ctx.readBuffer(ctx.COLOR_ATTACHMENT2);
|
|
65
|
+
ctx.readPixels(0, 0, mrtRef.current.width, mrtRef.current.height, ctx.RGBA, ctx.UNSIGNED_BYTE, bufUsage.current);
|
|
66
|
+
// Based on the usage buffer contents, process the usage data.
|
|
67
|
+
// This identifies used bricks and updates their timestamps.
|
|
68
|
+
// Timestamps are used to determine which bricks are most/least recently used for cache eviction.
|
|
69
|
+
// If the brick cache is full, it will create an LRU cache.
|
|
70
|
+
// Finally, it will set triggerRequest to true.
|
|
71
|
+
dataManager.processUsageData(bufUsage.current);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function handleAdaptiveQuality(clock, params) {
|
|
75
|
+
const { invalidate, isInteracting, dataManager, spatialRenderingModeChanging, meshRef, stillRef, screenQuadRef, lastSampleRef, frameRef, lastFrameCountRef, } = params;
|
|
76
|
+
// Moved out of useEffect
|
|
77
|
+
if (screenQuadRef.current) {
|
|
78
|
+
if (!stillRef.current) {
|
|
79
|
+
// log.debug('stillRef is false');
|
|
80
|
+
screenQuadRef.current.material.uniforms.gaussian.value = 7;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// log.debug('stillRef is true');
|
|
84
|
+
screenQuadRef.current.material.uniforms.gaussian.value = 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// End moved out of useEffect
|
|
88
|
+
if (isInteracting) {
|
|
89
|
+
// While the user is interacting, we want to ensure that the next frame's handleRequests call
|
|
90
|
+
// will trigger the processUsageData call, and subsequently the processRequestData call
|
|
91
|
+
// (since processUsageData will set triggerRequest to true its conclusion).
|
|
92
|
+
dataManager.noNewRequests = false;
|
|
93
|
+
dataManager.triggerUsage = true;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (spatialRenderingModeChanging)
|
|
97
|
+
return;
|
|
98
|
+
const meshRefUniforms = meshRef.current?.material?.uniforms;
|
|
99
|
+
const renderSpeed = meshRefUniforms?.renderRes?.value ?? dataManager?.PT?.lowestDataRes;
|
|
100
|
+
if (dataManager.noNewRequests) {
|
|
101
|
+
// If there are no more new requests, we can render in higher resolution.
|
|
102
|
+
if (renderSpeed !== 0) {
|
|
103
|
+
if (meshRefUniforms) {
|
|
104
|
+
meshRefUniforms.renderRes.value = 0; // Set to best quality
|
|
105
|
+
}
|
|
106
|
+
log.debug('Adaptive Quality: No new requests. Setting renderSpeed to 0 (best quality).');
|
|
107
|
+
stillRef.current = false;
|
|
108
|
+
screenQuadRef.current.material.uniforms.gaussian.value = 7;
|
|
109
|
+
invalidate();
|
|
110
|
+
}
|
|
111
|
+
else if (!stillRef.current) {
|
|
112
|
+
log.debug('Adaptive Quality: No new requests and already at best quality. Setting stillRef to true.');
|
|
113
|
+
stillRef.current = true;
|
|
114
|
+
screenQuadRef.current.material.uniforms.gaussian.value = 0;
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (stillRef.current) {
|
|
119
|
+
stillRef.current = false;
|
|
120
|
+
invalidate();
|
|
121
|
+
}
|
|
122
|
+
const t = clock.getElapsedTime();
|
|
123
|
+
if (t - lastSampleRef.current < 1) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const timeElapsedDuringSample = t - lastSampleRef.current;
|
|
127
|
+
const framesRenderedDuringSample = frameRef.current - lastFrameCountRef.current;
|
|
128
|
+
let fps = 0;
|
|
129
|
+
if (timeElapsedDuringSample > 0) {
|
|
130
|
+
fps = framesRenderedDuringSample / timeElapsedDuringSample;
|
|
131
|
+
}
|
|
132
|
+
lastSampleRef.current = t;
|
|
133
|
+
lastFrameCountRef.current = frameRef.current;
|
|
134
|
+
const upscale = fps > 100 && renderSpeed > 0; // Can't go below 0
|
|
135
|
+
const downscale = fps < 30 && renderSpeed < dataManager.PT.lowestDataRes; // Can't go above
|
|
136
|
+
if (upscale || downscale) {
|
|
137
|
+
const newSpeed = renderSpeed + (downscale ? 1 : -1);
|
|
138
|
+
if (meshRefUniforms) {
|
|
139
|
+
meshRefUniforms.renderRes.value = newSpeed;
|
|
140
|
+
}
|
|
141
|
+
invalidate(); // Ensure the change takes effect
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// END Functions called with useFrame
|
|
145
|
+
export function VolumeView(props) {
|
|
146
|
+
const { images, imageLayerScopes, imageLayerCoordination, imageChannelScopesByLayer, imageChannelCoordination,
|
|
147
|
+
// onInitComplete,
|
|
148
|
+
spatialRenderingMode, spatialRenderingModeChanging, } = props;
|
|
149
|
+
const { gl,
|
|
150
|
+
// scene,
|
|
151
|
+
// camera
|
|
152
|
+
} = useThree();
|
|
153
|
+
// Request a new render.
|
|
154
|
+
const invalidate = useThree(state => state.invalidate);
|
|
155
|
+
const orbitRef = useRef(null);
|
|
156
|
+
const meshRef = useRef(null);
|
|
157
|
+
const bufRequest = useRef(null);
|
|
158
|
+
const bufUsage = useRef(null);
|
|
159
|
+
const mrtRef = useRef(null);
|
|
160
|
+
// const [managers, setManagers] = useState(null);
|
|
161
|
+
const [renderState, setRenderState] = useState({
|
|
162
|
+
uniforms: null,
|
|
163
|
+
shader: null,
|
|
164
|
+
meshScale: [1, 1, 1],
|
|
165
|
+
geometrySize: [1, 1, 1],
|
|
166
|
+
});
|
|
167
|
+
// const [lastRes, setLastRes] = useState(null);
|
|
168
|
+
// const [lastChannels, setLastChannels] = useState([]);
|
|
169
|
+
const [is3D, setIs3D] = useState(false);
|
|
170
|
+
// const [loading, setLoading] = useState(false);
|
|
171
|
+
// Add new refs for screen quad setup
|
|
172
|
+
const screenSceneRef = useRef(null);
|
|
173
|
+
const screenCameraRef = useRef(null);
|
|
174
|
+
const screenQuadRef = useRef(null);
|
|
175
|
+
const [isInteracting, setIsInteracting] = useState(false);
|
|
176
|
+
const interactionTimeoutRef = useRef(null);
|
|
177
|
+
// Track interaction-triggering props
|
|
178
|
+
// const prevInteractionChannels = useRef();
|
|
179
|
+
// new refs from step 2
|
|
180
|
+
// const [renderSpeed, setRenderSpeed] = useState(dataManager.PT.lowestDataRes);
|
|
181
|
+
const stillRef = useRef(false); // skip geometry pass when true
|
|
182
|
+
const frameRef = useRef(0); // frame counter
|
|
183
|
+
const lastSampleRef = useRef(0);
|
|
184
|
+
const lastFrameCountRef = useRef(0); // For more stable FPS calculation
|
|
185
|
+
// const mainOrbitControlsRef = useRef(null); // Added for main view OrbitControls
|
|
186
|
+
// const sameArray = (a, b) => a && b && a.length === b.length && a.every((v, i) => v === b[i]);
|
|
187
|
+
// INITIALIZATION (GL-DEPENDENT ONLY)
|
|
188
|
+
const dataManager = useMemo(() => new VolumeDataManager(gl), [gl]);
|
|
189
|
+
const renderManager = useMemo(() => new VolumeRenderManager(), []);
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
logWithColor('useEffect MRT target matching canvas');
|
|
192
|
+
const { width, height } = gl.domElement;
|
|
193
|
+
// We use three render targets:
|
|
194
|
+
// layout(location = 0) out vec4 gColor: Final rendered color (sRGB)
|
|
195
|
+
// layout(location = 1) out vec4 gRequest: Brick loading requests (packed coordinates)
|
|
196
|
+
// layout(location = 2) out vec4 gUsage: Brick usage tracking (for cache management)
|
|
197
|
+
const mrt = new WebGLMultipleRenderTargets(width, height, 3);
|
|
198
|
+
mrt.texture.forEach((tex) => {
|
|
199
|
+
// eslint-disable-next-line no-param-reassign
|
|
200
|
+
tex.format = THREE.RGBAFormat;
|
|
201
|
+
// eslint-disable-next-line no-param-reassign
|
|
202
|
+
tex.type = THREE.UnsignedByteType;
|
|
203
|
+
// eslint-disable-next-line no-param-reassign
|
|
204
|
+
// tex.encoding = THREE.LinearEncoding;
|
|
205
|
+
// eslint-disable-next-line no-param-reassign
|
|
206
|
+
tex.minFilter = THREE.NearestFilter;
|
|
207
|
+
// eslint-disable-next-line no-param-reassign
|
|
208
|
+
tex.magFilter = THREE.NearestFilter;
|
|
209
|
+
// eslint-disable-next-line no-param-reassign
|
|
210
|
+
tex.generateMipmaps = false;
|
|
211
|
+
});
|
|
212
|
+
// Create screen quad setup
|
|
213
|
+
const screenScene = new THREE.Scene();
|
|
214
|
+
const screenCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 10);
|
|
215
|
+
screenCamera.position.z = 1;
|
|
216
|
+
const screenMaterial = new THREE.ShaderMaterial({
|
|
217
|
+
uniforms: {
|
|
218
|
+
// Bind the first render target texture as the input of the gaussian blur shader.
|
|
219
|
+
tDiffuse: { value: mrt.texture[0] },
|
|
220
|
+
resolution: { value: new THREE.Vector2(width, height) },
|
|
221
|
+
gaussian: { value: 7 },
|
|
222
|
+
},
|
|
223
|
+
vertexShader: gaussianVertexShader,
|
|
224
|
+
fragmentShader: gaussianFragmentShader,
|
|
225
|
+
transparent: true,
|
|
226
|
+
});
|
|
227
|
+
const screenQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), screenMaterial);
|
|
228
|
+
screenScene.add(screenQuad);
|
|
229
|
+
screenSceneRef.current = screenScene;
|
|
230
|
+
screenCameraRef.current = screenCamera;
|
|
231
|
+
screenQuadRef.current = screenQuad;
|
|
232
|
+
bufRequest.current = new Uint8Array(width * height * 4);
|
|
233
|
+
bufUsage.current = new Uint8Array(width * height * 4);
|
|
234
|
+
mrtRef.current = mrt;
|
|
235
|
+
return () => {
|
|
236
|
+
mrt.dispose();
|
|
237
|
+
screenMaterial.dispose();
|
|
238
|
+
screenQuad.geometry.dispose();
|
|
239
|
+
};
|
|
240
|
+
}, [gl]);
|
|
241
|
+
// INITIALIZATION (IMAGE-DEPENDENT)
|
|
242
|
+
const firstImageLayerScope = imageLayerScopes?.[0];
|
|
243
|
+
const firstImage = images?.[firstImageLayerScope];
|
|
244
|
+
const firstImageChannelScope = imageChannelScopesByLayer?.[firstImageLayerScope];
|
|
245
|
+
const firstImageLayerChannelCoordination = imageChannelCoordination?.[0]?.[firstImageLayerScope];
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
logWithColor('useEffect INIT');
|
|
248
|
+
if (!dataManager || !renderManager) {
|
|
249
|
+
log.debug('dataManager or renderManager not initialized yet');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!firstImage) {
|
|
253
|
+
log.debug('no first image layer yet');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (!firstImageLayerChannelCoordination) {
|
|
257
|
+
log.debug('no firstImageLayerChannelCoordination yet');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// TODO(mark): prevent dataManager.init from being called more than once.
|
|
261
|
+
(async () => {
|
|
262
|
+
// TODO(mark): separate the initialization which depends on gl, from the initialization which depends on images, from the dm.init(firstImageLayer)
|
|
263
|
+
dataManager.initImages(images, imageLayerScopes);
|
|
264
|
+
// TODO: generalize to more than one image layer.
|
|
265
|
+
await dataManager.init(firstImageLayerChannelCoordination); // device limits, zarr meta
|
|
266
|
+
// log.debug('dm.physicalScale', dataManager.physicalScale);
|
|
267
|
+
renderManager.setZarrUniforms(dataManager.zarrStore, dataManager.PT);
|
|
268
|
+
renderManager.setChannelMapping(dataManager.channels.colorMappings);
|
|
269
|
+
log.debug('rm.uniforms', renderManager.uniforms);
|
|
270
|
+
// setManagers({ dataManager: dm, renderManager: rm });
|
|
271
|
+
// if (onInitComplete) {
|
|
272
|
+
// onInitComplete({ zarrStoreInfo: dm.zarrStore, deviceLimits: dm.deviceLimits });
|
|
273
|
+
// }
|
|
274
|
+
})();
|
|
275
|
+
// The data manager does not have a clearCache method, should it?
|
|
276
|
+
// return () => {
|
|
277
|
+
// log.debug('VolumeView useEffect cleanup: running dataManager.clearCache()');
|
|
278
|
+
// dataManager?.clearCache?.();
|
|
279
|
+
// };
|
|
280
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
281
|
+
}, [dataManager, renderManager,
|
|
282
|
+
images, imageLayerScopes,
|
|
283
|
+
]);
|
|
284
|
+
/*
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
logWithColor('useEffect GL');
|
|
287
|
+
gl.autoClear = false;
|
|
288
|
+
}, [gl]);
|
|
289
|
+
*/
|
|
290
|
+
/*
|
|
291
|
+
const extractSettings = useCallback(() => {
|
|
292
|
+
logWithColor('callback extractSettings');
|
|
293
|
+
if (!dataManager || !renderManager) return null;
|
|
294
|
+
const ok = renderManager.updateFromProps(props);
|
|
295
|
+
return ok ? renderManager.extractRenderingSettingsFromProps(props) : null;
|
|
296
|
+
}, [props, dataManager, renderManager]);
|
|
297
|
+
*/
|
|
298
|
+
/*
|
|
299
|
+
const loadIfNeeded = useCallback(async (settings) => {
|
|
300
|
+
if (!settings || !dataManager || !renderManager) return;
|
|
301
|
+
|
|
302
|
+
const resolutionChanged = settings.resolution !== lastRes;
|
|
303
|
+
const channelsChanged = !sameArray(settings.channelTargetC, lastChannels);
|
|
304
|
+
|
|
305
|
+
// Always update rendering state
|
|
306
|
+
const renderManagerState = renderManager.updateRendering(dataManager);
|
|
307
|
+
if (renderManagerState) setRenderState(renderManagerState);
|
|
308
|
+
|
|
309
|
+
// Update tracking state
|
|
310
|
+
if (resolutionChanged) setLastRes(settings.resolution);
|
|
311
|
+
if (channelsChanged) setLastChannels([...settings.channelTargetC]);
|
|
312
|
+
|
|
313
|
+
stillRef.current = false;
|
|
314
|
+
}, [dataManager, renderManager, lastRes, lastChannels]);
|
|
315
|
+
*/
|
|
316
|
+
// TODO(mark): convert to a useMemo?
|
|
317
|
+
useEffect(() => {
|
|
318
|
+
logWithColor('useEffect spatialRenderingMode');
|
|
319
|
+
const on3D = spatialRenderingMode === '3D';
|
|
320
|
+
setIs3D(on3D);
|
|
321
|
+
if (on3D && dataManager && renderManager) {
|
|
322
|
+
// Direct call, no callbacks needed
|
|
323
|
+
const propsForRenderManager = {
|
|
324
|
+
images,
|
|
325
|
+
imageLayerScopes,
|
|
326
|
+
imageLayerCoordination,
|
|
327
|
+
imageChannelScopesByLayer,
|
|
328
|
+
imageChannelCoordination,
|
|
329
|
+
spatialRenderingMode,
|
|
330
|
+
};
|
|
331
|
+
if (renderManager.updateFromProps(propsForRenderManager)) {
|
|
332
|
+
const { zarrInit } = renderManager;
|
|
333
|
+
if (!zarrInit) {
|
|
334
|
+
// If textures have not yet been initialized, do so prior to calling .updateRendering,
|
|
335
|
+
// as this internally calls renderManager.updateUniforms,
|
|
336
|
+
// which (I anticipate) expects the textures to be initialized.
|
|
337
|
+
// Initialize textures without warnings
|
|
338
|
+
dataManager.ptTHREE.needsUpdate = false;
|
|
339
|
+
dataManager.bcTHREE.needsUpdate = false;
|
|
340
|
+
// Initialize textures if needed
|
|
341
|
+
dataManager.renderer.initTexture(dataManager.bcTHREE);
|
|
342
|
+
dataManager.renderer.initTexture(dataManager.ptTHREE);
|
|
343
|
+
// Handle the first brick request.
|
|
344
|
+
dataManager.initTexture();
|
|
345
|
+
}
|
|
346
|
+
// Render manager needs some things from the data manager.
|
|
347
|
+
const nextRenderState = renderManager.updateRendering({
|
|
348
|
+
zarrStoreShapes: dataManager.zarrStore.shapes,
|
|
349
|
+
originalScaleXYZ: dataManager.getOriginalScaleXYZ(),
|
|
350
|
+
physicalDimensionsXYZ: dataManager.getPhysicalDimensionsXYZ(),
|
|
351
|
+
maxResolutionXYZ: dataManager.getMaxResolutionXYZ(),
|
|
352
|
+
boxDimensionsXYZ: dataManager.getBoxDimensionsXYZ(),
|
|
353
|
+
normalizedScaleXYZ: dataManager.getNormalizedScaleXYZ(),
|
|
354
|
+
bcTHREE: dataManager.bcTHREE,
|
|
355
|
+
ptTHREE: dataManager.ptTHREE,
|
|
356
|
+
});
|
|
357
|
+
if (nextRenderState) {
|
|
358
|
+
setRenderState(nextRenderState);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}, [dataManager, renderManager, images, imageLayerScopes, imageLayerCoordination,
|
|
363
|
+
imageChannelScopesByLayer, imageChannelCoordination, spatialRenderingMode]);
|
|
364
|
+
// TODO(mark): can this logic be moved into useFrame?
|
|
365
|
+
/*
|
|
366
|
+
useEffect(() => {
|
|
367
|
+
logWithColor('useEffect stillRef');
|
|
368
|
+
// log.debug('useEffect stillRef');
|
|
369
|
+
// log.debug('stillRef', stillRef.current);
|
|
370
|
+
if (!screenQuadRef.current) {
|
|
371
|
+
log.debug('no screen quad yet');
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (!stillRef.current) {
|
|
375
|
+
// log.debug('stillRef is false');
|
|
376
|
+
screenQuadRef.current.material.uniforms.gaussian.value = 7;
|
|
377
|
+
} else {
|
|
378
|
+
//log.debug('stillRef is true');
|
|
379
|
+
screenQuadRef.current.material.uniforms.gaussian.value = 0;
|
|
380
|
+
}
|
|
381
|
+
}, [stillRef.current]);
|
|
382
|
+
*/
|
|
383
|
+
// TODO(mark): can this logic be moved into useFrame?
|
|
384
|
+
// But need to be careful, since currently, it only runs upon change of isInteracting,
|
|
385
|
+
// and we do not want to execute more `invalidate` calls than necessary/currently.
|
|
386
|
+
useEffect(() => {
|
|
387
|
+
logWithColor('useEffect isInteracting');
|
|
388
|
+
if (isInteracting) {
|
|
389
|
+
const meshRefUniforms = meshRef.current?.material?.uniforms;
|
|
390
|
+
if (meshRefUniforms) {
|
|
391
|
+
meshRefUniforms.renderRes.value = dataManager.PT.lowestDataRes;
|
|
392
|
+
}
|
|
393
|
+
// setRenderSpeed(dataManager.PT.lowestDataRes);
|
|
394
|
+
stillRef.current = false;
|
|
395
|
+
invalidate();
|
|
396
|
+
}
|
|
397
|
+
}, [invalidate, isInteracting]);
|
|
398
|
+
// Execute code on every rendered frame.
|
|
399
|
+
// A numerical renderPriority value will cause React Three Fiber
|
|
400
|
+
// to disable automatic rendering altogether.
|
|
401
|
+
// It will now be your responsibility to render
|
|
402
|
+
// (by calling invalidate()).
|
|
403
|
+
// Reference: https://r3f.docs.pmnd.rs/api/hooks#useframe
|
|
404
|
+
const RENDER_PRIORITY = 1;
|
|
405
|
+
useFrame((state, delta, xrFrame) => {
|
|
406
|
+
// NOTE: Do not update React state inside useFrame.
|
|
407
|
+
// Reference: https://r3f.docs.pmnd.rs/advanced/pitfalls#%E2%9D%8C-setstate-in-useframe-is-bad
|
|
408
|
+
if (!mrtRef.current || !dataManager || !renderManager)
|
|
409
|
+
return;
|
|
410
|
+
// Receive the same state as the useThree hook.
|
|
411
|
+
const { gl: frameGl, camera: frameCamera, scene: frameScene, clock, } = state;
|
|
412
|
+
if (!stillRef.current) {
|
|
413
|
+
performGeometryPass(frameGl, frameCamera, frameScene, { mrtRef });
|
|
414
|
+
}
|
|
415
|
+
performBlitPass(frameGl, { screenSceneRef, screenCameraRef });
|
|
416
|
+
handleRequests(frameGl, { frameRef, dataManager, mrtRef, bufRequest, bufUsage });
|
|
417
|
+
handleAdaptiveQuality(clock, {
|
|
418
|
+
invalidate,
|
|
419
|
+
isInteracting,
|
|
420
|
+
dataManager,
|
|
421
|
+
spatialRenderingModeChanging,
|
|
422
|
+
meshRef,
|
|
423
|
+
stillRef,
|
|
424
|
+
screenQuadRef,
|
|
425
|
+
lastSampleRef,
|
|
426
|
+
frameRef,
|
|
427
|
+
lastFrameCountRef,
|
|
428
|
+
});
|
|
429
|
+
frameRef.current += 1;
|
|
430
|
+
}, RENDER_PRIORITY);
|
|
431
|
+
/*
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
logWithColor('useEffect setProcessingTargets');
|
|
434
|
+
renderManager.setProcessingTargets(mrtRef.current);
|
|
435
|
+
}, [renderManager, mrtRef]);
|
|
436
|
+
*/
|
|
437
|
+
/*
|
|
438
|
+
useEffect(() => {
|
|
439
|
+
logWithColor('useEffect renderState');
|
|
440
|
+
const u = meshRef.current?.material?.uniforms;
|
|
441
|
+
if (!u) return;
|
|
442
|
+
u.renderRes.value = renderSpeed;
|
|
443
|
+
}, [renderSpeed]);
|
|
444
|
+
*/
|
|
445
|
+
/*
|
|
446
|
+
useEffect(() => {
|
|
447
|
+
logWithColor('useEffect mainOrbitControlsRef');
|
|
448
|
+
const controlsInstance = mainOrbitControlsRef.current; // Renamed to avoid conflict with controls in event handlers
|
|
449
|
+
if (!controlsInstance) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const handleStart = () => setIsInteracting(true);
|
|
454
|
+
|
|
455
|
+
const handleEnd = () => {
|
|
456
|
+
clearTimeout(interactionTimeoutRef.current);
|
|
457
|
+
interactionTimeoutRef.current = setTimeout(() => setIsInteracting(false), 300);
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const handleWheel = () => {
|
|
461
|
+
setIsInteracting(true);
|
|
462
|
+
clearTimeout(interactionTimeoutRef.current);
|
|
463
|
+
interactionTimeoutRef.current = setTimeout(() => setIsInteracting(false), 300);
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
controlsInstance.addEventListener('start', handleStart);
|
|
467
|
+
controlsInstance.addEventListener('end', handleEnd);
|
|
468
|
+
|
|
469
|
+
const { domElement } = controlsInstance;
|
|
470
|
+
// Ensure domElement is available
|
|
471
|
+
if (domElement) {
|
|
472
|
+
domElement.addEventListener('wheel', handleWheel, { passive: true });
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return () => {
|
|
476
|
+
if (controlsInstance) {
|
|
477
|
+
controlsInstance.removeEventListener('start', handleStart);
|
|
478
|
+
controlsInstance.removeEventListener('end', handleEnd);
|
|
479
|
+
if (domElement) {
|
|
480
|
+
domElement.removeEventListener('wheel', handleWheel);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
clearTimeout(interactionTimeoutRef.current);
|
|
484
|
+
};
|
|
485
|
+
}, [mainOrbitControlsRef.current, setIsInteracting]);
|
|
486
|
+
*/
|
|
487
|
+
const onOrbitControlsStart = useCallback((e) => {
|
|
488
|
+
setIsInteracting(true);
|
|
489
|
+
}, []);
|
|
490
|
+
const onOrbitControlsEnd = useCallback((e) => {
|
|
491
|
+
clearTimeout(interactionTimeoutRef.current);
|
|
492
|
+
interactionTimeoutRef.current = setTimeout(() => {
|
|
493
|
+
setIsInteracting(false);
|
|
494
|
+
}, 300);
|
|
495
|
+
}, []);
|
|
496
|
+
// Set isInteracting while the user is sliding the channel slider
|
|
497
|
+
// for smooth updates.
|
|
498
|
+
useEffect(() => {
|
|
499
|
+
logWithColor('useEffect firstImageLayerChannelCoordination');
|
|
500
|
+
setIsInteracting(true);
|
|
501
|
+
log.debug('something about channels changed');
|
|
502
|
+
dataManager.updateChannels(firstImageLayerChannelCoordination);
|
|
503
|
+
renderManager.setChannelMapping(dataManager.channels.colorMappings);
|
|
504
|
+
clearTimeout(interactionTimeoutRef.current);
|
|
505
|
+
interactionTimeoutRef.current = setTimeout(() => {
|
|
506
|
+
setIsInteracting(false);
|
|
507
|
+
}, 300);
|
|
508
|
+
}, [firstImageLayerChannelCoordination]);
|
|
509
|
+
// Render nothing during initialization.
|
|
510
|
+
if (!is3D || !dataManager || !renderManager)
|
|
511
|
+
return null;
|
|
512
|
+
if (!renderState.shader) {
|
|
513
|
+
return (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("meshBasicMaterial", { color: "#444", wireframe: true })] }), _jsx(OrbitControls, { ref: orbitRef })] }));
|
|
514
|
+
}
|
|
515
|
+
// Render the volume.
|
|
516
|
+
return (_jsxs("group", { children: [_jsx(OrbitControls
|
|
517
|
+
// ref={mainOrbitControlsRef}
|
|
518
|
+
, {
|
|
519
|
+
// ref={mainOrbitControlsRef}
|
|
520
|
+
enableDamping: false, onStart: onOrbitControlsStart, onEnd: onOrbitControlsEnd }), _jsxs("mesh", { ref: meshRef, scale: renderState.meshScale, children: [_jsx("boxGeometry", { args: renderState.geometrySize }), _jsx("shaderMaterial", { uniforms: renderState.uniforms, vertexShader: renderState.shader.vertexShader, fragmentShader: renderState.shader.fragmentShader, side: THREE.BackSide, transparent: false, glslVersion: THREE.GLSL3 })] })] }));
|
|
521
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SpatialWrapper } from './SpatialWrapper.js';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export const gaussianFragmentShader: "//\n// Input texture to blur\nuniform sampler2D tDiffuse;\n// Resolution of the texture (width, height)\nuniform vec2 resolution;\n// Blur strength: 1=no blur, 2-3=3x3 kernel, 4-5=5x5 kernel, 6+=7x7 kernel\nuniform int gaussian;\n// Texture coordinates for current pixel\nvarying vec2 vUv;\n\n/**\n * No blur - returns the original pixel color\n */\nvec4 noGaussian() {\n vec4 color = texture2D(tDiffuse, vUv);\n return color;\n}\n\n/**\n * Applies 3x3 Gaussian blur kernel\n * Samples 9 pixels in a 3x3 grid around the current pixel\n * Weights are based on 2D Gaussian distribution\n */\nvec4 gaussian3(vec2 texel) {\n vec4 color = vec4(0.0);\n\n // Top row: weights [0.0625, 0.125, 0.0625]\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, -1.0)) * 0.0625;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, -1.0)) * 0.125;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, -1.0)) * 0.0625;\n\n // Middle row: weights [0.125, 0.25, 0.125] (center pixel gets highest weight)\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 0.0)) * 0.125;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 0.0)) * 0.25;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 0.0)) * 0.125;\n\n // Bottom row: weights [0.0625, 0.125, 0.0625]\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 1.0)) * 0.0625;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 1.0)) * 0.125;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 1.0)) * 0.0625;\n\n return color;\n}\n\n/**\n * Applies 5x5 Gaussian blur kernel\n * Samples 25 pixels in a 5x5 grid around the current pixel\n * Weights sum to 1.0 (273/273) for proper normalization\n */\nvec4 gaussian5(vec2 texel) {\n vec4 color = vec4(0.0);\n\n // Row 1: weights [1/273, 4/273, 7/273, 4/273, 1/273]\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, -2.0)) * 1.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, -2.0)) * 4.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, -2.0)) * 7.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, -2.0)) * 4.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, -2.0)) * 1.0/273.0;\n \n // Row 2: weights [4/273, 16/273, 26/273, 16/273, 4/273]\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, -1.0)) * 4.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, -1.0)) * 16.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, -1.0)) * 26.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, -1.0)) * 16.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, -1.0)) * 4.0/273.0;\n\n // Row 3 (center): weights [7/273, 26/273, 41/273, 26/273, 7/273]\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, 0.0)) * 7.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 0.0)) * 26.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 0.0)) * 41.0/273.0; // Center pixel\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 0.0)) * 26.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, 0.0)) * 7.0/273.0;\n \n // Row 4: weights [4/273, 16/273, 26/273, 16/273, 4/273]\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, 1.0)) * 4.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 1.0)) * 16.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 1.0)) * 26.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 1.0)) * 16.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, 1.0)) * 4.0/273.0;\n \n // Row 5: weights [1/273, 4/273, 7/273, 4/273, 1/273]\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, 2.0)) * 1.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 2.0)) * 4.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 2.0)) * 7.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 2.0)) * 4.0/273.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, 2.0)) * 1.0/273.0;\n\n return color;\n}\n\n/**\n * Applies 7x7 Gaussian blur kernel\n * Samples 49 pixels in a 7x7 grid around the current pixel\n * Weights sum to 1.0 (1003/1003) for proper normalization\n * Creates the strongest blur effect\n */\nvec4 gaussian7(vec2 texel) {\n vec4 color = vec4(0.0);\n\n // Row 1: weights [0, 0, 1/1003, 2/1003, 1/1003, 0, 0]\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, -3.0)) * 1.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, -3.0)) * 2.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, -3.0)) * 1.0/1003.0;\n\n // Row 2: weights [0, 3/1003, 13/1003, 22/1003, 13/1003, 3/1003, 0]\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, -2.0)) * 3.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, -2.0)) * 13.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, -2.0)) * 22.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, -2.0)) * 13.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, -2.0)) * 3.0/1003.0;\n\n // Row 3: weights [1/1003, 13/1003, 59/1003, 97/1003, 59/1003, 13/1003, 1/1003]\n color += texture2D(tDiffuse, vUv + texel * vec2(-3.0, -1.0)) * 1.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, -1.0)) * 13.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, -1.0)) * 59.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, -1.0)) * 97.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, -1.0)) * 59.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, -1.0)) * 13.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 3.0, -1.0)) * 1.0/1003.0;\n \n // Row 4 (center): weights [2/1003, 22/1003, 97/1003, 159/1003, 97/1003, 22/1003, 2/1003]\n color += texture2D(tDiffuse, vUv + texel * vec2(-3.0, 0.0)) * 2.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, 0.0)) * 22.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 0.0)) * 97.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 0.0)) * 159.0/1003.0; // Center pixel\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 0.0)) * 97.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, 0.0)) * 22.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 3.0, 0.0)) * 2.0/1003.0;\n \n // Row 5: weights [1/1003, 13/1003, 59/1003, 97/1003, 59/1003, 13/1003, 1/1003]\n color += texture2D(tDiffuse, vUv + texel * vec2(-3.0, 1.0)) * 1.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, 1.0)) * 13.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 1.0)) * 59.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 1.0)) * 97.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 1.0)) * 59.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, 1.0)) * 13.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 3.0, 1.0)) * 1.0/1003.0;\n \n // Row 6: weights [0, 3/1003, 13/1003, 22/1003, 13/1003, 3/1003, 0]\n color += texture2D(tDiffuse, vUv + texel * vec2(-2.0, 2.0)) * 3.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 2.0)) * 13.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 2.0)) * 22.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 2.0)) * 13.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 2.0, 2.0)) * 3.0/1003.0;\n\n // Row 7: weights [0, 0, 1/1003, 2/1003, 1/1003, 0, 0]\n color += texture2D(tDiffuse, vUv + texel * vec2(-1.0, 3.0)) * 1.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 0.0, 3.0)) * 2.0/1003.0;\n color += texture2D(tDiffuse, vUv + texel * vec2( 1.0, 3.0)) * 1.0/1003.0;\n\n return color;\n}\n\n/**\n * Main fragment shader function\n * Determines which blur kernel to apply based on the 'gaussian' uniform\n */\nvoid main() {\n // Calculate the size of one texel (pixel) in texture coordinates\n vec2 texel = 1.0 / resolution;\n \n // Initialize output color (this line appears to be unused/debug code)\n vec4 color = vec4(1.0, 0.0, 0.0, 0.0);\n\n // TODO: remove these variables since not used\n bool left = vUv.x < 0.5;\n bool top = vUv.y < 0.5;\n\n if (gaussian > 1 && gaussian <= 3) {\n // Apply 3x3 Gaussian blur for values 2-3\n color = gaussian3(texel);\n } else if (gaussian > 3 && gaussian <= 5) {\n // Apply 5x5 Gaussian blur for values 4-5\n color = gaussian5(texel);\n } else if (gaussian > 5) {\n // Apply 7x7 Gaussian blur for values 6 and above\n color = gaussian7(texel);\n } else {\n // No blur for value 1 or less\n color = noGaussian();\n }\n\n // Output the final blurred color\n gl_FragColor = color;\n}\n";
|
|
2
|
+
//# sourceMappingURL=GaussianFragmentShader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GaussianFragmentShader.d.ts","sourceRoot":"","sources":["../../src/shaders/GaussianFragmentShader.js"],"names":[],"mappings":"AAyDA,+1RAyLE"}
|