Skip to content

Commit 292965f

Browse files
Add bc4-r-unorm and astc-12x12-unorm brain images (#501)
* Add BC4-unorm brain image * Recreate bindgroup * Add astc-12x12-unorm brain image * Change default near and far values * Add comments for source files * Remove useless DecompressionStream * if unsupported, clear screen and show status message --------- Co-authored-by: Kai Ninomiya <[email protected]>
1 parent 2b84b82 commit 292965f

6 files changed

+97
-36
lines changed

public/assets/img/volume/t1_icbm_normal_1mm_pn0_rf0.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def load_byte_array_from_file(file_path):
5656
with open("t1_icbm_normal_1mm_pn0_rf0_180x216x180_uint8_1x1.bin", "rb") as f:
5757
bytes_data = f.read()
5858

59-
gzip_filename = "t1_icbm_normal_1mm_pn0_rf0_180x216x180_uint8_1x1.bin-gz"
59+
gzip_filename = "t1_icbm_normal_1mm_pn0_rf0_180x216x180_uint8_1x1.bin.gz"
6060

6161
with gzip.open(gzip_filename, "wb", compresslevel=9) as f:
6262
f.write(bytes_data)
Binary file not shown.

sample/volumeRenderingTexture3D/index.html

+10
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,21 @@
2020
max-width: 100%;
2121
display: block;
2222
}
23+
#status {
24+
position: fixed;
25+
left: 0;
26+
bottom: 0;
27+
background: darkred;
28+
color: white;
29+
font-family: monospace;
30+
font-size: 200%;
31+
}
2332
</style>
2433
<script defer src="main.js" type="module"></script>
2534
<script defer type="module" src="../../js/iframe-helper.js"></script>
2635
</head>
2736
<body>
2837
<canvas></canvas>
38+
<div id=status></div>
2939
</body>
3040
</html>

sample/volumeRenderingTexture3D/main.ts

+86-35
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,76 @@ import volumeWGSL from './volume.wgsl';
44
import { quitIfWebGPUNotAvailable } from '../util';
55

66
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
7+
const status = document.getElementById('status') as HTMLDivElement;
78

89
const gui = new GUI();
910

11+
const brainImages = {
12+
r8unorm: {
13+
bytesPerBlock: 1,
14+
blockLength: 1,
15+
feature: undefined,
16+
dataPath:
17+
'../../assets/img/volume/t1_icbm_normal_1mm_pn0_rf0_180x216x180_uint8_1x1.bin.gz',
18+
},
19+
'bc4-r-unorm': {
20+
bytesPerBlock: 8,
21+
blockLength: 4,
22+
feature: 'texture-compression-bc-sliced-3d',
23+
dataPath:
24+
'../../assets/img/volume/t1_icbm_normal_1mm_pn0_rf0_180x216x180_bc4_4x4.bin.gz',
25+
// Generated with texconv from https://github.com/microsoft/DirectXTex/releases
26+
},
27+
'astc-12x12-unorm': {
28+
bytesPerBlock: 16,
29+
blockLength: 12,
30+
feature: 'texture-compression-astc-sliced-3d',
31+
dataPath:
32+
'../../assets/img/volume/t1_icbm_normal_1mm_pn0_rf0_180x216x180_astc_12x12.bin.gz',
33+
// Generated with astcenc from https://github.com/ARM-software/astc-encoder/releases
34+
},
35+
};
36+
1037
// GUI parameters
11-
const params: { rotateCamera: boolean; near: number; far: number } = {
38+
const params: {
39+
rotateCamera: boolean;
40+
near: number;
41+
far: number;
42+
textureFormat: GPUTextureFormat;
43+
} = {
1244
rotateCamera: true,
13-
near: 2.0,
14-
far: 7.0,
45+
near: 4.3,
46+
far: 4.4,
47+
textureFormat: 'r8unorm',
1548
};
1649

1750
gui.add(params, 'rotateCamera', true);
1851
gui.add(params, 'near', 2.0, 7.0);
1952
gui.add(params, 'far', 2.0, 7.0);
53+
gui
54+
.add(params, 'textureFormat', Object.keys(brainImages))
55+
.onChange(async () => {
56+
await createVolumeTexture(params.textureFormat);
57+
});
2058

2159
const adapter = await navigator.gpu?.requestAdapter({
2260
featureLevel: 'compatibility',
2361
});
24-
const device = await adapter?.requestDevice();
62+
const requiredFeatures = [];
63+
if (adapter?.features.has('texture-compression-bc-sliced-3d')) {
64+
requiredFeatures.push(
65+
'texture-compression-bc',
66+
'texture-compression-bc-sliced-3d'
67+
);
68+
}
69+
if (adapter?.features.has('texture-compression-astc-sliced-3d')) {
70+
requiredFeatures.push(
71+
'texture-compression-astc',
72+
'texture-compression-astc-sliced-3d'
73+
);
74+
}
75+
const device = await adapter?.requestDevice({ requiredFeatures });
76+
2577
quitIfWebGPUNotAvailable(adapter, device);
2678
const context = canvas.getContext('webgpu') as GPUCanvasContext;
2779

@@ -77,34 +129,26 @@ const uniformBuffer = device.createBuffer({
77129
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
78130
});
79131

132+
let volumeTexture: GPUTexture | null = null;
133+
80134
// Fetch the image and upload it into a GPUTexture.
81-
let volumeTexture: GPUTexture;
82-
{
135+
async function createVolumeTexture(format: GPUTextureFormat) {
136+
volumeTexture = null;
137+
138+
const { blockLength, bytesPerBlock, dataPath, feature } = brainImages[format];
83139
const width = 180;
84140
const height = 216;
85141
const depth = 180;
86-
const format: GPUTextureFormat = 'r8unorm';
87-
const blockLength = 1;
88-
const bytesPerBlock = 1;
89142
const blocksWide = Math.ceil(width / blockLength);
90143
const blocksHigh = Math.ceil(height / blockLength);
91144
const bytesPerRow = blocksWide * bytesPerBlock;
92-
const dataPath =
93-
'../../assets/img/volume/t1_icbm_normal_1mm_pn0_rf0_180x216x180_uint8_1x1.bin-gz';
94145

95-
// Fetch the compressed data
96-
const response = await fetch(dataPath);
97-
const compressedArrayBuffer = await response.arrayBuffer();
98-
99-
// Decompress the data using DecompressionStream for gzip format
100-
const decompressionStream = new DecompressionStream('gzip');
101-
const decompressedStream = new Response(
102-
compressedArrayBuffer
103-
).body.pipeThrough(decompressionStream);
104-
const decompressedArrayBuffer = await new Response(
105-
decompressedStream
106-
).arrayBuffer();
107-
const byteArray = new Uint8Array(decompressedArrayBuffer);
146+
if (feature && !device.features.has(feature)) {
147+
status.textContent = `${feature} not supported`;
148+
return;
149+
} else {
150+
status.textContent = '';
151+
}
108152

109153
volumeTexture = device.createTexture({
110154
dimension: '3d',
@@ -113,16 +157,19 @@ let volumeTexture: GPUTexture;
113157
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
114158
});
115159

160+
const response = await fetch(dataPath);
161+
const buffer = await response.arrayBuffer();
162+
116163
device.queue.writeTexture(
117-
{
118-
texture: volumeTexture,
119-
},
120-
byteArray,
164+
{ texture: volumeTexture },
165+
buffer,
121166
{ bytesPerRow: bytesPerRow, rowsPerImage: blocksHigh },
122167
[width, height, depth]
123168
);
124169
}
125170

171+
await createVolumeTexture(params.textureFormat);
172+
126173
// Create a sampler with linear filtering for smooth interpolation.
127174
const sampler = device.createSampler({
128175
magFilter: 'linear',
@@ -131,7 +178,7 @@ const sampler = device.createSampler({
131178
maxAnisotropy: 16,
132179
});
133180

134-
const uniformBindGroup = device.createBindGroup({
181+
const bindGroupDescriptor: GPUBindGroupDescriptor = {
135182
layout: pipeline.getBindGroupLayout(0),
136183
entries: [
137184
{
@@ -146,17 +193,17 @@ const uniformBindGroup = device.createBindGroup({
146193
},
147194
{
148195
binding: 2,
149-
resource: volumeTexture.createView(),
196+
resource: undefined, // Assigned later
150197
},
151198
],
152-
});
199+
};
153200

154201
const renderPassDescriptor: GPURenderPassDescriptor = {
155202
colorAttachments: [
156203
{
157204
view: undefined, // Assigned later
158205

159-
clearValue: [0.5, 0.5, 0.5, 1.0],
206+
clearValue: [0, 0, 0, 1.0],
160207
loadOp: 'clear',
161208
storeOp: 'discard',
162209
},
@@ -207,9 +254,13 @@ function frame() {
207254

208255
const commandEncoder = device.createCommandEncoder();
209256
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
210-
passEncoder.setPipeline(pipeline);
211-
passEncoder.setBindGroup(0, uniformBindGroup);
212-
passEncoder.draw(3);
257+
if (volumeTexture) {
258+
bindGroupDescriptor.entries[2].resource = volumeTexture.createView();
259+
const uniformBindGroup = device.createBindGroup(bindGroupDescriptor);
260+
passEncoder.setPipeline(pipeline);
261+
passEncoder.setBindGroup(0, uniformBindGroup);
262+
passEncoder.draw(3);
263+
}
213264
passEncoder.end();
214265
device.queue.submit([commandEncoder.finish()]);
215266

0 commit comments

Comments
 (0)