Skip to content

Commit 88f7f94

Browse files
committed
feat(WebGPU): add vtkWebGPUBackground
1 parent cc8a76e commit 88f7f94

4 files changed

Lines changed: 309 additions & 147 deletions

File tree

Sources/Rendering/Core/Viewport/index.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ export interface vtkViewport extends vtkObject {
5757
*/
5858
getBackgroundByReference(): number[];
5959

60+
/**
61+
* Get the gradient background flag.
62+
* @returns {Boolean}
63+
*/
64+
getGradientBackground(): boolean;
65+
6066
/**
6167
*
6268
*/
@@ -177,6 +183,12 @@ export interface vtkViewport extends vtkObject {
177183
*/
178184
setBackgroundFrom(background: number[]): boolean;
179185

186+
/**
187+
* Set the gradient background flag.
188+
* @param {Boolean} gradientBackground
189+
*/
190+
setGradientBackground(gradientBackground: boolean): boolean;
191+
180192
/**
181193
* Specify the viewport for the Viewport to draw in the rendering window.
182194
* Each coordinate is 0 <= coordinate <= 1.0.

Sources/Rendering/Core/Viewport/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ export function extend(publicAPI, model, initialValues = {}) {
172172
macro.obj(publicAPI, model);
173173
macro.event(publicAPI, model, 'event');
174174

175+
macro.setGet(publicAPI, model, ['gradientBackground']);
175176
macro.setGetArray(publicAPI, model, ['viewport'], 4);
176177

177178
macro.setGetArray(publicAPI, model, ['background', 'background2'], 3);
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
import { mat4 } from 'gl-matrix';
2+
3+
import macro from 'vtk.js/Sources/macros';
4+
import { areEquals } from 'vtk.js/Sources/Common/Core/Math';
5+
import vtkWebGPUFullScreenQuad from 'vtk.js/Sources/Rendering/WebGPU/FullScreenQuad';
6+
import vtkWebGPUUniformBuffer from 'vtk.js/Sources/Rendering/WebGPU/UniformBuffer';
7+
8+
const solidFragTemplate = `
9+
//VTK::Renderer::Dec
10+
11+
//VTK::Mapper::Dec
12+
13+
//VTK::TCoord::Dec
14+
15+
//VTK::RenderEncoder::Dec
16+
17+
//VTK::IOStructs::Dec
18+
19+
@fragment
20+
fn main(
21+
//VTK::IOStructs::Input
22+
)
23+
//VTK::IOStructs::Output
24+
{
25+
var output: fragmentOutput;
26+
27+
var computedColor: vec4<f32> = mapperUBO.BackgroundColor;
28+
29+
//VTK::RenderEncoder::Impl
30+
return output;
31+
}
32+
`;
33+
34+
const gradientFragTemplate = `
35+
//VTK::Renderer::Dec
36+
37+
//VTK::Mapper::Dec
38+
39+
//VTK::TCoord::Dec
40+
41+
//VTK::RenderEncoder::Dec
42+
43+
//VTK::IOStructs::Dec
44+
45+
@fragment
46+
fn main(
47+
//VTK::IOStructs::Input
48+
)
49+
//VTK::IOStructs::Output
50+
{
51+
var output: fragmentOutput;
52+
53+
let t = clamp(input.tcoordVS.y, 0.0, 1.0);
54+
var computedColor: vec4<f32> = mix(mapperUBO.BackgroundColor2, mapperUBO.BackgroundColor, t);
55+
56+
//VTK::RenderEncoder::Impl
57+
return output;
58+
}
59+
`;
60+
61+
const textureFragTemplate = `
62+
//VTK::Renderer::Dec
63+
64+
//VTK::Mapper::Dec
65+
66+
//VTK::TCoord::Dec
67+
68+
//VTK::RenderEncoder::Dec
69+
70+
//VTK::IOStructs::Dec
71+
72+
@fragment
73+
fn main(
74+
//VTK::IOStructs::Input
75+
)
76+
//VTK::IOStructs::Output
77+
{
78+
var output: fragmentOutput;
79+
80+
var computedColor: vec4<f32> = textureSampleLevel(BackgroundTexture, BackgroundTextureSampler, input.tcoordVS, 0.0);
81+
82+
//VTK::RenderEncoder::Impl
83+
return output;
84+
}
85+
`;
86+
87+
const environmentFragTemplate = `
88+
fn vecToRectCoord(dir: vec3<f32>) -> vec2<f32> {
89+
var tau: f32 = 6.28318530718;
90+
var pi: f32 = 3.14159265359;
91+
var out: vec2<f32> = vec2<f32>(0.0);
92+
93+
out.x = atan2(dir.z, dir.x) / tau;
94+
out.x += 0.5;
95+
96+
var phix: f32 = length(vec2(dir.x, dir.z));
97+
out.y = atan2(dir.y, phix) / pi + 0.5;
98+
99+
return out;
100+
}
101+
102+
//VTK::Renderer::Dec
103+
104+
//VTK::Mapper::Dec
105+
106+
//VTK::TCoord::Dec
107+
108+
//VTK::RenderEncoder::Dec
109+
110+
//VTK::IOStructs::Dec
111+
112+
@fragment
113+
fn main(
114+
//VTK::IOStructs::Input
115+
)
116+
//VTK::IOStructs::Output
117+
{
118+
var output: fragmentOutput;
119+
120+
var tcoord: vec4<f32> = vec4<f32>(input.vertexVC.xy, -1, 1);
121+
var V: vec4<f32> = normalize(mapperUBO.FSQMatrix * tcoord);
122+
var background = textureSampleLevel(EnvironmentTexture, EnvironmentTextureSampler, vecToRectCoord(V.xyz), 0.0);
123+
var computedColor: vec4<f32> = vec4<f32>(background.rgb, 1.0);
124+
125+
//VTK::RenderEncoder::Impl
126+
return output;
127+
}
128+
`;
129+
130+
const _fsqMat4 = new Float64Array(16);
131+
const _tNormalMat4 = new Float64Array(16);
132+
133+
function vtkWebGPUBackground(publicAPI, model) {
134+
model.classHierarchy.push('vtkWebGPUBackground');
135+
136+
publicAPI.getMode = (renderer) => {
137+
if (
138+
renderer.getTexturedBackground?.() &&
139+
renderer.getBackgroundTexture?.().getImageLoaded?.()
140+
) {
141+
return {
142+
mode: 'texture',
143+
texture: renderer.getBackgroundTexture(),
144+
textureName: 'BackgroundTexture',
145+
pipelineHash: 'backgroundTexture',
146+
};
147+
}
148+
149+
if (
150+
renderer.getUseEnvironmentTextureAsBackground?.() &&
151+
renderer.getEnvironmentTexture?.().getImageLoaded?.()
152+
) {
153+
return {
154+
mode: 'environment',
155+
texture: renderer.getEnvironmentTexture(),
156+
textureName: 'EnvironmentTexture',
157+
pipelineHash: 'backgroundEnvironment',
158+
};
159+
}
160+
161+
const isGradientBackground = renderer.getGradientBackground?.();
162+
const background = renderer.getBackgroundByReference?.() ?? [0, 0, 0, 1];
163+
const background2 = renderer.getBackground2ByReference?.() ?? [0, 0, 0];
164+
const background2rgba = [...background2, 1.0];
165+
if (isGradientBackground && !areEquals(background, background2rgba)) {
166+
return {
167+
mode: 'gradient',
168+
texture: null,
169+
textureName: null,
170+
pipelineHash: 'backgroundGradient',
171+
};
172+
}
173+
174+
return {
175+
mode: 'solid',
176+
texture: null,
177+
textureName: null,
178+
pipelineHash: 'backgroundSolid',
179+
};
180+
};
181+
182+
publicAPI.ensureQuad = (device) => {
183+
if (model.quad) {
184+
return;
185+
}
186+
187+
model.quad = vtkWebGPUFullScreenQuad.newInstance();
188+
model.quad.setDevice(device);
189+
190+
model.UBO = vtkWebGPUUniformBuffer.newInstance({ label: 'mapperUBO' });
191+
model.UBO.addEntry('FSQMatrix', 'mat4x4<f32>');
192+
model.UBO.addEntry('BackgroundColor', 'vec4<f32>');
193+
model.UBO.addEntry('BackgroundColor2', 'vec4<f32>');
194+
model.quad.setUBO(model.UBO);
195+
};
196+
197+
publicAPI.getFragmentTemplate = (mode) => {
198+
switch (mode) {
199+
case 'gradient':
200+
return gradientFragTemplate;
201+
case 'texture':
202+
return textureFragTemplate;
203+
case 'environment':
204+
return environmentFragTemplate;
205+
case 'solid':
206+
default:
207+
return solidFragTemplate;
208+
}
209+
};
210+
211+
publicAPI.updateTexture = (device, texture, textureName) => {
212+
if (!texture || !textureName) {
213+
model.quad.setTextureViews([]);
214+
return;
215+
}
216+
217+
const webgpuTexture = device
218+
.getTextureManager()
219+
.getTextureForVTKTexture(texture, textureName);
220+
if (!webgpuTexture.getReady()) {
221+
model.quad.setTextureViews([]);
222+
return;
223+
}
224+
225+
const tview = webgpuTexture.createView(textureName);
226+
const interpolate = texture.getInterpolate?.() ? 'linear' : 'nearest';
227+
let options = {
228+
minFilter: interpolate,
229+
magFilter: interpolate,
230+
};
231+
if (textureName === 'EnvironmentTexture') {
232+
options = {
233+
addressModeU: 'repeat',
234+
addressModeV: 'clamp-to-edge',
235+
addressModeW: 'repeat',
236+
minFilter: interpolate,
237+
magFilter: interpolate,
238+
mipmapFilter: 'linear',
239+
};
240+
}
241+
tview.addSampler(device, options);
242+
model.quad.setTextureViews([tview]);
243+
};
244+
245+
publicAPI.updateUBO = (device, rendererNode, renderer) => {
246+
const background = renderer.getBackgroundByReference?.() ?? [0, 0, 0, 1];
247+
const background2 = renderer.getBackground2ByReference?.() ?? [0, 0, 0];
248+
model.UBO.setArray('BackgroundColor', background);
249+
model.UBO.setArray('BackgroundColor2', [...background2, 1.0]);
250+
251+
const keyMats = model.webgpuCamera.getKeyMatrices(rendererNode);
252+
mat4.transpose(_tNormalMat4, keyMats.normalMatrix);
253+
mat4.mul(_fsqMat4, keyMats.scvc, keyMats.pcsc);
254+
mat4.mul(_fsqMat4, _tNormalMat4, _fsqMat4);
255+
model.UBO.setArray('FSQMatrix', _fsqMat4);
256+
model.UBO.sendIfNeeded(device);
257+
};
258+
259+
publicAPI.render = (renderEncoder, rendererNode) => {
260+
const renderer = rendererNode.getRenderable();
261+
const device = rendererNode.getParent().getDevice();
262+
publicAPI.ensureQuad(device);
263+
264+
model.webgpuCamera = rendererNode.getViewNodeFor(
265+
renderer.getActiveCamera(),
266+
model.webgpuCamera
267+
);
268+
269+
const { mode, texture, textureName, pipelineHash } =
270+
publicAPI.getMode(renderer);
271+
model.quad.setPipelineHash(pipelineHash);
272+
model.quad.setFragmentShaderTemplate(publicAPI.getFragmentTemplate(mode));
273+
publicAPI.updateTexture(device, texture, textureName);
274+
publicAPI.updateUBO(device, rendererNode, renderer);
275+
model.quad.prepareAndDraw(renderEncoder);
276+
};
277+
}
278+
279+
const DEFAULT_VALUES = {
280+
quad: null,
281+
UBO: null,
282+
webgpuCamera: null,
283+
};
284+
285+
export function extend(publicAPI, model, initialValues = {}) {
286+
Object.assign(model, DEFAULT_VALUES, initialValues);
287+
macro.obj(publicAPI, model);
288+
vtkWebGPUBackground(publicAPI, model);
289+
}
290+
291+
export const newInstance = macro.newInstance(extend, 'vtkWebGPUBackground');
292+
293+
export default { newInstance, extend };

0 commit comments

Comments
 (0)