@kitware/vtk.js 33.1.1 → 33.2.1
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/IO/Geometry/GLTFImporter/Reader.js +5 -6
- package/Rendering/Core/Property.d.ts +31 -7
- package/Rendering/Core/Property.js +4 -2
- package/Rendering/Core/Texture.d.ts +12 -10
- package/Rendering/Core/Texture.js +132 -78
- package/Rendering/WebGPU/Buffer.js +3 -2
- package/Rendering/WebGPU/CellArrayMapper.js +48 -19
- package/Rendering/WebGPU/ForwardPass.js +1 -1
- package/Rendering/WebGPU/OrderIndependentTranslucentPass.js +3 -3
- package/Rendering/WebGPU/RenderEncoder.js +1 -1
- package/Rendering/WebGPU/Texture.js +67 -54
- package/Rendering/WebGPU/TextureManager.js +4 -1
- package/Rendering/WebGPU/VolumePass.js +5 -5
- package/package.json +1 -1
|
@@ -208,16 +208,15 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
|
|
|
208
208
|
property.setDiffuseTexture(diffuseTex);
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
+
|
|
212
|
+
// Handle metallic-roughness texture (metallicRoughnessTexture)
|
|
211
213
|
if (pbr.metallicRoughnessTexture) {
|
|
212
214
|
pbr.metallicRoughnessTexture.extensions;
|
|
213
215
|
const tex = pbr.metallicRoughnessTexture.texture;
|
|
214
216
|
const sampler = tex.sampler;
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
property.
|
|
218
|
-
const roughnessImage = await loadImage(tex.source, 'g');
|
|
219
|
-
const roughnessTex = createVTKTextureFromGLTFTexture(roughnessImage, sampler);
|
|
220
|
-
property.setRoughnessTexture(roughnessTex);
|
|
217
|
+
const rmImage = await loadImage(tex.source);
|
|
218
|
+
const rmTex = createVTKTextureFromGLTFTexture(rmImage, sampler);
|
|
219
|
+
property.setRMTexture(rmTex);
|
|
221
220
|
}
|
|
222
221
|
|
|
223
222
|
// Handle ambient occlusion texture (occlusionTexture)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { vtkObject } from './../../interfaces';
|
|
2
|
-
import { RGBColor } from './../../types';
|
|
2
|
+
import { Nullable, RGBColor } from './../../types';
|
|
3
3
|
import { Interpolation, Representation, Shading } from './Property/Constants';
|
|
4
4
|
import { vtkTexture } from './Texture';
|
|
5
5
|
|
|
@@ -14,6 +14,8 @@ export interface IPropertyInitialValues {
|
|
|
14
14
|
normalTexture?: vtkTexture;
|
|
15
15
|
ambientOcclusionTexture?: vtkTexture;
|
|
16
16
|
emissionTexture?: vtkTexture;
|
|
17
|
+
RMTexture?: vtkTexture;
|
|
18
|
+
ORMTexture?: vtkTexture;
|
|
17
19
|
edgeColor?: RGBColor;
|
|
18
20
|
ambient?: number;
|
|
19
21
|
diffuse?: number;
|
|
@@ -216,32 +218,42 @@ export interface vtkProperty extends vtkObject {
|
|
|
216
218
|
/**
|
|
217
219
|
* Get the diffuse texture.
|
|
218
220
|
*/
|
|
219
|
-
getDiffuseTexture(): vtkTexture
|
|
221
|
+
getDiffuseTexture(): Nullable<vtkTexture>;
|
|
220
222
|
|
|
221
223
|
/**
|
|
222
224
|
* Get the metallic texture.
|
|
223
225
|
*/
|
|
224
|
-
getMetallicTexture(): vtkTexture
|
|
226
|
+
getMetallicTexture(): Nullable<vtkTexture>;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get the roughness & metallic texture.
|
|
230
|
+
*/
|
|
231
|
+
getRMTexture(): Nullable<vtkTexture>;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get the occlusion, roughness & metallic texture.
|
|
235
|
+
*/
|
|
236
|
+
getORMTexture(): Nullable<vtkTexture>;
|
|
225
237
|
|
|
226
238
|
/**
|
|
227
239
|
* Get the roughness texture.
|
|
228
240
|
*/
|
|
229
|
-
getRoughnessTexture(): vtkTexture
|
|
241
|
+
getRoughnessTexture(): Nullable<vtkTexture>;
|
|
230
242
|
|
|
231
243
|
/**
|
|
232
244
|
* Get the normal texture.
|
|
233
245
|
*/
|
|
234
|
-
getNormalTexture(): vtkTexture
|
|
246
|
+
getNormalTexture(): Nullable<vtkTexture>;
|
|
235
247
|
|
|
236
248
|
/**
|
|
237
249
|
* Get the ambient occlusion texture.
|
|
238
250
|
*/
|
|
239
|
-
getAmbientOcclusionTexture(): vtkTexture
|
|
251
|
+
getAmbientOcclusionTexture(): Nullable<vtkTexture>;
|
|
240
252
|
|
|
241
253
|
/**
|
|
242
254
|
* Get the emission texture.
|
|
243
255
|
*/
|
|
244
|
-
getEmissionTexture(): vtkTexture
|
|
256
|
+
getEmissionTexture(): Nullable<vtkTexture>;
|
|
245
257
|
|
|
246
258
|
/**
|
|
247
259
|
* Set the ambient lighting coefficient.
|
|
@@ -500,6 +512,18 @@ export interface vtkProperty extends vtkObject {
|
|
|
500
512
|
*/
|
|
501
513
|
setRoughnessTexture(roughnessTexture: vtkTexture): boolean;
|
|
502
514
|
|
|
515
|
+
/**
|
|
516
|
+
* Set the roughness & metallic texture.
|
|
517
|
+
* @param {vtkTexture} RMTexture
|
|
518
|
+
*/
|
|
519
|
+
setRMTexture(RMTexture: vtkTexture): boolean;
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Set the occlusion, roughness & metallic texture.
|
|
523
|
+
* @param {vtkTexture} ORMTexture
|
|
524
|
+
*/
|
|
525
|
+
setORMTexture(ORMTexture: vtkTexture): boolean;
|
|
526
|
+
|
|
503
527
|
/**
|
|
504
528
|
* Set the normal texture.
|
|
505
529
|
* @param {vtkTexture} normalTexture
|
|
@@ -94,7 +94,9 @@ const DEFAULT_VALUES = {
|
|
|
94
94
|
lineWidth: 1,
|
|
95
95
|
lighting: true,
|
|
96
96
|
shading: false,
|
|
97
|
-
materialName: null
|
|
97
|
+
materialName: null,
|
|
98
|
+
ORMTexture: null,
|
|
99
|
+
RMTexture: null
|
|
98
100
|
};
|
|
99
101
|
|
|
100
102
|
// ----------------------------------------------------------------------------
|
|
@@ -105,7 +107,7 @@ function extend(publicAPI, model) {
|
|
|
105
107
|
|
|
106
108
|
// Build VTK API
|
|
107
109
|
macro.obj(publicAPI, model);
|
|
108
|
-
macro.setGet(publicAPI, model, ['lighting', 'interpolation', 'ambient', 'diffuse', 'metallic', 'roughness', 'normalStrength', 'emission', 'baseIOR', 'specular', 'specularPower', 'opacity', 'edgeVisibility', 'lineWidth', 'pointSize', 'backfaceCulling', 'frontfaceCulling', 'representation', 'diffuseTexture', 'metallicTexture', 'roughnessTexture', 'normalTexture', 'ambientOcclusionTexture', 'emissionTexture']);
|
|
110
|
+
macro.setGet(publicAPI, model, ['lighting', 'interpolation', 'ambient', 'diffuse', 'metallic', 'roughness', 'normalStrength', 'emission', 'baseIOR', 'specular', 'specularPower', 'opacity', 'edgeVisibility', 'lineWidth', 'pointSize', 'backfaceCulling', 'frontfaceCulling', 'representation', 'diffuseTexture', 'metallicTexture', 'roughnessTexture', 'normalTexture', 'ambientOcclusionTexture', 'emissionTexture', 'ORMTexture', 'RMTexture']);
|
|
109
111
|
macro.setGetArray(publicAPI, model, ['ambientColor', 'specularColor', 'diffuseColor', 'edgeColor'], 3);
|
|
110
112
|
|
|
111
113
|
// Object methods
|
|
@@ -98,19 +98,21 @@ export function extend(
|
|
|
98
98
|
export function newInstance(initialValues?: ITextureInitialValues): vtkTexture;
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
|
-
*
|
|
102
|
-
* width and a height that are powers of two.
|
|
101
|
+
* Generates mipmaps for a given GPU texture using a compute shader.
|
|
103
102
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
103
|
+
* This function iteratively generates each mip level for the provided texture,
|
|
104
|
+
* using a bilinear downsampling compute shader implemented in WGSL. It creates
|
|
105
|
+
* the necessary pipeline, bind groups, and dispatches compute passes for each
|
|
106
|
+
* mip level.
|
|
107
|
+
*
|
|
108
|
+
* @param {GPUDevice} device - The WebGPU device used to create resources and submit commands.
|
|
109
|
+
* @param {GPUTexture} texture - The GPU texture for which mipmaps will be generated.
|
|
110
|
+
* @param {number} mipLevelCount - The total number of mip levels to generate (including the base level).
|
|
108
111
|
*/
|
|
109
112
|
export function generateMipmaps(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
level: number
|
|
113
|
+
device: any,
|
|
114
|
+
texture: any,
|
|
115
|
+
mipLevelCount: number
|
|
114
116
|
): Array<Uint8ClampedArray>;
|
|
115
117
|
|
|
116
118
|
/**
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { m as macro } from '../../macros2.js';
|
|
2
2
|
|
|
3
|
+
/* eslint-disable no-bitwise */
|
|
4
|
+
|
|
3
5
|
// ----------------------------------------------------------------------------
|
|
4
6
|
// vtkTexture methods
|
|
5
7
|
// ----------------------------------------------------------------------------
|
|
@@ -114,90 +116,142 @@ function vtkTexture(publicAPI, model) {
|
|
|
114
116
|
};
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
let shift = 0;
|
|
142
|
-
for (let p = 0; p < imageData.length; p += hs) {
|
|
143
|
-
if (p % vs === 0) {
|
|
144
|
-
shift += 2 * hs * currentWidth;
|
|
119
|
+
/**
|
|
120
|
+
* Generates mipmaps for a given GPU texture using a compute shader.
|
|
121
|
+
*
|
|
122
|
+
* This function iteratively generates each mip level for the provided texture,
|
|
123
|
+
* using a bilinear downsampling compute shader implemented in WGSL. It creates
|
|
124
|
+
* the necessary pipeline, bind groups, and dispatches compute passes for each
|
|
125
|
+
* mip level.
|
|
126
|
+
*
|
|
127
|
+
* @param {GPUDevice} device - The WebGPU device used to create resources and submit commands.
|
|
128
|
+
* @param {GPUTexture} texture - The GPU texture for which mipmaps will be generated. Must be created with mip levels.
|
|
129
|
+
* @param {number} mipLevelCount - The total number of mip levels to generate (including the base level).
|
|
130
|
+
*/
|
|
131
|
+
const generateMipmaps = (device, texture, mipLevelCount) => {
|
|
132
|
+
const computeShaderCode = `
|
|
133
|
+
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
134
|
+
@group(0) @binding(1) var outputTexture: texture_storage_2d<rgba8unorm, write>;
|
|
135
|
+
|
|
136
|
+
@compute @workgroup_size(8, 8)
|
|
137
|
+
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
|
138
|
+
let texelCoord = vec2<i32>(global_id.xy);
|
|
139
|
+
let outputSize = textureDimensions(outputTexture);
|
|
140
|
+
|
|
141
|
+
if (texelCoord.x >= i32(outputSize.x) || texelCoord.y >= i32(outputSize.y)) {
|
|
142
|
+
return;
|
|
145
143
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
|
|
145
|
+
let inputSize = textureDimensions(inputTexture);
|
|
146
|
+
let scale = vec2<f32>(inputSize) / vec2<f32>(outputSize);
|
|
147
|
+
|
|
148
|
+
// Compute the floating-point source coordinate
|
|
149
|
+
let srcCoord = (vec2<f32>(texelCoord) + 0.5) * scale - 0.5;
|
|
150
|
+
|
|
151
|
+
// Get integer coordinates for the four surrounding texels
|
|
152
|
+
let x0 = i32(floor(srcCoord.x));
|
|
153
|
+
let x1 = min(x0 + 1, i32(inputSize.x) - 1);
|
|
154
|
+
let y0 = i32(floor(srcCoord.y));
|
|
155
|
+
let y1 = min(y0 + 1, i32(inputSize.y) - 1);
|
|
156
|
+
|
|
157
|
+
// Compute the weights
|
|
158
|
+
let wx = srcCoord.x - f32(x0);
|
|
159
|
+
let wy = srcCoord.y - f32(y0);
|
|
160
|
+
|
|
161
|
+
// Fetch the four texels
|
|
162
|
+
let c00 = textureLoad(inputTexture, vec2<i32>(x0, y0), 0);
|
|
163
|
+
let c10 = textureLoad(inputTexture, vec2<i32>(x1, y0), 0);
|
|
164
|
+
let c01 = textureLoad(inputTexture, vec2<i32>(x0, y1), 0);
|
|
165
|
+
let c11 = textureLoad(inputTexture, vec2<i32>(x1, y1), 0);
|
|
166
|
+
|
|
167
|
+
// Bilinear interpolation
|
|
168
|
+
let color = mix(
|
|
169
|
+
mix(c00, c10, wx),
|
|
170
|
+
mix(c01, c11, wx),
|
|
171
|
+
wy
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
textureStore(outputTexture, texelCoord, color);
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
const computeShader = device.createShaderModule({
|
|
178
|
+
code: computeShaderCode
|
|
179
|
+
});
|
|
180
|
+
const bindGroupLayout = device.createBindGroupLayout({
|
|
181
|
+
entries: [{
|
|
182
|
+
binding: 0,
|
|
183
|
+
// eslint-disable-next-line no-undef
|
|
184
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
185
|
+
texture: {
|
|
186
|
+
sampleType: 'float'
|
|
153
187
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
let x = -(kernel.length - 1) / 2;
|
|
162
|
-
let kw = kernelWeight;
|
|
163
|
-
let value = 0.0;
|
|
164
|
-
for (let k = 0; k < kernel.length; k++) {
|
|
165
|
-
let index = p + c + x * hs;
|
|
166
|
-
const lineShift = index % vs - (p + c) % vs;
|
|
167
|
-
if (lineShift > hs) index += vs;
|
|
168
|
-
if (lineShift < -hs) index -= vs;
|
|
169
|
-
if (dataCopy[index]) {
|
|
170
|
-
value += dataCopy[index] * kernel[k];
|
|
171
|
-
} else {
|
|
172
|
-
kw -= kernel[k];
|
|
173
|
-
}
|
|
174
|
-
x += 1;
|
|
175
|
-
}
|
|
176
|
-
imageData[p + c] = value / kw;
|
|
188
|
+
}, {
|
|
189
|
+
binding: 1,
|
|
190
|
+
// eslint-disable-next-line no-undef
|
|
191
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
192
|
+
storageTexture: {
|
|
193
|
+
format: 'rgba8unorm',
|
|
194
|
+
access: 'write-only'
|
|
177
195
|
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
let kw = kernelWeight;
|
|
185
|
-
let value = 0.0;
|
|
186
|
-
for (let k = 0; k < kernel.length; k++) {
|
|
187
|
-
const index = p + c + x * vs;
|
|
188
|
-
if (dataCopy[index]) {
|
|
189
|
-
value += dataCopy[index] * kernel[k];
|
|
190
|
-
} else {
|
|
191
|
-
kw -= kernel[k];
|
|
192
|
-
}
|
|
193
|
-
x += 1;
|
|
194
|
-
}
|
|
195
|
-
imageData[p + c] = value / kw;
|
|
196
|
+
}, {
|
|
197
|
+
binding: 2,
|
|
198
|
+
// eslint-disable-next-line no-undef
|
|
199
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
200
|
+
sampler: {
|
|
201
|
+
type: 'filtering'
|
|
196
202
|
}
|
|
203
|
+
}]
|
|
204
|
+
});
|
|
205
|
+
const pipelineLayout = device.createPipelineLayout({
|
|
206
|
+
bindGroupLayouts: [bindGroupLayout]
|
|
207
|
+
});
|
|
208
|
+
const pipeline = device.createComputePipeline({
|
|
209
|
+
layout: pipelineLayout,
|
|
210
|
+
compute: {
|
|
211
|
+
module: computeShader,
|
|
212
|
+
entryPoint: 'main'
|
|
197
213
|
}
|
|
198
|
-
|
|
214
|
+
});
|
|
215
|
+
const sampler = device.createSampler({
|
|
216
|
+
magFilter: 'linear',
|
|
217
|
+
minFilter: 'linear'
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Generate each mip level
|
|
221
|
+
for (let mipLevel = 1; mipLevel < mipLevelCount; mipLevel++) {
|
|
222
|
+
const srcView = texture.createView({
|
|
223
|
+
baseMipLevel: mipLevel - 1,
|
|
224
|
+
mipLevelCount: 1
|
|
225
|
+
});
|
|
226
|
+
const dstView = texture.createView({
|
|
227
|
+
baseMipLevel: mipLevel,
|
|
228
|
+
mipLevelCount: 1
|
|
229
|
+
});
|
|
230
|
+
const bindGroup = device.createBindGroup({
|
|
231
|
+
layout: pipeline.getBindGroupLayout(0),
|
|
232
|
+
entries: [{
|
|
233
|
+
binding: 0,
|
|
234
|
+
resource: srcView
|
|
235
|
+
}, {
|
|
236
|
+
binding: 1,
|
|
237
|
+
resource: dstView
|
|
238
|
+
}, {
|
|
239
|
+
binding: 2,
|
|
240
|
+
resource: sampler
|
|
241
|
+
}]
|
|
242
|
+
});
|
|
243
|
+
const commandEncoder = device.createCommandEncoder();
|
|
244
|
+
const computePass = commandEncoder.beginComputePass();
|
|
245
|
+
computePass.setPipeline(pipeline);
|
|
246
|
+
computePass.setBindGroup(0, bindGroup);
|
|
247
|
+
const mipWidth = Math.max(1, texture.width >> mipLevel);
|
|
248
|
+
const mipHeight = Math.max(1, texture.height >> mipLevel);
|
|
249
|
+
const workgroupsX = Math.ceil(mipWidth / 8);
|
|
250
|
+
const workgroupsY = Math.ceil(mipHeight / 8);
|
|
251
|
+
computePass.dispatchWorkgroups(workgroupsX, workgroupsY);
|
|
252
|
+
computePass.end();
|
|
253
|
+
device.queue.submit([commandEncoder.finish()]);
|
|
199
254
|
}
|
|
200
|
-
return maps;
|
|
201
255
|
};
|
|
202
256
|
|
|
203
257
|
// ----------------------------------------------------------------------------
|
|
@@ -42,13 +42,14 @@ function vtkWebGPUBuffer(publicAPI, model) {
|
|
|
42
42
|
bufferSubData(model.device.getHandle(), model.handle, 0, data.buffer);
|
|
43
43
|
};
|
|
44
44
|
publicAPI.createAndWrite = (data, usage) => {
|
|
45
|
+
const paddedSize = Math.ceil(data.byteLength / 4) * 4;
|
|
45
46
|
model.handle = model.device.getHandle().createBuffer({
|
|
46
|
-
size:
|
|
47
|
+
size: paddedSize,
|
|
47
48
|
usage,
|
|
48
49
|
mappedAtCreation: true,
|
|
49
50
|
label: model.label
|
|
50
51
|
});
|
|
51
|
-
model.sizeInBytes =
|
|
52
|
+
model.sizeInBytes = paddedSize;
|
|
52
53
|
model.usage = usage;
|
|
53
54
|
new Uint8Array(model.handle.getMappedRange()).set(new Uint8Array(data.buffer)); // memcpy
|
|
54
55
|
model.handle.unmap();
|
|
@@ -595,44 +595,65 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
|
|
|
595
595
|
const fDesc = pipeline.getShaderDescription('fragment');
|
|
596
596
|
code = fDesc.getCode();
|
|
597
597
|
const actor = model.WebGPUActor.getRenderable();
|
|
598
|
+
const property = actor.getProperty();
|
|
598
599
|
const checkDims = texture => {
|
|
599
600
|
if (!texture) return false;
|
|
600
601
|
const dims = texture.getDimensionality();
|
|
601
602
|
return dims === numComp;
|
|
602
603
|
};
|
|
603
604
|
const usedTextures = [];
|
|
604
|
-
|
|
605
|
+
const diffuseTexture = property.getDiffuseTexture?.();
|
|
606
|
+
if (diffuseTexture?.getImageLoaded() || actor.getTextures()[0] || model.colorTexture) {
|
|
605
607
|
if (
|
|
606
608
|
// Chained or statements here are questionable
|
|
607
|
-
checkDims(
|
|
609
|
+
checkDims(diffuseTexture) || checkDims(actor.getTextures()[0]) || checkDims(model.colorTexture)) {
|
|
608
610
|
usedTextures.push('_diffuseMap = textureSample(DiffuseTexture, DiffuseTextureSampler, input.tcoordVS);');
|
|
609
611
|
}
|
|
610
612
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
613
|
+
const ormTexture = property.getORMTexture?.();
|
|
614
|
+
const rmTexture = property.getRMTexture?.();
|
|
615
|
+
const roughnessTexture = property.getRoughnessTexture?.();
|
|
616
|
+
const metallicTexture = property.getMetallicTexture?.();
|
|
617
|
+
const ambientOcclusionTexture = property.getAmbientOcclusionTexture?.();
|
|
618
|
+
const emissionTexture = property.getEmissionTexture?.();
|
|
619
|
+
const normalTexture = property.getNormalTexture?.();
|
|
620
|
+
|
|
621
|
+
// ORM texture support: if present, sample R/G/B for AO/Roughness/Metallic
|
|
622
|
+
if (ormTexture?.getImageLoaded()) {
|
|
623
|
+
if (checkDims(ormTexture)) {
|
|
624
|
+
usedTextures.push('_ambientOcclusionMap = textureSample(ORMTexture, ORMTextureSampler, input.tcoordVS).rrra;', '_roughnessMap = textureSample(ORMTexture, ORMTextureSampler, input.tcoordVS).ggga;', '_metallicMap = textureSample(ORMTexture, ORMTextureSampler, input.tcoordVS).bbba;');
|
|
614
625
|
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
usedTextures.push('_metallicMap = textureSample(MetallicTexture, MetallicTextureSampler, input.tcoordVS);');
|
|
626
|
+
} else if (rmTexture?.getImageLoaded()) {
|
|
627
|
+
if (checkDims(rmTexture)) {
|
|
628
|
+
usedTextures.push('_roughnessMap = textureSample(RMTexture, RMTextureSampler, input.tcoordVS).ggga;', '_metallicMap = textureSample(RMTexture, RMTextureSampler, input.tcoordVS).bbba;');
|
|
619
629
|
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
630
|
+
} else {
|
|
631
|
+
if (roughnessTexture?.getImageLoaded()) {
|
|
632
|
+
if (checkDims(roughnessTexture)) {
|
|
633
|
+
usedTextures.push('_roughnessMap = textureSample(RoughnessTexture, RoughnessTextureSampler, input.tcoordVS);');
|
|
634
|
+
}
|
|
624
635
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
636
|
+
if (metallicTexture?.getImageLoaded()) {
|
|
637
|
+
if (checkDims(metallicTexture)) {
|
|
638
|
+
usedTextures.push('_metallicMap = textureSample(MetallicTexture, MetallicTextureSampler, input.tcoordVS);');
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (ambientOcclusionTexture?.getImageLoaded()) {
|
|
642
|
+
if (checkDims(ambientOcclusionTexture)) {
|
|
643
|
+
usedTextures.push('_ambientOcclusionMap = textureSample(AmbientOcclusionTexture, AmbientOcclusionTextureSampler, input.tcoordVS);');
|
|
644
|
+
}
|
|
629
645
|
}
|
|
630
646
|
}
|
|
631
|
-
if (
|
|
632
|
-
if (checkDims(
|
|
647
|
+
if (emissionTexture?.getImageLoaded()) {
|
|
648
|
+
if (checkDims(emissionTexture)) {
|
|
633
649
|
usedTextures.push('_emissionMap = textureSample(EmissionTexture, EmissionTextureSampler, input.tcoordVS);');
|
|
634
650
|
}
|
|
635
651
|
}
|
|
652
|
+
if (normalTexture?.getImageLoaded()) {
|
|
653
|
+
if (checkDims(normalTexture)) {
|
|
654
|
+
usedTextures.push('_normalMap = textureSample(NormalTexture, NormalTextureSampler, input.tcoordVS);');
|
|
655
|
+
}
|
|
656
|
+
}
|
|
636
657
|
code = vtkWebGPUShaderCache.substitute(code, '//VTK::TCoord::Impl', usedTextures).result;
|
|
637
658
|
fDesc.setCode(code);
|
|
638
659
|
};
|
|
@@ -863,6 +884,14 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
|
|
|
863
884
|
const pair = ['Diffuse', model.colorTexture];
|
|
864
885
|
textures.push(pair);
|
|
865
886
|
}
|
|
887
|
+
if (actor.getProperty().getORMTexture?.()) {
|
|
888
|
+
const pair = ['ORM', actor.getProperty().getORMTexture()];
|
|
889
|
+
textures.push(pair);
|
|
890
|
+
}
|
|
891
|
+
if (actor.getProperty().getRMTexture?.()) {
|
|
892
|
+
const pair = ['RM', actor.getProperty().getRMTexture()];
|
|
893
|
+
textures.push(pair);
|
|
894
|
+
}
|
|
866
895
|
if (actor.getProperty().getRoughnessTexture?.()) {
|
|
867
896
|
const pair = ['Roughness', actor.getProperty().getRoughnessTexture()];
|
|
868
897
|
textures.push(pair);
|
|
@@ -156,7 +156,7 @@ function vtkWebGPUOrderIndependentTranslucentPass(publicAPI, model) {
|
|
|
156
156
|
dstFactor: 'one'
|
|
157
157
|
},
|
|
158
158
|
alpha: {
|
|
159
|
-
|
|
159
|
+
srcFactor: 'one',
|
|
160
160
|
dstFactor: 'one'
|
|
161
161
|
}
|
|
162
162
|
}
|
|
@@ -168,7 +168,7 @@ function vtkWebGPUOrderIndependentTranslucentPass(publicAPI, model) {
|
|
|
168
168
|
dstFactor: 'one-minus-src'
|
|
169
169
|
},
|
|
170
170
|
alpha: {
|
|
171
|
-
|
|
171
|
+
srcFactor: 'one',
|
|
172
172
|
dstFactor: 'one-minus-src-alpha'
|
|
173
173
|
}
|
|
174
174
|
}
|
|
@@ -209,7 +209,7 @@ function vtkWebGPUOrderIndependentTranslucentPass(publicAPI, model) {
|
|
|
209
209
|
dstFactor: 'one-minus-src-alpha'
|
|
210
210
|
},
|
|
211
211
|
alpha: {
|
|
212
|
-
|
|
212
|
+
srcFactor: 'one',
|
|
213
213
|
dstFactor: 'one-minus-src-alpha'
|
|
214
214
|
}
|
|
215
215
|
}
|
|
@@ -67,6 +67,11 @@ function vtkWebGPUTexture(publicAPI, model) {
|
|
|
67
67
|
texture: model.handle,
|
|
68
68
|
premultipliedAlpha: true
|
|
69
69
|
}, [model.width, model.height, model.depth]);
|
|
70
|
+
|
|
71
|
+
// Generate mipmaps on GPU if needed
|
|
72
|
+
if (publicAPI.getDimensionality() !== 3 && model.mipLevel > 0) {
|
|
73
|
+
vtkTexture.generateMipmaps(model.device.getHandle(), model.handle, model.mipLevel + 1);
|
|
74
|
+
}
|
|
70
75
|
model.ready = true;
|
|
71
76
|
return;
|
|
72
77
|
}
|
|
@@ -80,35 +85,48 @@ function vtkWebGPUTexture(publicAPI, model) {
|
|
|
80
85
|
}
|
|
81
86
|
const tDetails = vtkWebGPUTypes.getDetailsFromTextureFormat(model.format);
|
|
82
87
|
let bufferBytesPerRow = model.width * tDetails.stride;
|
|
83
|
-
const
|
|
88
|
+
const alignTextureData = (arr, height, depth) => {
|
|
84
89
|
// bytesPerRow must be a multiple of 256 so we might need to rebuild
|
|
85
90
|
// the data here before passing to the buffer. e.g. if it is unorm8x4 then
|
|
86
91
|
// we need to have width be a multiple of 64
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// is this a half float texture?
|
|
92
|
+
// Check if the texture is half float
|
|
90
93
|
const halfFloat = tDetails.elementSize === 2 && tDetails.sampleType === 'float';
|
|
94
|
+
const bytesPerElement = arr.BYTES_PER_ELEMENT;
|
|
95
|
+
const inWidthInBytes = arr.length / (height * depth) * bytesPerElement;
|
|
96
|
+
|
|
97
|
+
// No changes needed if not half float and already aligned
|
|
98
|
+
if (!halfFloat && inWidthInBytes % 256 === 0) {
|
|
99
|
+
return [arr, inWidthInBytes];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Calculate dimensions for the new buffer
|
|
103
|
+
const inWidth = inWidthInBytes / bytesPerElement;
|
|
104
|
+
const outBytesPerElement = tDetails.elementSize;
|
|
105
|
+
const outWidthInBytes = 256 * Math.floor((inWidth * outBytesPerElement + 255) / 256);
|
|
106
|
+
const outWidth = outWidthInBytes / outBytesPerElement;
|
|
91
107
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
outArray[v * outWidth + i] = HalfFloat.toHalf(inArray[v * inWidth + i]);
|
|
104
|
-
}
|
|
105
|
-
} else {
|
|
106
|
-
outArray.set(inArray.subarray(v * inWidth, (v + 1) * inWidth), v * outWidth);
|
|
108
|
+
// Create the output array
|
|
109
|
+
const outArray = macro.newTypedArray(halfFloat ? 'Uint16Array' : arr.constructor.name, outWidth * height * depth);
|
|
110
|
+
|
|
111
|
+
// Copy and convert data when needed
|
|
112
|
+
const totalRows = height * depth;
|
|
113
|
+
if (halfFloat) {
|
|
114
|
+
for (let v = 0; v < totalRows; v++) {
|
|
115
|
+
const inOffset = v * inWidth;
|
|
116
|
+
const outOffset = v * outWidth;
|
|
117
|
+
for (let i = 0; i < inWidth; i++) {
|
|
118
|
+
outArray[outOffset + i] = HalfFloat.toHalf(arr[inOffset + i]);
|
|
107
119
|
}
|
|
108
120
|
}
|
|
109
|
-
|
|
121
|
+
} else if (outWidth === inWidth) {
|
|
122
|
+
// If the output width is the same as input, just copy
|
|
123
|
+
outArray.set(arr);
|
|
124
|
+
} else {
|
|
125
|
+
for (let v = 0; v < totalRows; v++) {
|
|
126
|
+
outArray.set(arr.subarray(v * inWidth, (v + 1) * inWidth), v * outWidth);
|
|
127
|
+
}
|
|
110
128
|
}
|
|
111
|
-
return [
|
|
129
|
+
return [outArray, outWidthInBytes];
|
|
112
130
|
};
|
|
113
131
|
if (req.nativeArray) {
|
|
114
132
|
nativeArray = req.nativeArray;
|
|
@@ -126,48 +144,43 @@ function vtkWebGPUTexture(publicAPI, model) {
|
|
|
126
144
|
}
|
|
127
145
|
const cmdEnc = model.device.createCommandEncoder();
|
|
128
146
|
if (publicAPI.getDimensionality() !== 3) {
|
|
129
|
-
// Non-3D
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
// Non-3D
|
|
148
|
+
// First, upload the base mip level (level 0)
|
|
149
|
+
const ret = alignTextureData(nativeArray, model.height, 1);
|
|
150
|
+
bufferBytesPerRow = ret[1];
|
|
151
|
+
const buffRequest = {
|
|
152
|
+
dataArray: req.dataArray ? req.dataArray : null,
|
|
153
|
+
nativeArray: ret[0],
|
|
154
|
+
usage: BufferUsage.Texture
|
|
155
|
+
};
|
|
156
|
+
const buff = model.device.getBufferManager().getBuffer(buffRequest);
|
|
157
|
+
cmdEnc.copyBufferToTexture({
|
|
158
|
+
buffer: buff.getHandle(),
|
|
159
|
+
offset: 0,
|
|
160
|
+
bytesPerRow: bufferBytesPerRow,
|
|
161
|
+
rowsPerImage: model.height
|
|
162
|
+
}, {
|
|
163
|
+
texture: model.handle,
|
|
164
|
+
mipLevel: 0
|
|
165
|
+
}, [model.width, model.height, 1]);
|
|
143
166
|
|
|
144
|
-
|
|
145
|
-
cmdEnc.copyBufferToTexture({
|
|
146
|
-
buffer: buff.getHandle(),
|
|
147
|
-
offset: 0,
|
|
148
|
-
bytesPerRow: bufferBytesPerRow,
|
|
149
|
-
rowsPerImage: currentHeight
|
|
150
|
-
}, {
|
|
151
|
-
texture: model.handle,
|
|
152
|
-
mipLevel: m
|
|
153
|
-
}, [currentWidth, currentHeight, 1]);
|
|
154
|
-
currentWidth /= 2;
|
|
155
|
-
currentHeight /= 2;
|
|
156
|
-
}
|
|
167
|
+
// Submit the base level upload
|
|
157
168
|
model.device.submitCommandEncoder(cmdEnc);
|
|
169
|
+
|
|
170
|
+
// Generate remaining mip levels on GPU
|
|
171
|
+
if (model.mipLevel > 0) {
|
|
172
|
+
vtkTexture.generateMipmaps(model.device.getHandle(), model.handle, model.mipLevel + 1);
|
|
173
|
+
}
|
|
158
174
|
model.ready = true;
|
|
159
175
|
} else {
|
|
160
176
|
// 3D, no mipmaps
|
|
161
|
-
const
|
|
162
|
-
bufferBytesPerRow =
|
|
177
|
+
const ret = alignTextureData(nativeArray, model.height, model.depth);
|
|
178
|
+
bufferBytesPerRow = ret[1];
|
|
163
179
|
const buffRequest = {
|
|
164
180
|
dataArray: req.dataArray ? req.dataArray : null,
|
|
165
|
-
/* eslint-disable no-undef */
|
|
166
181
|
usage: BufferUsage.Texture
|
|
167
|
-
/* eslint-enable no-undef */
|
|
168
182
|
};
|
|
169
|
-
|
|
170
|
-
buffRequest.nativeArray = fix[0];
|
|
183
|
+
buffRequest.nativeArray = ret[0];
|
|
171
184
|
const buff = model.device.getBufferManager().getBuffer(buffRequest);
|
|
172
185
|
cmdEnc.copyBufferToTexture({
|
|
173
186
|
buffer: buff.getHandle(),
|
|
@@ -2,6 +2,7 @@ import { m as macro } from '../../macros2.js';
|
|
|
2
2
|
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
3
3
|
import vtkWebGPUTexture from './Texture.js';
|
|
4
4
|
|
|
5
|
+
/* eslint-disable no-bitwise */
|
|
5
6
|
const {
|
|
6
7
|
VtkDataTypes
|
|
7
8
|
} = vtkDataArray;
|
|
@@ -69,6 +70,7 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
69
70
|
req.height = req.image.height;
|
|
70
71
|
req.depth = 1;
|
|
71
72
|
req.format = 'rgba8unorm';
|
|
73
|
+
req.usage = GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
// fill in based on js imageData
|
|
@@ -79,6 +81,7 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
79
81
|
req.format = 'rgba8unorm';
|
|
80
82
|
req.flip = true;
|
|
81
83
|
req.nativeArray = req.jsImageData.data;
|
|
84
|
+
req.usage = GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING;
|
|
82
85
|
}
|
|
83
86
|
if (req.canvas) {
|
|
84
87
|
req.width = req.canvas.width;
|
|
@@ -116,7 +119,7 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
116
119
|
// get a texture or create it if not cached.
|
|
117
120
|
// this is the main entry point
|
|
118
121
|
publicAPI.getTexture = req => {
|
|
119
|
-
// if we have a source
|
|
122
|
+
// if we have a source then get/create/cache the texture
|
|
120
123
|
if (req.hash) {
|
|
121
124
|
// if a matching texture already exists then return it
|
|
122
125
|
return model.device.getCachedObject(req.hash, _createTexture, req);
|
|
@@ -424,7 +424,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
424
424
|
operation: 'max'
|
|
425
425
|
},
|
|
426
426
|
alpha: {
|
|
427
|
-
|
|
427
|
+
srcFactor: 'one',
|
|
428
428
|
dstFactor: 'one',
|
|
429
429
|
operation: 'max'
|
|
430
430
|
}
|
|
@@ -438,7 +438,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
438
438
|
operation: 'min'
|
|
439
439
|
},
|
|
440
440
|
alpha: {
|
|
441
|
-
|
|
441
|
+
srcFactor: 'one',
|
|
442
442
|
dstFactor: 'one',
|
|
443
443
|
operation: 'min'
|
|
444
444
|
}
|
|
@@ -516,7 +516,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
516
516
|
dstFactor: 'one-minus-src-alpha'
|
|
517
517
|
},
|
|
518
518
|
alpha: {
|
|
519
|
-
|
|
519
|
+
srcFactor: 'one',
|
|
520
520
|
dstFactor: 'one-minus-src-alpha'
|
|
521
521
|
}
|
|
522
522
|
}
|
|
@@ -549,7 +549,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
549
549
|
dstFactor: 'one-minus-src-alpha'
|
|
550
550
|
},
|
|
551
551
|
alpha: {
|
|
552
|
-
|
|
552
|
+
srcFactor: 'one',
|
|
553
553
|
dstFactor: 'one-minus-src-alpha'
|
|
554
554
|
}
|
|
555
555
|
}
|
|
@@ -590,7 +590,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
590
590
|
dstFactor: 'one-minus-src-alpha'
|
|
591
591
|
},
|
|
592
592
|
alpha: {
|
|
593
|
-
|
|
593
|
+
srcFactor: 'one',
|
|
594
594
|
dstFactor: 'one-minus-src-alpha'
|
|
595
595
|
}
|
|
596
596
|
}
|