From 0746a09c457469bc7e60ea9bb7f9342c6ff3f74f Mon Sep 17 00:00:00 2001 From: nullprop Date: Tue, 24 Jan 2023 03:59:06 +0200 Subject: [PATCH] pbr --- src/core/model.rs | 18 ++++++++++ src/core/resources.rs | 37 +++++++++++++++++-- src/core/state.rs | 19 +++++++++- src/shaders/test.wgsl | 84 ++++++++++++++++++++++++++++++++++++++----- 4 files changed, 147 insertions(+), 11 deletions(-) diff --git a/src/core/model.rs b/src/core/model.rs index c9dd0a4..3c47fa1 100644 --- a/src/core/model.rs +++ b/src/core/model.rs @@ -6,6 +6,9 @@ pub struct Material { pub name: String, pub diffuse_texture: Texture, pub normal_texture: Texture, + pub metallic_roughness_texture: Texture, + pub metallic_factor: f32, // TODO pass to shader + pub roughness_factor: f32, // TODO pass to shader pub bind_group: wgpu::BindGroup, } @@ -15,6 +18,9 @@ impl Material { name: &str, diffuse_texture: Texture, normal_texture: Texture, + metallic_roughness_texture: Texture, + metallic_factor: f32, + roughness_factor: f32, layout: &wgpu::BindGroupLayout, ) -> Self { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -38,6 +44,15 @@ impl Material { binding: 3, resource: wgpu::BindingResource::Sampler(&normal_texture.sampler), }, + // metallic roughness + wgpu::BindGroupEntry { + binding: 4, + resource: wgpu::BindingResource::TextureView(&metallic_roughness_texture.view), + }, + wgpu::BindGroupEntry { + binding: 5, + resource: wgpu::BindingResource::Sampler(&metallic_roughness_texture.sampler), + }, ], label: None, }); @@ -46,6 +61,9 @@ impl Material { name: String::from(name), diffuse_texture, normal_texture, + metallic_roughness_texture, + metallic_factor, + roughness_factor, bind_group, }; } diff --git a/src/core/resources.rs b/src/core/resources.rs index c8d3d50..caa233d 100644 --- a/src/core/resources.rs +++ b/src/core/resources.rs @@ -192,7 +192,7 @@ pub async fn load_model_gltf( let diffuse_index = pbr .base_color_texture() .map(|tex| { - println!("Grabbing diffuse tex"); + println!("gltf: get diffuse tex"); tex.texture().source().index() }) .unwrap_or(0); // TODO default tex @@ -221,7 +221,7 @@ pub async fn load_model_gltf( let normal_index = material .normal_texture() .map(|tex| { - println!("Grabbing normal tex"); + println!("gltf: get normal tex"); tex.texture().source().index() }) .unwrap_or(0); // TODO default tex @@ -246,11 +246,44 @@ pub async fn load_model_gltf( ) .unwrap(); + // metallic + roughness + let mr_index = pbr + .metallic_roughness_texture() + .map(|tex| { + println!("gltf: get metallic roughness tex"); + tex.texture().source().index() + }) + .unwrap_or(0); // TODO default tex + + let mr_data = &mut images[mr_index]; + dbg!(mr_data.format); + + if mr_data.format == gltf::image::Format::R8G8B8 + || mr_data.format == gltf::image::Format::R16G16B16 + { + mr_data.pixels = + gltf_pixels_to_wgpu(mr_data.pixels.clone(), mr_data.format); + } + + let mr_texture = Texture::from_pixels( + device, + queue, + &mr_data.pixels, + (mr_data.width, mr_data.height), + gltf_image_format_stride(mr_data.format), + gltf_image_format_to_wgpu(mr_data.format, false), + Some(file_name), + ) + .unwrap(); + materials.push(Material::new( device, &material.name().unwrap_or("Default Material").to_string(), diffuse_texture, normal_texture, + mr_texture, + pbr.metallic_factor(), + pbr.roughness_factor(), layout, )); } diff --git a/src/core/state.rs b/src/core/state.rs index 39c1764..cafda12 100644 --- a/src/core/state.rs +++ b/src/core/state.rs @@ -117,7 +117,7 @@ impl State { let camera_controller = CameraController::new(1.0, 2.0); - let light_uniform = LightUniform::new([100.0, 60.0, 0.0], [1.0, 1.0, 1.0, 10000.0]); + let light_uniform = LightUniform::new([100.0, 60.0, 0.0], [1.0, 1.0, 1.0, 100000.0]); // We'll want to update our lights position, so we use COPY_DST let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { @@ -189,6 +189,23 @@ impl State { ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: None, }, + // metallic + roughness + wgpu::BindGroupLayoutEntry { + binding: 4, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 5, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, ], label: Some("texture_bind_group_layout"), }); diff --git a/src/shaders/test.wgsl b/src/shaders/test.wgsl index a64c4ee..a8a3a55 100644 --- a/src/shaders/test.wgsl +++ b/src/shaders/test.wgsl @@ -1,3 +1,5 @@ +let PI = 3.14159; + // Vertex shader struct CameraUniform { @@ -84,6 +86,40 @@ fn vs_main( // Fragment shader +// normal distribution function (Trowbridge-Reitz GGX) + +fn distribution_ggx(n: vec3, h: vec3, a: f32) -> f32 { + let a2 = a * a; + let n_dot_h = max(dot(n, h), 0.0); + let n_dot_h2 = n_dot_h * n_dot_h; + + var denom = (n_dot_h2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return a2 / denom; +} + +// geometry function (Smith's Schlick-GGX) + +fn geometry_schlick_ggx(nom: f32, k: f32) -> f32 { + let denom = nom * (1.0 - k) + k; + return nom / denom; +} + +fn geometry_smith(n: vec3, v: vec3, l: vec3, k: f32) -> f32 { + let n_dot_v = max(dot(n, v), 0.0); + let n_dot_l = max(dot(n, l), 0.0); + let ggx1 = geometry_schlick_ggx(n_dot_v, k); + let ggx2 = geometry_schlick_ggx(n_dot_l, k); + return ggx1 * ggx2; +} + +// fresnel function (Fresnel-Schlick approximation) + +fn fresnel_schlick(cos_theta: f32, f: vec3) -> vec3 { + return f + (1.0 - f) * pow(1.0 - cos_theta, 5.0); +} + @group(0) @binding(0) var t_diffuse: texture_2d; @group(0)@binding(1) @@ -94,11 +130,23 @@ var t_normal: texture_2d; @group(0) @binding(3) var s_normal: sampler; +@group(0)@binding(4) +var t_metallic_roughness: texture_2d; +@group(0) @binding(5) +var s_metallic_roughness: sampler; + @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { // textures let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); let object_normal: vec4 = textureSample(t_normal, s_normal, in.tex_coords); + let object_metallic_roughness: vec4 = textureSample( + t_metallic_roughness, s_metallic_roughness, in.tex_coords); + // TODO: AO + + let albedo = object_color.xyz; + let metallic = object_metallic_roughness.z; + let roughness = object_metallic_roughness.y; // lighting vecs let tangent_normal = object_normal.xyz * 2.0 - 1.0; @@ -109,23 +157,43 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { // attenuation let light_dist = length(light.position - in.world_position); let coef_a = 0.0; - let coef_b = 1.25; + let coef_b = 1.0; let light_attenuation = 1.0 / (1.0 + coef_a * light_dist + coef_b * light_dist * light_dist); - // diffuse - let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0); - let diffuse_color = diffuse_strength * light.color.xyz * light.color.w * light_attenuation; + // radiance + let radiance_strength = max(dot(tangent_normal, light_dir), 0.0); + let radiance = radiance_strength * light.color.xyz * light.color.w * light_attenuation; - // specular - let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0); - let specular_color = specular_strength * light.color.xyz * light.color.w * light_attenuation; + // fresnel + var f = vec3(0.04); + f = mix(f, albedo, metallic); + let fresnel = fresnel_schlick(max(dot(half_dir, view_dir), 0.0), f); + + // distribution + let ndf = distribution_ggx(tangent_normal, half_dir, roughness); + + // geometry + let geo = geometry_smith(tangent_normal, view_dir, light_dir, roughness); + + // brdf + let nom = ndf * geo * fresnel; + let denom = 4.0 * max(dot(tangent_normal, view_dir), 0.0) * max(dot(tangent_normal, light_dir), 0.0) + 0.0001; + let specular = nom / denom; + + let k_d = (vec3(1.0) - fresnel) * (1.0 - metallic); + let n_dot_l = max(dot(tangent_normal, light_dir), 0.0); + let total_radiance = (k_d * albedo / PI + specular) * radiance * n_dot_l; // ambient let ambient_light_color = vec3(1.0); let ambient_strength = 0.025; let ambient_color = ambient_light_color * ambient_strength; - let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; + var result = ambient_color + total_radiance; + + // tonemap + result = result / (result + vec3(1.0)); + //result = pow(result, vec3(1.0/2.2)); return vec4(result, object_color.a); }