-
Notifications
You must be signed in to change notification settings - Fork 357
Add color space selection to particles sample #574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,13 +30,23 @@ 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, | ||
| colorSpace: simulationParams.colorSpace as PredefinedColorSpace, | ||
| format: presentationFormat, | ||
| toneMapping: { mode: simulationParams.toneMappingMode }, | ||
| }); | ||
| hdrFolder.name = getHdrFolderName(); | ||
| updateColorSpaceName(); | ||
| } | ||
|
|
||
| const particlesBuffer = device.createBuffer({ | ||
|
|
@@ -189,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, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't doing this mean that the colorSpace option will never make a visual difference? webgpu.png is sRGB, so if we color-manage it correctly, it's always going to stay in sRGB. We should use an image that is Display-P3 or wider (something like https://webkit.org/blog-files/color-gamut/Webkit-logo-P3.png from https://webkit.org/blog-files/color-gamut/) so we can see a difference. But actually, there should* still be no difference because both the texture and the canvas are float formats so they don't get clamped. Would need to change the texture back to unorm. * I tried hacking in the webkit image (keeping the float format) and it does make a visible difference so I'm not sure if I'm missing something or there is a chrome bug. |
||
| }, | ||
| [imageBitmap.width, imageBitmap.height] | ||
| ); | ||
| } | ||
| copyImageToTexture(); | ||
|
|
||
| ////////////////////////////////////////////////////////////////////////////// | ||
| // Probability map generation | ||
|
|
@@ -275,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, | ||
|
|
@@ -303,13 +322,6 @@ device.queue.copyExternalImageToTexture( | |
| ////////////////////////////////////////////////////////////////////////////// | ||
| // Simulation compute pipeline | ||
| ////////////////////////////////////////////////////////////////////////////// | ||
| const simulationParams = { | ||
| simulate: true, | ||
| deltaTime: 0.04, | ||
| toneMappingMode: 'standard' as GPUCanvasToneMappingMode, | ||
| brightnessFactor: 1.0, | ||
| }; | ||
|
|
||
| const simulationUBOBufferSize = | ||
| 1 * 4 + // deltaTime | ||
| 1 * 4 + // brightnessFactor | ||
|
|
@@ -325,7 +337,30 @@ 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, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's also split colorSpace and toneMappingMode into a separate settings object, they are not "simulation params". |
||
| 'colorSpace', | ||
| ['srgb', 'srgb-linear', 'display-p3', 'display-p3-linear'].filter( | ||
| isColorSpaceSupported | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we show all options even if they're disabled but somehow mark them as not available (and blank the screen when they're selected or something like that) |
||
| ) | ||
| ) | ||
| .onChange(() => { | ||
| configureContext(); | ||
| copyImageToTexture(); | ||
| }); | ||
| 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 +481,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(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Give this a more descriptive name like "reinitLogoTexture" rather than describing what it does.
Also rename
texturetologoTexture