From a7bca6321b6f34fd616fa800f3fd6911c6836864 Mon Sep 17 00:00:00 2001 From: Sander Date: Tue, 23 Jul 2024 04:22:24 +0400 Subject: [PATCH] flux: use nearest sampling to stabilize fluid for short timesteps --- flux/shader/adjust_advection.comp.wgsl | 8 ++++---- flux/shader/advect.comp.wgsl | 2 +- flux/shader/diffuse.comp.wgsl | 9 +++++---- flux/shader/divergence.comp.wgsl | 10 +++++----- flux/shader/generate_noise.comp.wgsl | 2 +- flux/shader/solve_pressure.comp.wgsl | 9 +++++---- flux/shader/subtract_gradient.comp.wgsl | 9 +++++---- flux/src/render/fluid.rs | 25 ++++++++++++++++++++++--- 8 files changed, 48 insertions(+), 26 deletions(-) diff --git a/flux/shader/adjust_advection.comp.wgsl b/flux/shader/adjust_advection.comp.wgsl index 18b638e..9360dcb 100644 --- a/flux/shader/adjust_advection.comp.wgsl +++ b/flux/shader/adjust_advection.comp.wgsl @@ -25,11 +25,11 @@ fn main( let velocity = textureLoad(velocity_texture, global_id.xy, 0).xy; let size = vec2(textureDimensions(velocity_texture)); - let sample_position = vec2(global_id.xy); + let advected_position = (vec2(global_id.xy) + 1.0) - uniforms.timestep * velocity; // NOTE: Using floor here produces very different results from the GL version. - let pos2 = (vec2f(floor(sample_position.x + 1.0), floor(sample_position.y + 1.0))); - let adv = floor(sample_position + 1.0) - uniforms.timestep * velocity; - let min_max_sampling_position = (0.5 + (adv)) / size; + // Using floor individually on each dimensions also seems to produce different results! + // floor produces a distinct diagonal bias, moving fluid in waves to/from the upper right corner. + let min_max_sampling_position = (0.5 + round(advected_position)) / size; let l = textureSampleLevel(velocity_texture, linear_sampler, min_max_sampling_position, 0.0, vec2(-1, 0)).xy; let r = textureSampleLevel(velocity_texture, linear_sampler, min_max_sampling_position, 0.0, vec2(1, 0)).xy; let b = textureSampleLevel(velocity_texture, linear_sampler, min_max_sampling_position, 0.0, vec2(0, -1)).xy; diff --git a/flux/shader/advect.comp.wgsl b/flux/shader/advect.comp.wgsl index 2f60054..8db6f74 100644 --- a/flux/shader/advect.comp.wgsl +++ b/flux/shader/advect.comp.wgsl @@ -35,7 +35,7 @@ fn main( let size = vec2(textureDimensions(velocity_texture)); let sample_position = vec2(global_id.xy); - let advected_position = ((sample_position + 0.5) - direction.direction * uniforms.timestep * velocity) / size; + let advected_position = ((sample_position + 0.5) + direction.direction * uniforms.timestep * velocity) / size; let decay = 1.0 + uniforms.dissipation * uniforms.timestep; let new_velocity = textureSampleLevel(velocity_texture, linear_sampler, advected_position, 0.0).xy / decay; textureStore(out_texture, global_id.xy, vec4(new_velocity, 0.0, 0.0)); diff --git a/flux/shader/diffuse.comp.wgsl b/flux/shader/diffuse.comp.wgsl index 3582d64..353f7e1 100644 --- a/flux/shader/diffuse.comp.wgsl +++ b/flux/shader/diffuse.comp.wgsl @@ -10,6 +10,7 @@ struct FluidUniforms { @group(0) @binding(0) var uniforms: FluidUniforms; @group(0) @binding(1) var linear_sampler: sampler; +@group(0) @binding(1) var nearest_sampler: sampler; @group(1) @binding(0) var velocity_texture: texture_2d; @group(1) @binding(1) var out_texture: texture_storage_2d; @@ -23,10 +24,10 @@ fn main( let size = textureDimensions(velocity_texture, 0); let sample_position = (vec2(global_id.xy) + 0.0) / vec2(size); - let l = textureSampleLevel(velocity_texture, linear_sampler, sample_position, 0.0, vec2(-1, 0)).xy; - let r = textureSampleLevel(velocity_texture, linear_sampler, sample_position, 0.0, vec2(1, 0)).xy; - let b = textureSampleLevel(velocity_texture, linear_sampler, sample_position, 0.0, vec2(0, -1)).xy; - let t = textureSampleLevel(velocity_texture, linear_sampler, sample_position, 0.0, vec2(0, 1)).xy; + let l = textureSampleLevel(velocity_texture, nearest_sampler, sample_position, 0.0, vec2(-1, 0)).xy; + let r = textureSampleLevel(velocity_texture, nearest_sampler, sample_position, 0.0, vec2(1, 0)).xy; + let b = textureSampleLevel(velocity_texture, nearest_sampler, sample_position, 0.0, vec2(0, -1)).xy; + let t = textureSampleLevel(velocity_texture, nearest_sampler, sample_position, 0.0, vec2(0, 1)).xy; let new_velocity = uniforms.stencil_factor * (l + r + b + t + uniforms.center_factor * velocity); diff --git a/flux/shader/divergence.comp.wgsl b/flux/shader/divergence.comp.wgsl index 8fda728..dcb9ef3 100644 --- a/flux/shader/divergence.comp.wgsl +++ b/flux/shader/divergence.comp.wgsl @@ -1,4 +1,4 @@ -@group(0) @binding(0) var linear_sampler: sampler; +@group(0) @binding(0) var nearest_sampler: sampler; @group(0) @binding(1) var out_divergence_texture: texture_storage_2d; @group(1) @binding(0) var velocity_texture: texture_2d; @@ -12,10 +12,10 @@ fn main( let size = (textureDimensions(velocity_texture)); let sample_position = (vec2(global_id.xy) + 0.0) / vec2(size); - let l = textureSampleLevel(velocity_texture, linear_sampler, sample_position, 0.0, vec2(-1, 0)).x; - let r = textureSampleLevel(velocity_texture, linear_sampler, sample_position, 0.0, vec2(1, 0)).x; - let t = textureSampleLevel(velocity_texture, linear_sampler, sample_position, 0.0, vec2(0, 1)).y; - let b = textureSampleLevel(velocity_texture, linear_sampler, sample_position, 0.0, vec2(0, -1)).y; + let l = textureSampleLevel(velocity_texture, nearest_sampler, sample_position, 0.0, vec2(-1, 0)).x; + let r = textureSampleLevel(velocity_texture, nearest_sampler, sample_position, 0.0, vec2(1, 0)).x; + let t = textureSampleLevel(velocity_texture, nearest_sampler, sample_position, 0.0, vec2(0, 1)).y; + let b = textureSampleLevel(velocity_texture, nearest_sampler, sample_position, 0.0, vec2(0, -1)).y; let new_divergence = 0.5 * ((r - l) + (t - b)); diff --git a/flux/shader/generate_noise.comp.wgsl b/flux/shader/generate_noise.comp.wgsl index f881799..c72b924 100644 --- a/flux/shader/generate_noise.comp.wgsl +++ b/flux/shader/generate_noise.comp.wgsl @@ -114,7 +114,7 @@ fn main( ) { let size = vec2(textureDimensions(out_texture)); let texel_position = vec2(global_id.xy) / size; - + var noise = vec2(0.0); for (var i = 0; i < 3; i = i + 1) { let channel = global.channels[i]; diff --git a/flux/shader/solve_pressure.comp.wgsl b/flux/shader/solve_pressure.comp.wgsl index bb42ac1..e4ac827 100644 --- a/flux/shader/solve_pressure.comp.wgsl +++ b/flux/shader/solve_pressure.comp.wgsl @@ -10,6 +10,7 @@ struct FluidUniforms { @group(0) @binding(0) var uniforms: FluidUniforms; @group(0) @binding(1) var linear_sampler: sampler; +@group(0) @binding(2) var nearest_sampler: sampler; @group(1) @binding(0) var divergence_texture: texture_2d; @@ -27,10 +28,10 @@ fn main( let pressure = textureLoad(pressure_texture, global_id.xy, 0).x; let divergence = textureLoad(divergence_texture, global_id.xy, 0).x; - var l = textureSampleLevel(pressure_texture, linear_sampler, sample_position, 0.0, vec2(-1, 0)).x; - var r = textureSampleLevel(pressure_texture, linear_sampler, sample_position, 0.0, vec2(1, 0)).x; - var b = textureSampleLevel(pressure_texture, linear_sampler, sample_position, 0.0, vec2(0, -1)).x; - var t = textureSampleLevel(pressure_texture, linear_sampler, sample_position, 0.0, vec2(0, 1)).x; + var l = textureSampleLevel(pressure_texture, nearest_sampler, sample_position, 0.0, vec2(-1, 0)).x; + var r = textureSampleLevel(pressure_texture, nearest_sampler, sample_position, 0.0, vec2(1, 0)).x; + var b = textureSampleLevel(pressure_texture, nearest_sampler, sample_position, 0.0, vec2(0, -1)).x; + var t = textureSampleLevel(pressure_texture, nearest_sampler, sample_position, 0.0, vec2(0, 1)).x; if (global_id.x == 0u) { l = pressure; diff --git a/flux/shader/subtract_gradient.comp.wgsl b/flux/shader/subtract_gradient.comp.wgsl index 2250182..3dc6934 100644 --- a/flux/shader/subtract_gradient.comp.wgsl +++ b/flux/shader/subtract_gradient.comp.wgsl @@ -10,6 +10,7 @@ struct FluidUniforms { @group(0) @binding(0) var uniforms: FluidUniforms; @group(0) @binding(1) var linear_sampler: sampler; +@group(0) @binding(2) var nearest_sampler: sampler; @group(1) @binding(0) var pressure_texture: texture_2d; @group(1) @binding(1) var out_pressure_texture: texture_storage_2d; @@ -27,10 +28,10 @@ fn main( let pressure = textureLoad(pressure_texture, global_id.xy, 0).x; - var l = textureSampleLevel(pressure_texture, linear_sampler, sample_position, 0.0, vec2(-1, 0)).x; - var r = textureSampleLevel(pressure_texture, linear_sampler, sample_position, 0.0, vec2(1, 0)).x; - var b = textureSampleLevel(pressure_texture, linear_sampler, sample_position, 0.0, vec2(0, -1)).x; - var t = textureSampleLevel(pressure_texture, linear_sampler, sample_position, 0.0, vec2(0, 1)).x; + var l = textureSampleLevel(pressure_texture, nearest_sampler, sample_position, 0.0, vec2(-1, 0)).x; + var r = textureSampleLevel(pressure_texture, nearest_sampler, sample_position, 0.0, vec2(1, 0)).x; + var b = textureSampleLevel(pressure_texture, nearest_sampler, sample_position, 0.0, vec2(0, -1)).x; + var t = textureSampleLevel(pressure_texture, nearest_sampler, sample_position, 0.0, vec2(0, 1)).x; // Enforce the following boundary conditions: // diff --git a/flux/src/render/fluid.rs b/flux/src/render/fluid.rs index 2173f03..5a4cfa7 100644 --- a/flux/src/render/fluid.rs +++ b/flux/src/render/fluid.rs @@ -297,7 +297,15 @@ impl Context { label: Some("sampler:linear"), mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + ..Default::default() + }); + + let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("sampler:nearest"), + mag_filter: wgpu::FilterMode::Nearest, + min_filter: wgpu::FilterMode::Nearest, address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, ..Default::default() @@ -387,6 +395,13 @@ impl Context { ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: None, }, + // nearest_sampler + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, ], }); @@ -439,6 +454,10 @@ impl Context { binding: 1, resource: wgpu::BindingResource::Sampler(&linear_sampler), }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&nearest_sampler), + }, ], }); @@ -659,7 +678,7 @@ impl Context { entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Sampler(&linear_sampler), + resource: wgpu::BindingResource::Sampler(&nearest_sampler), }, wgpu::BindGroupEntry { binding: 1, @@ -671,7 +690,7 @@ impl Context { let divergence_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("pipeline_layout:divergence"), - bind_group_layouts: &[&&divergence_bind_group_layout, &velocity_bind_group_layout], + bind_group_layouts: &[&divergence_bind_group_layout, &velocity_bind_group_layout], push_constant_ranges: &[], });