From 69b660ac5afff188b73eb5ae7ee4b8ff4f4b5a79 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Sat, 27 Jun 2020 23:23:54 +0200 Subject: [PATCH] Thickness & depth target for screen space fluid rendering still work in progress. Unclear how depth target should be handled exactly (what kind of depth, how to clear, etc.) --- Cargo.toml | 7 +- shader/screenspace_fluid/compose.comp | 20 +++ shader/screenspace_fluid/final_compose.comp | 14 -- shader/screenspace_fluid/particles.frag | 33 ++++ shader/screenspace_fluid/particles.vert | 19 ++ shader/utilities.glsl | 13 ++ src/renderer/scene_renderer.rs | 6 +- src/renderer/screenspace_fluid.rs | 186 +++++++++++++++----- 8 files changed, 237 insertions(+), 61 deletions(-) create mode 100644 shader/screenspace_fluid/compose.comp delete mode 100644 shader/screenspace_fluid/final_compose.comp create mode 100644 shader/screenspace_fluid/particles.frag create mode 100644 shader/screenspace_fluid/particles.vert diff --git a/Cargo.toml b/Cargo.toml index 6fa9d5f..a055388 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,4 +28,9 @@ strum_macros = "0.18" strum = "0.18" bytemuck = "1.2" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" \ No newline at end of file +serde_json = "1.0" + +# todo: Get independentBlending upstream +[patch."https://github.com/gfx-rs/wgpu"] +wgpu-types = { version = "0.5.0", path = "../wgpu/wgpu-types" } +wgpu-core = { version = "0.5.0", path = "../wgpu/wgpu-core" } \ No newline at end of file diff --git a/shader/screenspace_fluid/compose.comp b/shader/screenspace_fluid/compose.comp new file mode 100644 index 0000000..978a421 --- /dev/null +++ b/shader/screenspace_fluid/compose.comp @@ -0,0 +1,20 @@ +#version 460 + +#include "../fluid_render_info.glsl" +#include "../per_frame_resources.glsl" +#include "../utilities.glsl" + +layout(set = 2, binding = 0) uniform texture2D ParticleDepth; +layout(set = 2, binding = 1) uniform texture2D ParticleThickness; +layout(set = 2, binding = 2, HDR_BACKBUFFER_IMAGE_FORMAT) uniform restrict image2D Backbuffer; + +layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; + +void main() { + float particleDepth = texelFetch(ParticleDepth, ivec2(gl_GlobalInvocationID.xy), 0).r; + float particleThickness = texelFetch(ParticleThickness, ivec2(gl_GlobalInvocationID.xy), 0).r; + vec4 previousColor = imageLoad(Backbuffer, ivec2(gl_GlobalInvocationID.xy)); + + vec4 outputColor = vec4(particleThickness * 0.01); + imageStore(Backbuffer, ivec2(gl_GlobalInvocationID.xy), outputColor); +} \ No newline at end of file diff --git a/shader/screenspace_fluid/final_compose.comp b/shader/screenspace_fluid/final_compose.comp deleted file mode 100644 index b028dd3..0000000 --- a/shader/screenspace_fluid/final_compose.comp +++ /dev/null @@ -1,14 +0,0 @@ -#version 460 - -#include "../fluid_render_info.glsl" -#include "../per_frame_resources.glsl" -#include "../utilities.glsl" - -layout(set = 2, binding = 0, HDR_BACKBUFFER_IMAGE_FORMAT) uniform restrict image2D Backbuffer; - -layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; - -void main() { - vec4 previousColor = imageLoad(Backbuffer, ivec2(gl_GlobalInvocationID.xy)); - imageStore(Backbuffer, ivec2(gl_GlobalInvocationID.xy), previousColor * vec4(1, 1, 1, 1)); -} \ No newline at end of file diff --git a/shader/screenspace_fluid/particles.frag b/shader/screenspace_fluid/particles.frag new file mode 100644 index 0000000..439049a --- /dev/null +++ b/shader/screenspace_fluid/particles.frag @@ -0,0 +1,33 @@ +#version 460 + +#include "../per_frame_resources.glsl" +#include "../utilities.glsl" + +layout(location = 0) in vec3 in_WorldPosition; +layout(location = 1) in vec3 in_ParticleWorldPosition; +layout(location = 2) in float in_Radius; +layout(location = 0) out float out_Depth; +layout(location = 1) out float out_Thickness; + +// Note that we promise to only lessen the depth value, so gpu can still do some hi-z/early depth culling +layout(depth_less) out float gl_FragDepth; + +void main() { + vec3 rayDir = normalize(in_WorldPosition - Camera.Position); + float cameraDistance; + // TODO: Elipsoids using APIC matrix? + if (!sphereIntersect(in_ParticleWorldPosition, in_Radius, Camera.Position, rayDir, cameraDistance)) + discard; + + vec3 sphereWorldPos = Camera.Position + cameraDistance * rayDir; + vec3 normal = (sphereWorldPos - in_ParticleWorldPosition) / in_Radius; + + // Adjust depth buffer value. + // TODO: Necessary for this step? + vec2 projected_zw = (Camera.ViewProjection * vec4(sphereWorldPos, 1.0)).zw; // (trusting optimizer to pick the right thing ;-)) + gl_FragDepth = projected_zw.x / projected_zw.y; + + // TODO: What kind of depth + out_Depth = cameraDistance; + out_Thickness = in_Radius; +} diff --git a/shader/screenspace_fluid/particles.vert b/shader/screenspace_fluid/particles.vert new file mode 100644 index 0000000..e34b7a4 --- /dev/null +++ b/shader/screenspace_fluid/particles.vert @@ -0,0 +1,19 @@ +#version 450 + +#include "fluid_render_info.glsl" +#include "per_frame_resources.glsl" +#include "sphere_particles.glsl" +#include "utilities.glsl" + +out gl_PerVertex { vec4 gl_Position; }; + +layout(location = 0) out vec3 out_WorldPosition; +layout(location = 1) out vec3 out_ParticleWorldPosition; +layout(location = 2) out float out_Radius; + +void main() { + out_Radius = 0.25 * Rendering.FluidGridToWorldScale; + out_ParticleWorldPosition = Particles[gl_InstanceIndex].Position * Rendering.FluidGridToWorldScale + Rendering.FluidWorldOrigin; + out_WorldPosition = spanParticle(out_ParticleWorldPosition, out_Radius); + gl_Position = Camera.ViewProjection * vec4(out_WorldPosition, 1.0); +} diff --git a/shader/utilities.glsl b/shader/utilities.glsl index f200568..a54384a 100644 --- a/shader/utilities.glsl +++ b/shader/utilities.glsl @@ -30,4 +30,17 @@ vec3 colormapHeat(float t) { return saturate(vec3(t * 3, t * 3 - 1, t * 3 - 2)); // t = [-1; 1] vec3 colormapCoolToWarm(float t) { return t < 0.0 ? mix(vec3(1.0), vec3(0.0, 0.0, 1.0), -t) : mix(vec3(1.0), vec3(1.0, 0.0, 0.0), t); } +bool sphereIntersect(vec3 spherePosition, float radius, vec3 rayOrigin, vec3 rayDir, out float sphereDistance) { + // Sphere intersect raycast. + // (uses equation based intersect: rayOrigin + t * rayDir, ||sphereOrigin-pointOnSphere||= r*r, [...]) + vec3 particleCenterToCamera = rayOrigin - spherePosition; // (often denoted as oc == OriginCenter) + float b = dot(particleCenterToCamera, rayDir); + float c = dot(particleCenterToCamera, particleCenterToCamera) - radius * radius; + float discr = b * b - c; + if (discr < 0.0) + return false; + sphereDistance = -b - sqrt(discr); + return true; +} + #endif // INCLUDE_UTILITIES \ No newline at end of file diff --git a/src/renderer/scene_renderer.rs b/src/renderer/scene_renderer.rs index d5bfbdb..5144de2 100644 --- a/src/renderer/scene_renderer.rs +++ b/src/renderer/scene_renderer.rs @@ -142,7 +142,7 @@ impl SceneRenderer { encoder: &mut wgpu::CommandEncoder, pipeline_manager: &PipelineManager, backbuffer: &wgpu::TextureView, - depth: &wgpu::TextureView, + depthbuffer: &wgpu::TextureView, per_frame_bind_group: &wgpu::BindGroup, ) { // Opaque @@ -162,7 +162,7 @@ impl SceneRenderer { }, }], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { - attachment: depth, + attachment: depthbuffer, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.0), store: true, @@ -204,7 +204,7 @@ impl SceneRenderer { { if let FluidRenderingMode::ScreenSpaceFluid = self.fluid_rendering_mode { self.screenspace_fluid - .draw(encoder, pipeline_manager, per_frame_bind_group, &scene.fluid()); + .draw(encoder, pipeline_manager, depthbuffer, per_frame_bind_group, &scene.fluid()); } } } diff --git a/src/renderer/screenspace_fluid.rs b/src/renderer/screenspace_fluid.rs index 4d2d798..7935e5e 100644 --- a/src/renderer/screenspace_fluid.rs +++ b/src/renderer/screenspace_fluid.rs @@ -1,26 +1,27 @@ use crate::hybrid_fluid::*; +use crate::render_output::hdr_backbuffer::HdrBackbuffer; +use crate::render_output::screen::Screen; use crate::wgpu_utils::pipelines::*; -use crate::{ - render_output::hdr_backbuffer::HdrBackbuffer, - wgpu_utils::{ - self, - binding_builder::{BindGroupBuilder, BindGroupLayoutBuilder, BindGroupLayoutWithDesc}, - binding_glsl, - shader::*, - }, +use crate::wgpu_utils::{ + self, + binding_builder::{BindGroupBuilder, BindGroupLayoutBuilder, BindGroupLayoutWithDesc}, + binding_glsl, + shader::*, }; -use std::{path::Path, rc::Rc}; +use std::path::{Path, PathBuf}; +use std::rc::Rc; struct ScreenDependentProperties { - texture_fluid_thickness: wgpu::Texture, - texture_fluid_depth: wgpu::Texture, - bind_group_final_compose: wgpu::BindGroup, + texture_view_fluid_depth: wgpu::TextureView, + texture_view_fluid_thickness: wgpu::TextureView, + bind_group_compose: wgpu::BindGroup, target_textures_resolution: wgpu::Extent3d, } struct ScreenIndependentProperties { - pipeline_final_compose: ComputePipelineHandle, - group_layout_final_compose: BindGroupLayoutWithDesc, + pipeline_compose: ComputePipelineHandle, + pipeline_render_particles: RenderPipelineHandle, + group_layout_compose: BindGroupLayoutWithDesc, } pub struct ScreenSpaceFluid { @@ -29,6 +30,9 @@ pub struct ScreenSpaceFluid { } impl ScreenSpaceFluid { + const FORMAT_FLUID_DEPTH: wgpu::TextureFormat = wgpu::TextureFormat::R32Float; // TODO: Smaller? + const FORMAT_FLUID_THICKNESS: wgpu::TextureFormat = wgpu::TextureFormat::R16Float; // TODO: Smaller? + pub fn new( device: &wgpu::Device, shader_dir: &ShaderDirectory, @@ -37,28 +41,79 @@ impl ScreenSpaceFluid { fluid_renderer_group_layout: &wgpu::BindGroupLayout, backbuffer: &HdrBackbuffer, ) -> ScreenSpaceFluid { - let group_layout_final_compose = BindGroupLayoutBuilder::new() - .next_binding_compute(binding_glsl::image2d(HdrBackbuffer::FORMAT, false)) + let group_layout_compose = BindGroupLayoutBuilder::new() + .next_binding_compute(binding_glsl::texture2D()) // Fluid depth + .next_binding_compute(binding_glsl::texture2D()) // Fluid thickness + .next_binding_compute(binding_glsl::image2d(HdrBackbuffer::FORMAT, false)) // hdr backbuffer .create(device, "BindGroupLayout: SSFluid, Final Compose"); - let pipeline_final_compose = pipeline_manager.create_compute_pipeline( + let pipeline_render_particles = pipeline_manager.create_render_pipeline( + device, + shader_dir, + RenderPipelineCreationDesc { + layout: Rc::new(device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&per_frame_bind_group_layout, &fluid_renderer_group_layout], + })), + vertex_shader_relative_path: PathBuf::from("screenspace_fluid/particles.vert"), + fragment_shader_relative_path: Some(PathBuf::from("screenspace_fluid/particles.frag")), + rasterization_state: Some(rasterization_state::culling_none()), + primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, + color_states: vec![ + wgpu::ColorStateDescriptor { + format: Self::FORMAT_FLUID_DEPTH, + color_blend: wgpu::BlendDescriptor { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Min, + }, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + wgpu::ColorStateDescriptor { + format: Self::FORMAT_FLUID_THICKNESS, + color_blend: wgpu::BlendDescriptor { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Add, + }, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { + format: Screen::FORMAT_DEPTH, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::LessEqual, + stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_read_mask: 0, + stencil_write_mask: 0, + }), + vertex_state: wgpu::VertexStateDescriptor { + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + }, + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }, + ); + + let pipeline_compose = pipeline_manager.create_compute_pipeline( device, shader_dir, ComputePipelineCreationDesc::new( Rc::new(device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - bind_group_layouts: &[ - &per_frame_bind_group_layout, - &fluid_renderer_group_layout, - &group_layout_final_compose.layout, - ], + bind_group_layouts: &[&per_frame_bind_group_layout, &fluid_renderer_group_layout, &group_layout_compose.layout], })), - Path::new("screenspace_fluid/final_compose.comp"), + Path::new("screenspace_fluid/compose.comp"), ), ); let screen_independent = ScreenIndependentProperties { - group_layout_final_compose, - pipeline_final_compose, + group_layout_compose, + pipeline_compose, + pipeline_render_particles, }; let screen_dependent = Self::create_screen_dependent_properties(&screen_independent, device, backbuffer); @@ -79,34 +134,39 @@ impl ScreenSpaceFluid { height: backbuffer.resolution().height, depth: 1, }; - let texture_fluid_thickness = device.create_texture(&wgpu::TextureDescriptor { - label: Some("Texture: Fluid Thickness"), + let texture_fluid_depth = device.create_texture(&wgpu::TextureDescriptor { + label: Some("Texture: Fluid Depth"), size: target_textures_resolution, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::R16Float, // R8UNorm might do as well? + format: Self::FORMAT_FLUID_DEPTH, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED, }); - let texture_fluid_depth = device.create_texture(&wgpu::TextureDescriptor { - label: Some("Texture: Fluid Depth"), + let texture_view_fluid_depth = texture_fluid_depth.create_default_view(); + + let texture_fluid_thickness = device.create_texture(&wgpu::TextureDescriptor { + label: Some("Texture: Fluid Thickness"), size: target_textures_resolution, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Depth32Float, + format: Self::FORMAT_FLUID_THICKNESS, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED, }); + let texture_view_fluid_thickness = texture_fluid_thickness.create_default_view(); - let bind_group_final_compose = BindGroupBuilder::new(&screen_independent.group_layout_final_compose) + let bind_group_compose = BindGroupBuilder::new(&screen_independent.group_layout_compose) + .texture(&texture_view_fluid_depth) + .texture(&texture_view_fluid_thickness) .texture(&backbuffer.texture_view()) .create(device, "BindGroup: SSFluid, Final Compose"); ScreenDependentProperties { - texture_fluid_thickness, - texture_fluid_depth, + texture_view_fluid_depth, + texture_view_fluid_thickness, target_textures_resolution, - bind_group_final_compose, + bind_group_compose, } } @@ -118,23 +178,63 @@ impl ScreenSpaceFluid { &'a self, encoder: &mut wgpu::CommandEncoder, pipeline_manager: &'a PipelineManager, + depthbuffer: &wgpu::TextureView, per_frame_bind_group: &wgpu::BindGroup, fluid: &HybridFluid, ) { - const COMPUTE_LOCAL_SIZE_FINAL_COMPOSE: wgpu::Extent3d = wgpu::Extent3d { - width: 32, - height: 32, - depth: 1, - }; - let compose_work_group = wgpu_utils::compute_group_size(self.screen_dependent.target_textures_resolution, COMPUTE_LOCAL_SIZE_FINAL_COMPOSE); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &self.screen_dependent.texture_view_fluid_depth, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::WHITE), + store: true, + }, + }, + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &self.screen_dependent.texture_view_fluid_thickness, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + store: true, + }, + }, + ], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { + attachment: depthbuffer, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: false, + }), + stencil_ops: None, + }), + }); + rpass.push_debug_group("screen space fluid, particles"); + + rpass.set_bind_group(0, &per_frame_bind_group, &[]); + rpass.set_bind_group(1, fluid.bind_group_renderer(), &[]); + rpass.set_pipeline(pipeline_manager.get_render(&self.screen_independent.pipeline_render_particles)); + rpass.draw(0..4, 0..fluid.num_particles()); + + rpass.pop_debug_group(); + } { + const COMPUTE_LOCAL_SIZE_COMPOSE: wgpu::Extent3d = wgpu::Extent3d { + width: 32, + height: 32, + depth: 1, + }; + let compose_work_group = wgpu_utils::compute_group_size(self.screen_dependent.target_textures_resolution, COMPUTE_LOCAL_SIZE_COMPOSE); + let mut cpass = encoder.begin_compute_pass(); cpass.set_bind_group(0, &per_frame_bind_group, &[]); cpass.set_bind_group(1, fluid.bind_group_renderer(), &[]); - cpass.set_bind_group(2, &self.screen_dependent.bind_group_final_compose, &[]); + cpass.set_bind_group(2, &self.screen_dependent.bind_group_compose, &[]); - cpass.set_pipeline(pipeline_manager.get_compute(&self.screen_independent.pipeline_final_compose)); + cpass.set_pipeline(pipeline_manager.get_compute(&self.screen_independent.pipeline_compose)); cpass.dispatch(compose_work_group.width, compose_work_group.height, compose_work_group.depth); } }