From c40436a6e9f1234e424fbbb2978fbea39decb990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaufort?= Date: Wed, 30 Sep 2026 11:19:30 +0200 Subject: [PATCH 1/6] Add color space selection to particles sample Let users pick the canvas colorSpace (srgb, srgb-linear, display-p3, display-p3-linear) in a new "Color settings" folder. Only color spaces supported by the browser are listed, and a warning is shown when a display-p3 space is selected on a non wide gamut display. --- sample/particles/main.ts | 43 +++++++++++++++++++++++++++++++++++++++- sample/particles/meta.ts | 2 +- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/sample/particles/main.ts b/sample/particles/main.ts index 89237ff6..4364e868 100644 --- a/sample/particles/main.ts +++ b/sample/particles/main.ts @@ -33,10 +33,12 @@ const presentationFormat = 'rgba16float'; function configureContext() { context.configure({ device, + colorSpace: simulationParams.colorSpace as PredefinedColorSpace, format: presentationFormat, toneMapping: { mode: simulationParams.toneMappingMode }, }); hdrFolder.name = getHdrFolderName(); + updateColorSpaceName(); } const particlesBuffer = device.createBuffer({ @@ -306,6 +308,7 @@ device.queue.copyExternalImageToTexture( const simulationParams = { simulate: true, deltaTime: 0.04, + colorSpace: 'srgb' as string, toneMappingMode: 'standard' as GPUCanvasToneMappingMode, brightnessFactor: 1.0, }; @@ -325,7 +328,27 @@ const gui = new GUI(); gui.width = 325; gui.add(simulationParams, 'simulate'); gui.add(simulationParams, 'deltaTime'); -const hdrFolder = gui.addFolder(''); +const colorFolder = gui.addFolder('Color settings'); +colorFolder + .add( + simulationParams, + 'colorSpace', + ['srgb', 'srgb-linear', 'display-p3', 'display-p3-linear'].filter( + isColorSpaceSupported + ) + ) + .onChange(configureContext); +colorFolder.open(); +const p3MediaQuery = window.matchMedia('(color-gamut: p3)'); +function updateColorSpaceName() { + const wantsP3 = simulationParams.colorSpace.startsWith('display-p3'); + colorFolder.name = + wantsP3 && !p3MediaQuery.matches + ? "Color settings ⚠️ Display isn't wide gamut" + : 'Color settings'; +} +p3MediaQuery.onchange = updateColorSpaceName; +const hdrFolder = gui.addFolder('HDR settings'); hdrFolder .add(simulationParams, 'toneMappingMode', ['standard', 'extended']) .onChange(configureContext); @@ -446,3 +469,21 @@ function assert(cond: boolean, msg = '') { throw new Error(msg); } } + +// Configuring a canvas context with an unsupported color space throws a +// TypeError, so probe each one on a throwaway OffscreenCanvas context. +function isColorSpaceSupported(colorSpace: string) { + const ctx = new OffscreenCanvas(1, 1).getContext('webgpu'); + try { + ctx.configure({ + device, + format: presentationFormat, + colorSpace: colorSpace as PredefinedColorSpace, + }); + return true; + } catch { + return false; + } finally { + ctx.unconfigure(); + } +} diff --git a/sample/particles/meta.ts b/sample/particles/meta.ts index a3416d84..465bb856 100644 --- a/sample/particles/meta.ts +++ b/sample/particles/meta.ts @@ -2,7 +2,7 @@ export default { name: 'Particles (HDR)', tocName: 'particles (HDR)', description: - 'This example demonstrates rendering of particles (using HDR capabilities when possible) simulated with compute shaders.', + 'This example demonstrates rendering of particles simulated with compute shaders, using HDR and wide color gamut canvas capabilities when possible.', filename: __DIRNAME__, sources: [ { path: 'main.ts' }, From d7e0043f80cb07bfd85d16aaacd526caab709a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaufort?= Date: Thu, 1 Oct 2026 07:22:42 +0200 Subject: [PATCH 2/6] Use color space when copying image to particles texture Pass the selected color space to copyExternalImageToTexture and re-copy the image when it changes. Switch the texture to rgba16float to avoid banding with linear color spaces. --- sample/particles/main.ts | 44 ++++++++++++++++++---------- sample/particles/probabilityMap.wgsl | 2 +- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/sample/particles/main.ts b/sample/particles/main.ts index 4364e868..0fedd621 100644 --- a/sample/particles/main.ts +++ b/sample/particles/main.ts @@ -30,6 +30,14 @@ canvas.width = canvas.clientWidth * devicePixelRatio; canvas.height = canvas.clientHeight * devicePixelRatio; const presentationFormat = 'rgba16float'; +const simulationParams = { + simulate: true, + deltaTime: 0.04, + colorSpace: 'srgb' as string, + toneMappingMode: 'standard' as GPUCanvasToneMappingMode, + brightnessFactor: 1.0, +}; + function configureContext() { context.configure({ device, @@ -191,18 +199,27 @@ const mipLevelCount = const texture = device.createTexture({ size: [imageBitmap.width, imageBitmap.height, 1], mipLevelCount, - format: 'rgba8unorm', + format: 'rgba16float', usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT, }); -device.queue.copyExternalImageToTexture( - { source: imageBitmap }, - { texture: texture }, - [imageBitmap.width, imageBitmap.height] -); +// Copies the image into mip level 0, converting its colors to the current +// color space. The alpha channel (used for the probability map) is unaffected +// by the color space, so the probability map doesn't need regenerating. +function copyImageToTexture() { + device.queue.copyExternalImageToTexture( + { source: imageBitmap }, + { + texture: texture, + colorSpace: simulationParams.colorSpace as PredefinedColorSpace, + }, + [imageBitmap.width, imageBitmap.height] + ); +} +copyImageToTexture(); ////////////////////////////////////////////////////////////////////////////// // Probability map generation @@ -277,7 +294,7 @@ device.queue.copyExternalImageToTexture( // tex_in / tex_out binding: 3, resource: texture.createView({ - format: 'rgba8unorm', + format: 'rgba16float', dimension: '2d', baseMipLevel: level, mipLevelCount: 1, @@ -305,14 +322,6 @@ device.queue.copyExternalImageToTexture( ////////////////////////////////////////////////////////////////////////////// // Simulation compute pipeline ////////////////////////////////////////////////////////////////////////////// -const simulationParams = { - simulate: true, - deltaTime: 0.04, - colorSpace: 'srgb' as string, - toneMappingMode: 'standard' as GPUCanvasToneMappingMode, - brightnessFactor: 1.0, -}; - const simulationUBOBufferSize = 1 * 4 + // deltaTime 1 * 4 + // brightnessFactor @@ -337,7 +346,10 @@ colorFolder isColorSpaceSupported ) ) - .onChange(configureContext); + .onChange(() => { + configureContext(); + copyImageToTexture(); + }); colorFolder.open(); const p3MediaQuery = window.matchMedia('(color-gamut: p3)'); function updateColorSpaceName() { diff --git a/sample/particles/probabilityMap.wgsl b/sample/particles/probabilityMap.wgsl index f5e08f5b..0016a539 100644 --- a/sample/particles/probabilityMap.wgsl +++ b/sample/particles/probabilityMap.wgsl @@ -6,7 +6,7 @@ struct UBO { @binding(1) @group(0) var buf_in : array; @binding(2) @group(0) var buf_out : array; @binding(3) @group(0) var tex_in : texture_2d; -@binding(3) @group(0) var tex_out : texture_storage_2d; +@binding(3) @group(0) var tex_out : texture_storage_2d; //////////////////////////////////////////////////////////////////////////////// // import_level From 68e237c106842466d9f3577ddd8c6cb4cbcd3694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaufort?= Date: Fri, 2 Oct 2026 08:08:22 +0200 Subject: [PATCH 3/6] Split color space and tone mapping into displaySettings in particles sample --- sample/particles/main.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/sample/particles/main.ts b/sample/particles/main.ts index 0fedd621..ed6c7558 100644 --- a/sample/particles/main.ts +++ b/sample/particles/main.ts @@ -30,20 +30,17 @@ canvas.width = canvas.clientWidth * devicePixelRatio; canvas.height = canvas.clientHeight * devicePixelRatio; const presentationFormat = 'rgba16float'; -const simulationParams = { - simulate: true, - deltaTime: 0.04, +const displaySettings = { colorSpace: 'srgb' as string, toneMappingMode: 'standard' as GPUCanvasToneMappingMode, - brightnessFactor: 1.0, }; function configureContext() { context.configure({ device, - colorSpace: simulationParams.colorSpace as PredefinedColorSpace, + colorSpace: displaySettings.colorSpace as PredefinedColorSpace, format: presentationFormat, - toneMapping: { mode: simulationParams.toneMappingMode }, + toneMapping: { mode: displaySettings.toneMappingMode }, }); hdrFolder.name = getHdrFolderName(); updateColorSpaceName(); @@ -214,7 +211,7 @@ function copyImageToTexture() { { source: imageBitmap }, { texture: texture, - colorSpace: simulationParams.colorSpace as PredefinedColorSpace, + colorSpace: displaySettings.colorSpace as PredefinedColorSpace, }, [imageBitmap.width, imageBitmap.height] ); @@ -322,6 +319,12 @@ copyImageToTexture(); ////////////////////////////////////////////////////////////////////////////// // Simulation compute pipeline ////////////////////////////////////////////////////////////////////////////// +const simulationParams = { + simulate: true, + deltaTime: 0.04, + brightnessFactor: 1.0, +}; + const simulationUBOBufferSize = 1 * 4 + // deltaTime 1 * 4 + // brightnessFactor @@ -340,7 +343,7 @@ gui.add(simulationParams, 'deltaTime'); const colorFolder = gui.addFolder('Color settings'); colorFolder .add( - simulationParams, + displaySettings, 'colorSpace', ['srgb', 'srgb-linear', 'display-p3', 'display-p3-linear'].filter( isColorSpaceSupported @@ -353,7 +356,7 @@ colorFolder colorFolder.open(); const p3MediaQuery = window.matchMedia('(color-gamut: p3)'); function updateColorSpaceName() { - const wantsP3 = simulationParams.colorSpace.startsWith('display-p3'); + const wantsP3 = displaySettings.colorSpace.startsWith('display-p3'); colorFolder.name = wantsP3 && !p3MediaQuery.matches ? "Color settings ⚠️ Display isn't wide gamut" @@ -362,7 +365,7 @@ function updateColorSpaceName() { p3MediaQuery.onchange = updateColorSpaceName; const hdrFolder = gui.addFolder('HDR settings'); hdrFolder - .add(simulationParams, 'toneMappingMode', ['standard', 'extended']) + .add(displaySettings, 'toneMappingMode', ['standard', 'extended']) .onChange(configureContext); hdrFolder.add(simulationParams, 'brightnessFactor', 0, 4, 0.1); hdrFolder.open(); @@ -375,7 +378,7 @@ function getHdrFolderName() { return 'HDR settings'; } if ( - simulationParams.toneMappingMode === 'extended' && + displaySettings.toneMappingMode === 'extended' && context.getConfiguration().toneMapping?.mode !== 'extended' ) { return "HDR settings ⚠️ Browser doesn't support HDR canvas"; From a3a0108560ebca65482abf9db023213a413ef28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaufort?= Date: Fri, 2 Oct 2026 08:17:33 +0200 Subject: [PATCH 4/6] Show unsupported color spaces as disabled options in particles sample --- sample/particles/main.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sample/particles/main.ts b/sample/particles/main.ts index ed6c7558..dc8330bc 100644 --- a/sample/particles/main.ts +++ b/sample/particles/main.ts @@ -341,18 +341,23 @@ gui.width = 325; gui.add(simulationParams, 'simulate'); gui.add(simulationParams, 'deltaTime'); const colorFolder = gui.addFolder('Color settings'); -colorFolder - .add( - displaySettings, - 'colorSpace', - ['srgb', 'srgb-linear', 'display-p3', 'display-p3-linear'].filter( - isColorSpaceSupported - ) - ) +const colorSpaceController = colorFolder + .add(displaySettings, 'colorSpace', [ + 'srgb', + 'srgb-linear', + 'display-p3', + 'display-p3-linear', + ]) .onChange(() => { configureContext(); copyImageToTexture(); }); +// Disable color spaces the browser doesn't support. +for (const option of colorSpaceController.domElement.querySelectorAll( + 'option' +)) { + option.disabled = !isColorSpaceSupported(option.value); +} colorFolder.open(); const p3MediaQuery = window.matchMedia('(color-gamut: p3)'); function updateColorSpaceName() { From 5420fbd4cad733bfbd0e76018b3cd19d5e7ca93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaufort?= Date: Fri, 2 Oct 2026 08:20:02 +0200 Subject: [PATCH 5/6] Rename texture to logoTexture and copyImageToTexture to reinitLogoTexture in particles sample --- sample/particles/main.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sample/particles/main.ts b/sample/particles/main.ts index dc8330bc..345ca938 100644 --- a/sample/particles/main.ts +++ b/sample/particles/main.ts @@ -193,7 +193,7 @@ assert(isPowerOf2(imageBitmap.width), 'image must be a power of 2'); // Calculate number of mip levels required to generate the probability map const mipLevelCount = (Math.log2(Math.max(imageBitmap.width, imageBitmap.height)) + 1) | 0; -const texture = device.createTexture({ +const logoTexture = device.createTexture({ size: [imageBitmap.width, imageBitmap.height, 1], mipLevelCount, format: 'rgba16float', @@ -206,22 +206,22 @@ const texture = device.createTexture({ // Copies the image into mip level 0, converting its colors to the current // color space. The alpha channel (used for the probability map) is unaffected // by the color space, so the probability map doesn't need regenerating. -function copyImageToTexture() { +function reinitLogoTexture() { device.queue.copyExternalImageToTexture( { source: imageBitmap }, { - texture: texture, + texture: logoTexture, colorSpace: displaySettings.colorSpace as PredefinedColorSpace, }, [imageBitmap.width, imageBitmap.height] ); } -copyImageToTexture(); +reinitLogoTexture(); ////////////////////////////////////////////////////////////////////////////// // Probability map generation -// The 0'th mip level of texture holds the color data and spawn-probability in -// the alpha channel. The mip levels 1..N are generated to hold spawn +// The 0'th mip level of logoTexture holds the color data and spawn-probability +// in the alpha channel. The mip levels 1..N are generated to hold spawn // probabilities up to the top 1x1 mip level. ////////////////////////////////////////////////////////////////////////////// { @@ -249,7 +249,7 @@ copyImageToTexture(); usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }); const buffer_a = device.createBuffer({ - size: texture.width * texture.height * 4, + size: logoTexture.width * logoTexture.height * 4, usage: GPUBufferUsage.STORAGE, }); const buffer_b = device.createBuffer({ @@ -259,12 +259,12 @@ copyImageToTexture(); device.queue.writeBuffer( probabilityMapUBOBuffer, 0, - new Uint32Array([texture.width]) + new Uint32Array([logoTexture.width]) ); const commandEncoder = device.createCommandEncoder(); - for (let level = 0; level < texture.mipLevelCount; level++) { - const levelWidth = Math.max(1, texture.width >> level); - const levelHeight = Math.max(1, texture.height >> level); + for (let level = 0; level < logoTexture.mipLevelCount; level++) { + const levelWidth = Math.max(1, logoTexture.width >> level); + const levelHeight = Math.max(1, logoTexture.height >> level); const pipeline = level == 0 ? probabilityMapImportLevelPipeline.getBindGroupLayout(0) @@ -290,7 +290,7 @@ copyImageToTexture(); { // tex_in / tex_out binding: 3, - resource: texture.createView({ + resource: logoTexture.createView({ format: 'rgba16float', dimension: '2d', baseMipLevel: level, @@ -350,7 +350,7 @@ const colorSpaceController = colorFolder ]) .onChange(() => { configureContext(); - copyImageToTexture(); + reinitLogoTexture(); }); // Disable color spaces the browser doesn't support. for (const option of colorSpaceController.domElement.querySelectorAll( @@ -408,7 +408,7 @@ const computeBindGroup = device.createBindGroup({ entries: [ { binding: 0, resource: simulationUBOBuffer }, { binding: 1, resource: particlesBuffer }, - { binding: 2, resource: texture.createView() }, + { binding: 2, resource: logoTexture.createView() }, ], }); From 587e0b0c0ccaa287552bfee2cfff252904f19a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaufort?= Date: Mon, 5 Oct 2026 11:07:27 +0200 Subject: [PATCH 6/6] Add texture format toggle and Display P3 logo to particles sample Tag webgpu.png as Display P3 (pixel values unchanged) so its colors extend beyond sRGB, making the color space choice visible. Add a texture format option (rgba8unorm or rgba16float) for the logo texture. rgba8unorm clamps colors outside the selected color space when the image is copied, while rgba16float preserves them. Changing it recreates the texture, probability map, and compute bind group. --- public/assets/img/webgpu.png | Bin 38482 -> 32629 bytes sample/particles/main.ts | 108 ++++++++++++++++++--------- sample/particles/probabilityMap.wgsl | 4 +- 3 files changed, 76 insertions(+), 36 deletions(-) diff --git a/public/assets/img/webgpu.png b/public/assets/img/webgpu.png index a44b73ead9903e05cae19355b54ad274ed8147ea..7edddf27806e7b11a329e197ffcbf2477aa6233d 100644 GIT binary patch literal 32629 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 z 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 IdF!zM14p*&djJ3c diff --git a/sample/particles/main.ts b/sample/particles/main.ts index 345ca938..87382c4a 100644 --- a/sample/particles/main.ts +++ b/sample/particles/main.ts @@ -32,6 +32,9 @@ const presentationFormat = 'rgba16float'; const displaySettings = { colorSpace: 'srgb' as string, + // rgba8unorm clamps colors outside the color space when the image is copied, + // while rgba16float preserves them (and avoids banding in linear spaces). + textureFormat: 'rgba8unorm' as 'rgba8unorm' | 'rgba16float', toneMappingMode: 'standard' as GPUCanvasToneMappingMode, }; @@ -193,16 +196,24 @@ assert(isPowerOf2(imageBitmap.width), 'image must be a power of 2'); // Calculate number of mip levels required to generate the probability map const mipLevelCount = (Math.log2(Math.max(imageBitmap.width, imageBitmap.height)) + 1) | 0; -const logoTexture = device.createTexture({ - size: [imageBitmap.width, imageBitmap.height, 1], - mipLevelCount, - format: 'rgba16float', - usage: - GPUTextureUsage.TEXTURE_BINDING | - GPUTextureUsage.STORAGE_BINDING | - GPUTextureUsage.COPY_DST | - GPUTextureUsage.RENDER_ATTACHMENT, -}); +let logoTexture: GPUTexture; +// Creates the logo texture in the current texture format, copies the image +// into it, and generates the probability map. +function createLogoTexture() { + logoTexture?.destroy(); + logoTexture = device.createTexture({ + size: [imageBitmap.width, imageBitmap.height, 1], + mipLevelCount, + format: displaySettings.textureFormat, + usage: + GPUTextureUsage.TEXTURE_BINDING | + GPUTextureUsage.STORAGE_BINDING | + GPUTextureUsage.COPY_DST | + GPUTextureUsage.RENDER_ATTACHMENT, + }); + reinitLogoTexture(); + generateProbabilityMap(); +} // Copies the image into mip level 0, converting its colors to the current // color space. The alpha channel (used for the probability map) is unaffected // by the color space, so the probability map doesn't need regenerating. @@ -216,7 +227,6 @@ function reinitLogoTexture() { [imageBitmap.width, imageBitmap.height] ); } -reinitLogoTexture(); ////////////////////////////////////////////////////////////////////////////// // Probability map generation @@ -224,21 +234,37 @@ reinitLogoTexture(); // in the alpha channel. The mip levels 1..N are generated to hold spawn // probabilities up to the top 1x1 mip level. ////////////////////////////////////////////////////////////////////////////// -{ - const probabilityMapImportLevelPipeline = device.createComputePipeline({ - layout: 'auto', - compute: { - module: device.createShaderModule({ code: probabilityMapWGSL }), - entryPoint: 'import_level', - }, - }); - const probabilityMapExportLevelPipeline = device.createComputePipeline({ - layout: 'auto', - compute: { - module: device.createShaderModule({ code: probabilityMapWGSL }), - entryPoint: 'export_level', - }, - }); +// The storage texture format is part of the shader, so create a pair of +// pipelines for each texture format. +const probabilityMapPipelines = new Map< + GPUTextureFormat, + { importLevel: GPUComputePipeline; exportLevel: GPUComputePipeline } +>(); +function getProbabilityMapPipelines(format: GPUTextureFormat) { + let pipelines = probabilityMapPipelines.get(format); + if (!pipelines) { + const module = device.createShaderModule({ + code: probabilityMapWGSL.replace('rgba8unorm', format), + }); + pipelines = { + importLevel: device.createComputePipeline({ + layout: 'auto', + compute: { module, entryPoint: 'import_level' }, + }), + exportLevel: device.createComputePipeline({ + layout: 'auto', + compute: { module, entryPoint: 'export_level' }, + }), + }; + probabilityMapPipelines.set(format, pipelines); + } + return pipelines; +} +function generateProbabilityMap() { + const { + importLevel: probabilityMapImportLevelPipeline, + exportLevel: probabilityMapExportLevelPipeline, + } = getProbabilityMapPipelines(logoTexture.format); const probabilityMapUBOBufferSize = 1 * 4 + // stride @@ -291,7 +317,7 @@ reinitLogoTexture(); // tex_in / tex_out binding: 3, resource: logoTexture.createView({ - format: 'rgba16float', + format: logoTexture.format, dimension: '2d', baseMipLevel: level, mipLevelCount: 1, @@ -314,6 +340,9 @@ reinitLogoTexture(); } } device.queue.submit([commandEncoder.finish()]); + probabilityMapUBOBuffer.destroy(); + buffer_a.destroy(); + buffer_b.destroy(); } ////////////////////////////////////////////////////////////////////////////// @@ -358,6 +387,9 @@ for (const option of colorSpaceController.domElement.querySelectorAll( )) { option.disabled = !isColorSpaceSupported(option.value); } +colorFolder + .add(displaySettings, 'textureFormat', ['rgba8unorm', 'rgba16float']) + .onChange(initLogoTextureAndBindGroup); colorFolder.open(); const p3MediaQuery = window.matchMedia('(color-gamut: p3)'); function updateColorSpaceName() { @@ -403,14 +435,20 @@ const computePipeline = device.createComputePipeline({ entryPoint: 'simulate', }, }); -const computeBindGroup = device.createBindGroup({ - layout: computePipeline.getBindGroupLayout(0), - entries: [ - { binding: 0, resource: simulationUBOBuffer }, - { binding: 1, resource: particlesBuffer }, - { binding: 2, resource: logoTexture.createView() }, - ], -}); +let computeBindGroup: GPUBindGroup; +// (Re)creates the logo texture and the compute bind group that samples it. +function initLogoTextureAndBindGroup() { + createLogoTexture(); + computeBindGroup = device.createBindGroup({ + layout: computePipeline.getBindGroupLayout(0), + entries: [ + { binding: 0, resource: simulationUBOBuffer }, + { binding: 1, resource: particlesBuffer }, + { binding: 2, resource: logoTexture.createView() }, + ], + }); +} +initLogoTextureAndBindGroup(); const aspect = canvas.width / canvas.height; const projection = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 100.0); diff --git a/sample/particles/probabilityMap.wgsl b/sample/particles/probabilityMap.wgsl index 0016a539..83db8421 100644 --- a/sample/particles/probabilityMap.wgsl +++ b/sample/particles/probabilityMap.wgsl @@ -6,7 +6,9 @@ struct UBO { @binding(1) @group(0) var buf_in : array; @binding(2) @group(0) var buf_out : array; @binding(3) @group(0) var tex_in : texture_2d; -@binding(3) @group(0) var tex_out : texture_storage_2d; +// The storage format is replaced at runtime in main.ts to match the +// selected texture format. +@binding(3) @group(0) var tex_out : texture_storage_2d; //////////////////////////////////////////////////////////////////////////////// // import_level