From cccc62e5a8b21657f2ce8d0168a3de509c603b82 Mon Sep 17 00:00:00 2001 From: marceline-cramer Date: Wed, 23 Feb 2022 04:04:40 -0700 Subject: [PATCH] Shader includes --- Cargo.lock | 198 ++++++++++-------------- Cargo.toml | 1 + src/main.rs | 1 + src/renderer.rs | 15 +- src/shader.rs | 70 +++++++++ src/shader.wgsl | 275 ++++++++------------------------- src/shaders/basic_pbr.wgsl | 68 ++++++++ src/shaders/basic_structs.wgsl | 35 +++++ src/shaders/tri_sampler.wgsl | 12 ++ 9 files changed, 346 insertions(+), 329 deletions(-) create mode 100644 src/shader.rs create mode 100644 src/shaders/basic_pbr.wgsl create mode 100644 src/shaders/basic_structs.wgsl create mode 100644 src/shaders/tri_sampler.wgsl diff --git a/Cargo.lock b/Cargo.lock index ea66357..95867b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" @@ -160,7 +160,7 @@ dependencies = [ "bitflags", "block", "cocoa-foundation", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "core-graphics 0.22.3", "foreign-types", "libc", @@ -175,7 +175,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" dependencies = [ "bitflags", "block", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "core-graphics-types", "foreign-types", "libc", @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys 0.8.3", "libc", @@ -255,7 +255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ "bitflags", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "core-graphics-types", "foreign-types", "libc", @@ -268,7 +268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ "bitflags", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "foreign-types", "libc", ] @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if 1.0.0", ] @@ -318,9 +318,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ "cfg-if 1.0.0", "lazy_static", @@ -352,7 +352,8 @@ dependencies = [ "bytemuck", "glam", "gltf", - "image 0.24.0", + "image 0.24.1", + "naga", "pollster", "slab", "tobj", @@ -416,15 +417,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "deflate" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f95bf05dffba6e6cce8dfbb30def788154949ccd9aed761b472119c21e01c70" -dependencies = [ - "adler32", -] - [[package]] name = "deflate" version = "1.0.0" @@ -461,70 +453,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "encoding" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" -dependencies = [ - "encoding-index-japanese", - "encoding-index-korean", - "encoding-index-simpchinese", - "encoding-index-singlebyte", - "encoding-index-tradchinese", -] - -[[package]] -name = "encoding-index-japanese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-korean" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-simpchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-singlebyte" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-tradchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding_index_tests" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" - [[package]] name = "exr" version = "1.4.1" @@ -555,9 +483,9 @@ dependencies = [ [[package]] name = "flume" -version = "0.10.10" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d04dafd11240188e146b6f6476a898004cace3be31d4ec5e08e216bf4947ac0" +checksum = "0b279436a715a9de95dcd26b151db590a71961cc06e54918b24fe0dd5b7d3fc4" dependencies = [ "futures-core", "futures-sink", @@ -589,15 +517,15 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "futures-core" -version = "0.3.19" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" [[package]] name = "futures-sink" -version = "0.3.19" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" [[package]] name = "fxhash" @@ -610,9 +538,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -779,20 +707,20 @@ dependencies = [ [[package]] name = "image" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94ac3d41f882c624a82d7945952032388488681f45f9d4077999a6c85688d61" +checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" dependencies = [ "bytemuck", "byteorder", "color_quant", "exr", "gif", - "jpeg-decoder 0.2.1", + "jpeg-decoder 0.2.2", "num-iter", "num-rational 0.4.0", "num-traits", - "png 0.17.2", + "png 0.17.3", "scoped_threadpool", "tiff", ] @@ -805,6 +733,7 @@ checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ "autocfg", "hashbrown", + "serde", ] [[package]] @@ -860,9 +789,9 @@ checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" [[package]] name = "jpeg-decoder" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcf0244f6597be39ab8d9203f574cafb529ae8c698afa2182f7b3c3205a4a9c" +checksum = "105fb082d64e2100074587f59a74231f771750c664af903f1f9f76c9dedfc6f1" dependencies = [ "rayon", ] @@ -900,9 +829,9 @@ checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff" [[package]] name = "libc" -version = "0.2.116" +version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" [[package]] name = "libloading" @@ -1004,6 +933,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.0" @@ -1039,7 +977,9 @@ dependencies = [ "indexmap", "log", "num-traits", + "pp-rs", "rustc-hash", + "serde", "spirv", "thiserror", ] @@ -1067,15 +1007,22 @@ dependencies = [ ] [[package]] -name = "ndk-glue" -version = "0.5.0" +name = "ndk-context" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc291b8de2095cba8dab7cf381bf582ff4c17a09acf854c32e46545b08085d28" +checksum = "4e3c5cc68637e21fe8f077f6a1c9e0b9ca495bb74895226b476310f613325884" + +[[package]] +name = "ndk-glue" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1c68f70683c5fc9a747a383744206cd371741b2f0b31781ab6770487ec572e2" dependencies = [ "lazy_static", "libc", "log", "ndk", + "ndk-context", "ndk-macro", "ndk-sys", ] @@ -1125,9 +1072,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi", ] @@ -1311,15 +1258,14 @@ dependencies = [ [[package]] name = "png" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c845088517daa61e8a57eee40309347cea13f273694d1385c553e7a57127763b" +checksum = "8e8f1882177b17c98ec33a51f5910ecbf4db92ca0def706781a1f8d0c661f393" dependencies = [ "bitflags", "crc32fast", - "deflate 0.9.1", - "encoding", - "miniz_oxide 0.4.4", + "deflate 1.0.0", + "miniz_oxide 0.5.1", ] [[package]] @@ -1329,10 +1275,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" [[package]] -name = "proc-macro-crate" -version = "1.1.0" +name = "pp-rs" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" +checksum = "bb458bb7f6e250e6eb79d5026badc10a3ebb8f9a15d1fff0f13d17c71f4d6dee" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dada8c9981fcf32929c3c0f0cd796a9284aca335565227ed88c83babb1d43dc" dependencies = [ "thiserror", "toml", @@ -1452,6 +1407,9 @@ name = "serde" version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1927,7 +1885,7 @@ checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" dependencies = [ "bitflags", "cocoa", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "core-graphics 0.22.3", "core-video-sys", "dispatch", diff --git a/Cargo.toml b/Cargo.toml index 60bf8dd..4bb2adc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ slab = "0.4" tobj = "3.0" wgpu = "0.12" winit = "0.26" +naga = { version = "0.8.5", features = ["wgsl-in", "glsl-in", "wgsl-out", "serialize", "deserialize"] } diff --git a/src/main.rs b/src/main.rs index 4ad898a..fc04388 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ mod model; mod pool; mod renderer; mod scene; +mod shader; use camera::*; use model::*; diff --git a/src/renderer.rs b/src/renderer.rs index db6559b..c0b5655 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -3,9 +3,11 @@ use super::commands::{Command, CommandSet}; use super::mesh::{MeshData, Vertex}; use super::pool::*; use super::scene::{PointLight, Scene}; +use super::shader::{parse_wgsl, generate_wgsl, add_includes}; use crate::handle::*; use crate::model::OnLoad; use wgpu::util::DeviceExt; +use std::fs::{File, read_to_string}; pub struct Renderer { pub device: wgpu::Device, @@ -140,10 +142,21 @@ impl Renderer { push_constant_ranges: &[], }); + + // Generate a shader and preprocess it + let mut source = read_to_string("src/shader.wgsl").unwrap(); + source = add_includes(&source); + + // Parse the WGSL into a usable module + let module = parse_wgsl(&source); + + // Generate a valid WGSL string from the module + let gen_wgsl = generate_wgsl(&module); + let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: Some("shader.wgsl"), source: wgpu::ShaderSource::Wgsl( - std::fs::read_to_string("src/shader.wgsl").unwrap().into(), + std::borrow::Cow::Owned(gen_wgsl), ), }); diff --git a/src/shader.rs b/src/shader.rs new file mode 100644 index 0000000..7494932 --- /dev/null +++ b/src/shader.rs @@ -0,0 +1,70 @@ +use std::fs::{File, read_to_string}; + +pub fn parse_wgsl(source: &String) -> naga::Module { + // Create Parser + let mut parser = naga::front::wgsl::Parser::new(); + + // Create empty Module + let mut module = naga::Module::default(); + + // Attempt to parse the source code + let module_result = parser.parse(source.as_str()); + match module_result { + Ok(in_mod) => { + //println!("{:?}", in_mod); + module = in_mod; + }, + Err(error) => { println!("Parsing error:\n{}", error) } + } + + // Return the Module + module +} + +pub fn generate_wgsl(module: &naga::Module) -> String { + // Create options for the Writer + let wgsl_flags = naga::back::wgsl::WriterFlags::empty(); + + // Create a string buffer for the Writer to use + let mut wgsl_buffer = String::new(); + + // Create validator (to check if module is OK) + let mut validator = naga::valid::Validator::new(naga::valid::ValidationFlags::empty(), naga::valid::Capabilities::empty()); + + // Get ModuleInfo (produced by a syntax validator) + let module_info = validator.validate(&module).unwrap(); + + // Create the Writer itself + let mut wgsl_writer = naga::back::wgsl::Writer::new(&mut wgsl_buffer, wgsl_flags); + + // Attempt to write + wgsl_writer.write(&module, &module_info).expect("wgsl write failed"); + + wgsl_buffer +} + +pub fn add_includes(source: &String) -> String { + let mut combined = String::new(); + + let source_lines = source.lines(); + for line in source_lines { + // Get a vector of the words in the line + let mut words: Vec<&str> = line.split(" ").collect(); + + // Check if this is an include statement + if words[0] == "#include" { + // Get the rest of the line + words.remove(0); + let file_path: String = words.join(" "); + + // Get the code from the included file + let included = read_to_string(file_path).unwrap(); + + combined = format!("{}\n{}", combined, included); + } else { + combined = format!("{}\n{}", combined, line); + } + } + + combined +} \ No newline at end of file diff --git a/src/shader.wgsl b/src/shader.wgsl index 0fa21c7..17fe160 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -1,208 +1,67 @@ -struct CameraUniform { - eye: vec4; - vp: mat4x4; -}; - -struct MeshInstance { - transform: mat4x4; -}; - -struct MeshData { - instances: array; -}; - -struct PointLight { - center: vec4; - intensity: vec4; -}; - -struct PointLightData { - num: i32; - lights: array; -}; - -struct VertexInput { - [[location(0)]] position: vec3; - [[location(1)]] normal: vec3; - [[location(2)]] tex_coords: vec2; -}; - -struct VertexOutput { - [[builtin(position)]] clip_position: vec4; - [[location(0)]] position: vec3; - [[location(1)]] normal: vec3; - [[location(2)]] tex_coords: vec2; -}; - -[[group(0), binding(0)]] -var camera: CameraUniform; - -[[group(0), binding(1)]] -var point_lights: PointLightData; - -[[group(1), binding(0)]] -var meshes: MeshData; - -[[group(2), binding(0)]] var m_sampler: sampler; -[[group(2), binding(1)]] var m_albedo: texture_2d; -[[group(2), binding(2)]] var m_metallic_roughness: texture_2d; - -let PI: f32 = 3.141592; - -fn D_GGX(NoH: f32, roughness: f32) -> f32 { - let a = roughness * roughness; - let a2 = a * a; - let NoH2 = NoH * NoH; - let f = NoH * (a2 - 1.0) + 1.0; - return a2 / (PI * f * f); -} - -fn g1(NoV: f32, roughness: f32, k: f32) -> f32 { - let denom = NoV * (1.0 - k) + k; - return NoV / denom; -} - -fn G_SmithGGXCorrelated(NoV: f32, NoL: f32, roughness: f32) -> f32 { - let r = roughness + 1.0; - let k = (r * r) / 8.0; - - let g1l = g1(NoV, roughness, k); - let g1v = g1(NoL, roughness, k); - - return g1l * g1v; -} - -fn F_Schlick(u: f32, f0: vec3) -> vec3 { - let f = pow(1.0 - u, 5.0); - return f + f0 * (1.0 - f); -} - -fn BRDF( - l: vec3, // normalized light direction - n: vec3, // normalized surface normal - v: vec3, // normalized view direction - albedo: vec3, // surface albedo - metallic: f32, // surface metallic - roughness: f32, // surface roughness -) -> vec3 { - let h = normalize(v + l); - let NoL = max(dot(n, l), 0.0); - let NoV = max(dot(n, v), 0.0); - let NoH = max(dot(n, h), 0.0); - let LoH = max(dot(l, h), 0.0); - - // calculate reflectance at surface incidence - let f0 = mix(vec3(0.04), albedo, metallic); - - // specular BRDF - let D = D_GGX(NoH, roughness); - let G = G_SmithGGXCorrelated(NoV, NoL, roughness); - let F = F_Schlick(LoH, f0); - - let numerator = (D * G) * F; - let denominator = 4.0 * NoV * NoL; - - let Fr = numerator / max(denominator, 0.01); - - // diffuse BRDF - let diffuse_fresnel = - (vec3(1.0) - F_Schlick(NoL, f0)) * - (vec3(1.0) - F_Schlick(NoV, f0)); - - let lambertian = albedo / PI; - let Fd = diffuse_fresnel * lambertian; - - // TODO multiple scattering - return (Fr + Fd) * NoL; -} - -fn sample_ibl_fake( - n_color: vec3, - s_color: vec3, - gradient: f32, - coords: vec3, -) -> vec3 { - let blend = coords.y / gradient + 0.5; - return mix(s_color, n_color, clamp(blend, 0.0, 1.0)); -} - -fn F_Schlick_Roughness(cos_theta: f32, f0: vec3, roughness: f32) -> vec3 { - return f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(clamp(1.0 - cos_theta, 0.0, 1.0), 5.0); -} - -let N_COLOR = vec3(0.7, 0.0, 1.0); -let S_COLOR = vec3(0.1, 0.1, 0.1); -let IBL_G = 1.0; - -fn fake_ibl( - n: vec3, // normalized surface normal - v: vec3, // normalized view direction - albedo: vec3, // surface albedo - metallic: f32, // surface metallic - roughness: f32, // surface roughness -) -> vec3 { - // calculate reflectance at surface incidence - let f0 = mix(vec3(0.04), albedo, metallic); - let kS = F_Schlick_Roughness(max(dot(n, v), 0.0), f0, roughness); - let kD = 1.0 - kS; - let diffuse = sample_ibl_fake(N_COLOR, S_COLOR, IBL_G, n); - - let R = normalize(reflect(-v, n)); - let specular = sample_ibl_fake(N_COLOR, S_COLOR, IBL_G, R); - - let received = kD * diffuse + kS * specular; - - return received * albedo; -} - -[[stage(vertex)]] -fn vs_main( - [[builtin(instance_index)]] mesh_idx: u32, - vertex: VertexInput, -) -> VertexOutput { - let transform = meshes.instances[mesh_idx].transform; - let world_pos = transform * vec4(vertex.position, 1.0); - let world_normal = transform * vec4(vertex.normal, 0.0); - - var out: VertexOutput; - out.clip_position = camera.vp * world_pos; - out.position = world_pos.xyz; - out.normal = world_normal.xyz; - out.tex_coords = vertex.tex_coords; - return out; -} - -[[stage(fragment)]] -fn fs_main( - frag: VertexOutput, -) -> [[location(0)]] vec4 { - let albedo = textureSample(m_albedo, m_sampler, frag.tex_coords).rgb; - let normal = normalize(frag.normal); - let view = normalize(camera.eye.xyz - frag.position); - - let metallic_roughness = textureSample(m_metallic_roughness, m_sampler, frag.tex_coords).bg; - let metallic = metallic_roughness.x; - let roughness = metallic_roughness.y; - - var lum = vec3(0.0); - for(var i = 0; i < 4; i = i + 1) { - let light = point_lights.lights[i]; - let light_position = light.center.xyz - frag.position; - let light_intensity = light.intensity.rgb; - let light_direction = normalize(light_position); - - let radiance = light_intensity / dot(light_position, light_position); - - let reflected = BRDF( - light_direction, normal, view, - albedo, metallic, roughness - ); - - lum = lum + (radiance * reflected); - } - - lum = lum + fake_ibl(normal, view, albedo, metallic, roughness); - - let tone_mapped = lum / (lum + 1.0); - return vec4(tone_mapped, 1.0); -} +#include src/shaders/basic_structs.wgsl +#include src/shaders/basic_pbr.wgsl +#include src/shaders/tri_sampler.wgsl + +[[group(0), binding(0)]] +var camera: CameraUniform; + +[[group(0), binding(1)]] +var point_lights: PointLightData; + +[[group(1), binding(0)]] +var meshes: MeshData; + +[[group(2), binding(0)]] var m_sampler: sampler; +[[group(2), binding(1)]] var m_albedo: texture_2d; +[[group(2), binding(2)]] var m_metallic_roughness: texture_2d; + +[[stage(vertex)]] +fn vs_main( + [[builtin(instance_index)]] mesh_idx: u32, + vertex: VertexInput, +) -> VertexOutput { + let transform = meshes.instances[mesh_idx].transform; + let world_pos = transform * vec4(vertex.position, 1.0); + let world_normal = transform * vec4(vertex.normal, 0.0); + + var out: VertexOutput; + out.clip_position = camera.vp * world_pos; + out.position = world_pos.xyz; + out.normal = world_normal.xyz; + out.tex_coords = vertex.tex_coords; + return out; +} + +[[stage(fragment)]] +fn fs_main( + frag: VertexOutput, +) -> [[location(0)]] vec4 { + let normal = normalize(frag.normal); + let view = normalize(camera.eye.xyz - frag.position); + + let albedo = textureSample(m_albedo, m_sampler, frag.tex_coords).xyz; + // let albedo = TriSampler(m_albedo, m_sampler, frag.position, normal, 10.0); + + let metallic_roughness = textureSample(m_metallic_roughness, m_sampler, frag.tex_coords).bg; + let metallic = metallic_roughness.x; + let roughness = metallic_roughness.y; + + var lum = vec3(0.0); + for(var i = 0; i < 4; i = i + 1) { + let light = point_lights.lights[i]; + let light_position = light.center.xyz - frag.position; + let light_intensity = light.intensity.rgb; + let light_direction = normalize(light_position); + + let radiance = light_intensity / dot(light_position, light_position); + + let reflected = BRDF( + light_direction, normal, view, + albedo, metallic, roughness + ); + + lum = lum + (radiance * reflected); + } + + return vec4(lum, 1.0); +} diff --git a/src/shaders/basic_pbr.wgsl b/src/shaders/basic_pbr.wgsl new file mode 100644 index 0000000..c574d75 --- /dev/null +++ b/src/shaders/basic_pbr.wgsl @@ -0,0 +1,68 @@ +let PI: f32 = 3.141592; + +fn D_GGX(NoH: f32, roughness: f32) -> f32 { + let a = roughness * roughness; + let a2 = a * a; + let NoH2 = NoH * NoH; + let f = NoH * (a2 - 1.0) + 1.0; + return a2 / (PI * f * f); +} + +fn g1(NoV: f32, roughness: f32, k: f32) -> f32 { + let denom = NoV * (1.0 - k) + k; + return NoV / denom; +} + +fn G_SmithGGXCorrelated(NoV: f32, NoL: f32, roughness: f32) -> f32 { + let r = roughness + 1.0; + let k = (r * r) / 8.0; + + let g1l = g1(NoV, roughness, k); + let g1v = g1(NoL, roughness, k); + + return g1l * g1v; +} + +fn F_Schlick(u: f32, f0: vec3) -> vec3 { + let f = pow(1.0 - u, 5.0); + return f + f0 * (1.0 - f); +} + +fn BRDF( + l: vec3, // normalized light direction + n: vec3, // normalized surface normal + v: vec3, // normalized view direction + albedo: vec3, // surface albedo + metallic: f32, // surface metallic + roughness: f32, // surface roughness +) -> vec3 { + let h = normalize(v + l); + let NoL = max(dot(n, l), 0.0); + let NoV = max(dot(n, v), 0.0); + let NoH = max(dot(n, h), 0.0); + let LoH = max(dot(l, h), 0.0); + + // calculate reflectance at surface incidence + let f0 = mix(vec3(0.04), albedo, metallic); + + // specular BRDF + let D = D_GGX(NoH, roughness); + let G = G_SmithGGXCorrelated(NoV, NoL, roughness); + let F = F_Schlick(LoH, f0); + + let numerator = (D * G) * F; + let denominator = 4.0 * NoV * NoL; + + let Fr = numerator / max(denominator, 0.01); + + // diffuse BRDF + let diffuse_fresnel = + (vec3(1.0) - F_Schlick(NoL, f0)) * + (vec3(1.0) - F_Schlick(NoV, f0)); + + let lambertian = albedo / PI; + let Fd = diffuse_fresnel * lambertian; + + // TODO multiple scattering + return (Fr + Fd) * NoL; +} \ No newline at end of file diff --git a/src/shaders/basic_structs.wgsl b/src/shaders/basic_structs.wgsl new file mode 100644 index 0000000..310053c --- /dev/null +++ b/src/shaders/basic_structs.wgsl @@ -0,0 +1,35 @@ +struct CameraUniform { + eye: vec4; + vp: mat4x4; +}; + +struct MeshInstance { + transform: mat4x4; +}; + +struct MeshData { + instances: array; +}; + +struct PointLight { + center: vec4; + intensity: vec4; +}; + +struct PointLightData { + num: i32; + lights: array; +}; + +struct VertexInput { + [[location(0)]] position: vec3; + [[location(1)]] normal: vec3; + [[location(2)]] tex_coords: vec2; +}; + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4; + [[location(0)]] position: vec3; + [[location(1)]] normal: vec3; + [[location(2)]] tex_coords: vec2; +}; \ No newline at end of file diff --git a/src/shaders/tri_sampler.wgsl b/src/shaders/tri_sampler.wgsl new file mode 100644 index 0000000..bd018bb --- /dev/null +++ b/src/shaders/tri_sampler.wgsl @@ -0,0 +1,12 @@ +// Triplanar mapping sampler +fn TriSampler(tex: texture_2d, tex_sampler: sampler, position: vec3, normal: vec3, sharpness: f32) -> vec3 { + let uv_x = position.yz; + let uv_y = position.xz; + let uv_z = position.xy; + let tri_mask: vec3 = normalize(pow(abs(normal), vec3(sharpness))); + let sample_x = textureSample(tex, tex_sampler, uv_x).rgb; + let sample_y = textureSample(tex, tex_sampler, uv_y).rgb; + let sample_z = textureSample(tex, tex_sampler, uv_z).rgb; + + return (sample_x * tri_mask.x) + (sample_y * tri_mask.y) + (sample_z * tri_mask.z); +} \ No newline at end of file