diff --git a/crates/renderling/shaders/ibl-diffuse_irradiance-di_convolution_fragment.spv b/crates/renderling/shaders/ibl-diffuse_irradiance-di_convolution_fragment.spv new file mode 100644 index 00000000..f3d4b5e0 Binary files /dev/null and b/crates/renderling/shaders/ibl-diffuse_irradiance-di_convolution_fragment.spv differ diff --git a/crates/renderling/shaders/ibl-diffuse_irradiance-di_convolution_fragment.wgsl b/crates/renderling/shaders/ibl-diffuse_irradiance-di_convolution_fragment.wgsl new file mode 100644 index 00000000..52ca6527 --- /dev/null +++ b/crates/renderling/shaders/ibl-diffuse_irradiance-di_convolution_fragment.wgsl @@ -0,0 +1,153 @@ +var global: vec4; +var global_1: vec3; +@group(0) @binding(2) +var global_2: sampler; +@group(0) @binding(1) +var global_3: texture_cube; + +fn function() { + var phi_273_: vec3; + var phi_308_: vec3; + var phi_343_: vec3; + var phi_138_: vec3; + var phi_141_: f32; + var phi_143_: f32; + var phi_153_: vec3; + var phi_156_: f32; + var phi_158_: f32; + var phi_378_: vec3; + var phi_154_: vec3; + var phi_157_: f32; + var phi_159_: f32; + var phi_139_: vec3; + var phi_142_: f32; + var phi_144_: f32; + var local: f32; + var local_1: vec3; + var local_2: vec3; + var local_3: vec3; + var local_4: vec3; + var local_5: f32; + + let _e14 = global_1; + let _e21 = sqrt(fma(_e14.z, _e14.z, fma(_e14.x, _e14.x, (_e14.y * _e14.y)))); + if (_e21 == 0f) { + phi_273_ = vec3(0f, 0f, 0f); + } else { + phi_273_ = (_e14 * (1f / _e21)); + } + let _e26 = phi_273_; + let _e28 = (_e26.y * -1f); + let _e31 = -(_e26.x); + let _e35 = sqrt(fma(_e26.z, _e26.z, (_e31 * _e31))); + if (_e35 == 0f) { + phi_308_ = vec3(0f, 0f, 0f); + } else { + phi_308_ = (vec3(_e26.z, 0f, _e31) * (1f / _e35)); + } + let _e40 = phi_308_; + let _e45 = fma(_e28, _e40.z, -((_e40.y * _e26.z))); + let _e49 = fma(_e26.z, _e40.x, -((_e40.z * _e26.x))); + let _e52 = fma(_e26.x, _e40.y, -((_e40.x * _e28))); + let _e57 = sqrt(fma(_e52, _e52, fma(_e45, _e45, (_e49 * _e49)))); + if (_e57 == 0f) { + phi_343_ = vec3(0f, 0f, 0f); + } else { + phi_343_ = (vec3(_e45, _e49, _e52) * (1f / _e57)); + } + let _e62 = phi_343_; + phi_138_ = vec3(0f, 0f, 0f); + phi_141_ = 0f; + phi_143_ = 0f; + loop { + let _e64 = phi_138_; + let _e66 = phi_141_; + let _e68 = phi_143_; + local = _e68; + local_1 = _e64; + local_2 = _e64; + local_3 = _e64; + let _e69 = (_e66 < 6.2831855f); + if _e69 { + phi_153_ = _e64; + phi_156_ = 0f; + phi_158_ = _e68; + loop { + let _e71 = phi_153_; + let _e73 = phi_156_; + let _e75 = phi_158_; + local_4 = _e71; + local_5 = _e75; + let _e76 = (_e73 < 1.5707964f); + if _e76 { + let _e77 = sin(_e73); + let _e79 = (_e77 * cos(_e66)); + let _e81 = (_e77 * sin(_e66)); + let _e82 = cos(_e73); + let _e92 = fma(_e82, _e26.x, fma(_e79, _e40.x, (_e81 * _e62.x))); + let _e93 = fma(_e82, _e28, fma(_e79, _e40.y, (_e81 * _e62.y))); + let _e94 = fma(_e82, _e26.z, fma(_e79, _e40.z, (_e81 * _e62.z))); + let _e99 = sqrt(fma(_e94, _e94, fma(_e92, _e92, (_e93 * _e93)))); + if (_e99 == 0f) { + phi_378_ = vec3(0f, 0f, 0f); + } else { + phi_378_ = (vec3(_e92, _e93, _e94) * (1f / _e99)); + } + let _e104 = phi_378_; + let _e105 = textureSample(global_3, global_2, _e104); + phi_154_ = vec3(fma((_e105.x * _e82), _e77, _e71.x), fma((_e105.y * _e82), _e77, _e71.y), fma((_e105.z * _e82), _e77, _e71.z)); + phi_157_ = (_e73 + 0.025f); + phi_159_ = (_e75 + 1f); + } else { + phi_154_ = vec3(); + phi_157_ = f32(); + phi_159_ = f32(); + } + let _e122 = phi_154_; + let _e124 = phi_157_; + let _e126 = phi_159_; + continue; + continuing { + phi_153_ = _e122; + phi_156_ = _e124; + phi_158_ = _e126; + break if !(_e76); + } + } + let _e167 = local_4; + phi_139_ = _e167; + phi_142_ = (_e66 + 0.025f); + let _e171 = local_5; + phi_144_ = _e171; + } else { + phi_139_ = vec3(); + phi_142_ = f32(); + phi_144_ = f32(); + } + let _e130 = phi_139_; + let _e132 = phi_142_; + let _e134 = phi_144_; + continue; + continuing { + phi_138_ = _e130; + phi_141_ = _e132; + phi_143_ = _e134; + break if !(_e69); + } + } + let _e137 = local; + let _e138 = (3.1415927f / _e137); + let _e140 = local_1; + let _e144 = local_2; + let _e148 = local_3; + global = vec4((_e140.x * _e138), (_e144.y * _e138), (_e148.z * _e138), 1f); + return; +} + +@fragment +fn ibldiffuse_irradiancedi_convolution_fragment(@location(0) param: vec3) -> @location(0) vec4 { + global_1 = param; + function(); + let _e3 = global; + return _e3; +} diff --git a/crates/renderling/shaders/manifest.json b/crates/renderling/shaders/manifest.json index 7e8e0f99..0b2d812e 100644 --- a/crates/renderling/shaders/manifest.json +++ b/crates/renderling/shaders/manifest.json @@ -79,6 +79,11 @@ "entry_point": "debug::debug_overlay_vertex", "wgsl_entry_point": "debugdebug_overlay_vertex" }, + { + "source_path": "shaders/ibl-diffuse_irradiance-di_convolution_fragment.spv", + "entry_point": "ibl::diffuse_irradiance::di_convolution_fragment", + "wgsl_entry_point": "ibldiffuse_irradiancedi_convolution_fragment" + }, { "source_path": "shaders/skybox-skybox_cubemap_fragment.spv", "entry_point": "skybox::skybox_cubemap_fragment", diff --git a/crates/renderling/shaders/skybox-skybox_cubemap_fragment.spv b/crates/renderling/shaders/skybox-skybox_cubemap_fragment.spv index fb9e97a2..e72ba1d5 100644 Binary files a/crates/renderling/shaders/skybox-skybox_cubemap_fragment.spv and b/crates/renderling/shaders/skybox-skybox_cubemap_fragment.spv differ diff --git a/crates/renderling/src/ibl/diffuse_irradiance.rs b/crates/renderling/src/ibl/diffuse_irradiance.rs index 5b801dd3..df73f386 100644 --- a/crates/renderling/src/ibl/diffuse_irradiance.rs +++ b/crates/renderling/src/ibl/diffuse_irradiance.rs @@ -1,127 +1,60 @@ -//! Pipeline and bindings for for diffuse irradiance convolution shaders. +//! Diffuse irradiance convolution. -pub fn diffuse_irradiance_convolution_bindgroup_layout( - device: &wgpu::Device, -) -> wgpu::BindGroupLayout { - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("convolution bindgroup"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::Cube, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }) -} +use glam::{Vec3, Vec4, Vec4Swizzles}; +#[cfg(target_arch = "spirv")] +use spirv_std::num_traits::Float; +use spirv_std::{image::Cubemap, spirv, Sampler}; -pub fn diffuse_irradiance_convolution_bindgroup( - device: &wgpu::Device, - label: Option<&str>, - buffer: &wgpu::Buffer, - // The texture to sample the environment from - texture: &crate::texture::Texture, -) -> wgpu::BindGroup { - device.create_bind_group(&wgpu::BindGroupDescriptor { - label, - layout: &diffuse_irradiance_convolution_bindgroup_layout(device), - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(buffer.as_entire_buffer_binding()), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&texture.view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(&texture.sampler), - }, - ], - }) -} +use crate::math::IsVector; + +#[cfg(not(target_arch = "spirv"))] +mod cpu; +#[cfg(not(target_arch = "spirv"))] +pub use cpu::*; -pub struct DiffuseIrradianceConvolutionRenderPipeline(pub wgpu::RenderPipeline); +/// Diffuse irradiance convolution. +#[spirv(fragment)] +pub fn di_convolution_fragment( + #[spirv(descriptor_set = 0, binding = 1)] environment_texture: &Cubemap, + #[spirv(descriptor_set = 0, binding = 2)] environment_sampler: &Sampler, + local_pos: Vec3, + frag_color: &mut Vec4, +) { + let normal = { + let mut n = local_pos.alt_norm_or_zero(); + n.y *= -1.0; + n + }; + let mut irradiance = Vec3::ZERO; + let right = Vec3::Y.cross(normal).alt_norm_or_zero(); + let up = normal.cross(right).alt_norm_or_zero(); -impl DiffuseIrradianceConvolutionRenderPipeline { - /// Create the rendering pipeline that performs a convolution. - pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { - log::trace!("creating convolution render pipeline with format '{format:?}'"); - let vertex_linkage = crate::linkage::skybox_cubemap_vertex::linkage(device); - let fragment_shader = device.create_shader_module(wgpu::include_wgsl!( - // TODO: rewrite this shader in Rust after atomics are added to naga spv - "../wgsl/diffuse_irradiance_convolution.wgsl" - )); - log::trace!(" done."); - let bg_layout = diffuse_irradiance_convolution_bindgroup_layout(device); - let pp_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("convolution pipeline layout"), - bind_group_layouts: &[&bg_layout], - push_constant_ranges: &[], - }); - // TODO: merge irradiance pipeline with cubemap - let pipeline = DiffuseIrradianceConvolutionRenderPipeline(device.create_render_pipeline( - &wgpu::RenderPipelineDescriptor { - label: Some("convolution pipeline"), - layout: Some(&pp_layout), - vertex: wgpu::VertexState { - module: &vertex_linkage.module, - entry_point: Some(vertex_linkage.entry_point), - buffers: &[], - compilation_options: Default::default(), - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - unclipped_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - mask: !0, - alpha_to_coverage_enabled: false, - count: 1, - }, - fragment: Some(wgpu::FragmentState { - module: &fragment_shader, - entry_point: Some("fragment_convolve_diffuse_irradiance"), - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - multiview: None, - cache: None, - }, - )); - log::trace!(" completed pipeline creation"); - pipeline + let sample_delta = 0.025; + let mut nr_samples = 0.0; + let mut phi = 0.0f32; + while phi < 2.0 * core::f32::consts::PI { + let mut theta = 0.0f32; + while theta < core::f32::consts::FRAC_PI_2 { + // spherical to cartisian tangent coords + let tangent_sample = Vec3::new( + theta.sin() * phi.cos(), + theta.sin() * phi.sin(), + theta.cos(), + ); + // tangent to world coords + let sample_vec = + (tangent_sample.x * right + tangent_sample.y * up + tangent_sample.z * normal) + .alt_norm_or_zero(); + let sample = environment_texture.sample(*environment_sampler, sample_vec) + * theta.cos() + * theta.sin(); + irradiance += sample.xyz(); + nr_samples += 1.0; + + theta += sample_delta; + } + phi += sample_delta } + + *frag_color = (irradiance * (core::f32::consts::PI / nr_samples)).extend(1.0); } diff --git a/crates/renderling/src/diffuse_irradiance.rs b/crates/renderling/src/ibl/diffuse_irradiance/cpu.rs similarity index 80% rename from crates/renderling/src/diffuse_irradiance.rs rename to crates/renderling/src/ibl/diffuse_irradiance/cpu.rs index e1e8b9b5..141cdb9c 100644 --- a/crates/renderling/src/diffuse_irradiance.rs +++ b/crates/renderling/src/ibl/diffuse_irradiance/cpu.rs @@ -1,7 +1,4 @@ //! Pipeline and bindings for for diffuse irradiance convolution shaders. -use renderling_shader::scene::GpuConstants; - -use crate::Uniform; pub fn diffuse_irradiance_convolution_bindgroup_layout( device: &wgpu::Device, @@ -13,7 +10,7 @@ pub fn diffuse_irradiance_convolution_bindgroup_layout( binding: 0, visibility: wgpu::ShaderStages::VERTEX, ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, + ty: wgpu::BufferBindingType::Storage { read_only: true }, has_dynamic_offset: false, min_binding_size: None, }, @@ -42,9 +39,9 @@ pub fn diffuse_irradiance_convolution_bindgroup_layout( pub fn diffuse_irradiance_convolution_bindgroup( device: &wgpu::Device, label: Option<&str>, - constants: &Uniform, + buffer: &wgpu::Buffer, // The texture to sample the environment from - texture: &crate::Texture, + texture: &crate::texture::Texture, ) -> wgpu::BindGroup { device.create_bind_group(&wgpu::BindGroupDescriptor { label, @@ -52,9 +49,7 @@ pub fn diffuse_irradiance_convolution_bindgroup( entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::Buffer( - constants.buffer().as_entire_buffer_binding(), - ), + resource: wgpu::BindingResource::Buffer(buffer.as_entire_buffer_binding()), }, wgpu::BindGroupEntry { binding: 1, @@ -74,29 +69,29 @@ impl DiffuseIrradianceConvolutionRenderPipeline { /// Create the rendering pipeline that performs a convolution. pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { log::trace!("creating convolution render pipeline with format '{format:?}'"); - let vertex_linkage = crate::linkage::vertex_cubemap(device); - let vertex_shader = - device.create_shader_module(wgpu::include_spirv!("linkage/vertex_cubemap.spv")); - let fragment_shader = device.create_shader_module(wgpu::include_wgsl!( - "wgsl/diffuse_irradiance_convolution.wgsl" - )); + let vertex_linkage = crate::linkage::skybox_cubemap_vertex::linkage(device); + let fragment_linkage = crate::linkage::di_convolution_fragment::linkage(device); + // let fragment_shader = device.create_shader_module(wgpu::include_wgsl!( + // // TODO: rewrite this shader in Rust after atomics are added to naga spv + // "../../wgsl/diffuse_irradiance_convolution.wgsl" + // )); log::trace!(" done."); - //.create_shader_module(wgpu::include_spirv!("linkage/fragment_convolve_diffuse_irradiance.spv")); let bg_layout = diffuse_irradiance_convolution_bindgroup_layout(device); let pp_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("convolution pipeline layout"), bind_group_layouts: &[&bg_layout], push_constant_ranges: &[], }); - // TODO: merge irradiance pipeline with the pipeline in cubemap.rs + // TODO: merge irradiance pipeline with cubemap let pipeline = DiffuseIrradianceConvolutionRenderPipeline(device.create_render_pipeline( &wgpu::RenderPipelineDescriptor { label: Some("convolution pipeline"), layout: Some(&pp_layout), vertex: wgpu::VertexState { module: &vertex_linkage.module, - entry_point: vertex_linkage.entry_point, + entry_point: Some(vertex_linkage.entry_point), buffers: &[], + compilation_options: Default::default(), }, primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, @@ -114,15 +109,17 @@ impl DiffuseIrradianceConvolutionRenderPipeline { count: 1, }, fragment: Some(wgpu::FragmentState { - module: &fragment_shader, - entry_point: "fragment_convolve_diffuse_irradiance", + module: &fragment_linkage.module, + entry_point: Some(fragment_linkage.entry_point), targets: &[Some(wgpu::ColorTargetState { format, blend: Some(wgpu::BlendState::ALPHA_BLENDING), write_mask: wgpu::ColorWrites::ALL, })], + compilation_options: Default::default(), }), multiview: None, + cache: None, }, )); log::trace!(" completed pipeline creation"); diff --git a/crates/renderling/src/ibl/mod.rs b/crates/renderling/src/ibl/mod.rs index 0b8ebcc2..fe2a39a6 100644 --- a/crates/renderling/src/ibl/mod.rs +++ b/crates/renderling/src/ibl/mod.rs @@ -1,4 +1,5 @@ //! Resources for image based lighting. pub mod diffuse_irradiance; +#[cfg(not(target_arch = "spirv"))] pub mod prefiltered_environment; diff --git a/crates/renderling/src/lib.rs b/crates/renderling/src/lib.rs index af3577a5..1ef4b474 100644 --- a/crates/renderling/src/lib.rs +++ b/crates/renderling/src/lib.rs @@ -159,7 +159,6 @@ pub mod cubemap; pub mod cull; pub mod debug; pub mod draw; -#[cfg(not(target_arch = "spirv"))] pub mod ibl; #[cfg(not(target_arch = "spirv"))] mod linkage; @@ -169,7 +168,7 @@ pub mod sdf; pub mod skybox; pub mod slab; pub mod stage; -#[cfg(not(target_arch = "spirv"))] +#[cfg(not(target_arch = "spirv"))] pub mod texture; pub mod tonemapping; pub mod transform; diff --git a/crates/renderling/src/linkage.rs b/crates/renderling/src/linkage.rs index b5001c96..bbffe8f8 100644 --- a/crates/renderling/src/linkage.rs +++ b/crates/renderling/src/linkage.rs @@ -18,6 +18,7 @@ pub mod compute_culling; pub mod compute_downsample_depth_pyramid; pub mod debug_overlay_fragment; pub mod debug_overlay_vertex; +pub mod di_convolution_fragment; pub mod generate_mipmap_fragment; pub mod generate_mipmap_vertex; pub mod prefilter_environment_cubemap_fragment; diff --git a/crates/renderling/src/linkage/di_convolution_fragment.rs b/crates/renderling/src/linkage/di_convolution_fragment.rs new file mode 100644 index 00000000..3f7a0ad0 --- /dev/null +++ b/crates/renderling/src/linkage/di_convolution_fragment.rs @@ -0,0 +1,34 @@ +#![allow(dead_code)] +//! Automatically generated by Renderling's `build.rs`. +use crate::linkage::ShaderLinkage; +#[cfg(not(target_arch = "wasm32"))] +mod target { + pub const ENTRY_POINT: &str = "ibl::diffuse_irradiance::di_convolution_fragment"; + pub fn descriptor() -> wgpu::ShaderModuleDescriptor<'static> { + wgpu::include_spirv!("../../shaders/ibl-diffuse_irradiance-di_convolution_fragment.spv") + } + pub fn linkage(device: &wgpu::Device) -> super::ShaderLinkage { + log::info!("creating native linkage for {}", "di_convolution_fragment"); + super::ShaderLinkage { + entry_point: ENTRY_POINT, + module: device.create_shader_module(descriptor()).into(), + } + } +} +#[cfg(target_arch = "wasm32")] +mod target { + pub const ENTRY_POINT: &str = "ibldiffuse_irradiancedi_convolution_fragment"; + pub fn descriptor() -> wgpu::ShaderModuleDescriptor<'static> { + wgpu::include_wgsl!("../../shaders/ibl-diffuse_irradiance-di_convolution_fragment.wgsl") + } + pub fn linkage(device: &wgpu::Device) -> super::ShaderLinkage { + log::info!("creating web linkage for {}", "di_convolution_fragment"); + super::ShaderLinkage { + entry_point: ENTRY_POINT, + module: device.create_shader_module(descriptor()).into(), + } + } +} +pub fn linkage(device: &wgpu::Device) -> ShaderLinkage { + target::linkage(device) +} diff --git a/crates/renderling/src/wgsl/diffuse_irradiance_convolution.wgsl b/crates/renderling/src/wgsl/diffuse_irradiance_convolution.wgsl deleted file mode 100644 index 4df79245..00000000 --- a/crates/renderling/src/wgsl/diffuse_irradiance_convolution.wgsl +++ /dev/null @@ -1,45 +0,0 @@ -@group(0) -@binding(1) -var environment_texture: texture_cube; - -@group(0) -@binding(2) -var environment_sampler: sampler; - -struct Input { - @builtin(position) position: vec4, - @location(0) local_pos: vec3, -}; - -@fragment -fn fragment_convolve_diffuse_irradiance(input: Input) -> @location(0) vec4 { - let pi = 3.1415927; - let frac_pi_2 = pi / 2.0; - - var normal = normalize(input.local_pos); - normal.y *= -1.0; - var irradiance = vec3f(0.0, 0.0, 0.0); - let right = normalize(cross(vec3f(0.0, 1.0, 0.0), normal)); - let up = normalize(cross(normal, right)); - - let sample_delta = 0.025; - var nr_samples = 0.0; - var phi = 0.0; - for (var phi = 0.0; phi < 2.0 * pi; phi += sample_delta) { - for (var theta = 0.0; theta < frac_pi_2; theta += sample_delta) { - // spherical to cartisian tangent coords - let tangent_sample = vec3f( - sin(theta) * cos(phi), - sin(theta) * sin(phi), - cos(theta), - ); - // tangent to world coords - let sample_vec = normalize(tangent_sample.x * right + tangent_sample.y * up + tangent_sample.z * normal); - let sample = textureSample(environment_texture, environment_sampler, sample_vec) * cos(theta) * sin(theta); - irradiance += sample.xyz; - nr_samples += 1.0; - } - } - let color = irradiance * (pi / nr_samples); - return vec4(color.xyz, 1.0); -}