Skip to content

Commit ade52cf

Browse files
committed
Add pbrbasic rust shader
1 parent e5b7541 commit ade52cf

File tree

6 files changed

+160
-0
lines changed

6 files changed

+160
-0
lines changed

shaders/rust/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

shaders/rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ members = [
9393
"imgui/ui",
9494
"imgui/scene",
9595
"texturemipmapgen/texture",
96+
"pbrbasic/pbr",
9697
]
9798

9899
[workspace.dependencies]

shaders/rust/pbrbasic/pbr.frag.spv

6.08 KB
Binary file not shown.

shaders/rust/pbrbasic/pbr.vert.spv

5.85 KB
Binary file not shown.

shaders/rust/pbrbasic/pbr/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "pbrbasic-pbr"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
spirv-std = { workspace = true }
8+
9+
[lib]
10+
crate-type = ["dylib"]

shaders/rust/pbrbasic/pbr/src/lib.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#![cfg_attr(target_arch = "spirv", no_std)]
2+
#![allow(clippy::missing_safety_doc)]
3+
4+
use spirv_std::{spirv, glam::{vec3, vec4, Mat3, Mat4, Vec3, Vec4, Vec4Swizzles}, num_traits::Float};
5+
use core::f32::consts::PI;
6+
7+
#[repr(C)]
8+
#[derive(Copy, Clone)]
9+
pub struct Ubo {
10+
pub projection: Mat4,
11+
pub model: Mat4,
12+
pub view: Mat4,
13+
pub cam_pos: Vec3,
14+
}
15+
16+
#[repr(C)]
17+
#[derive(Copy, Clone)]
18+
pub struct UboShared {
19+
pub lights: [Vec4; 4],
20+
}
21+
22+
#[repr(C)]
23+
#[derive(Copy, Clone)]
24+
pub struct VertexPushConsts {
25+
pub obj_pos: Vec3,
26+
}
27+
28+
#[repr(C)]
29+
#[derive(Copy, Clone)]
30+
pub struct FragmentPushConsts {
31+
pub _padding: [f32; 3], // offset 0-11 to match GLSL offset 12
32+
pub roughness: f32, // offset 12
33+
pub metallic: f32, // offset 16
34+
pub r: f32, // offset 20
35+
pub g: f32, // offset 24
36+
pub b: f32, // offset 28
37+
}
38+
39+
#[spirv(vertex)]
40+
pub fn main_vs(
41+
in_pos: Vec3,
42+
in_normal: Vec3,
43+
#[spirv(uniform, descriptor_set = 0, binding = 0)] ubo: &Ubo,
44+
#[spirv(push_constant)] push_consts: &VertexPushConsts,
45+
#[spirv(position)] out_position: &mut Vec4,
46+
out_world_pos: &mut Vec3,
47+
out_normal: &mut Vec3,
48+
) {
49+
let loc_pos = (ubo.model * vec4(in_pos.x, in_pos.y, in_pos.z, 1.0)).xyz();
50+
*out_world_pos = loc_pos + push_consts.obj_pos;
51+
*out_normal = Mat3::from_mat4(ubo.model) * in_normal;
52+
*out_position = ubo.projection * ubo.view * vec4(out_world_pos.x, out_world_pos.y, out_world_pos.z, 1.0);
53+
}
54+
55+
fn material_color(material: &FragmentPushConsts) -> Vec3 {
56+
vec3(material.r, material.g, material.b)
57+
}
58+
59+
// Normal Distribution function
60+
fn d_ggx(dot_nh: f32, roughness: f32) -> f32 {
61+
let alpha = roughness * roughness;
62+
let alpha2 = alpha * alpha;
63+
let denom = dot_nh * dot_nh * (alpha2 - 1.0) + 1.0;
64+
alpha2 / (PI * denom * denom)
65+
}
66+
67+
// Geometric Shadowing function
68+
fn g_schlicksmith_ggx(dot_nl: f32, dot_nv: f32, roughness: f32) -> f32 {
69+
let r = roughness + 1.0;
70+
let k = (r * r) / 8.0;
71+
let gl = dot_nl / (dot_nl * (1.0 - k) + k);
72+
let gv = dot_nv / (dot_nv * (1.0 - k) + k);
73+
gl * gv
74+
}
75+
76+
// Fresnel function
77+
fn f_schlick(cos_theta: f32, metallic: f32, material: &FragmentPushConsts) -> Vec3 {
78+
let f0 = vec3(0.04, 0.04, 0.04).lerp(material_color(material), metallic);
79+
f0 + (vec3(1.0, 1.0, 1.0) - f0) * (1.0 - cos_theta).powf(5.0)
80+
}
81+
82+
// Specular BRDF composition
83+
fn brdf(l: Vec3, v: Vec3, n: Vec3, metallic: f32, roughness: f32, material: &FragmentPushConsts) -> Vec3 {
84+
// Precalculate vectors and dot products
85+
let h = (v + l).normalize();
86+
let dot_nv = n.dot(v).clamp(0.0, 1.0);
87+
let dot_nl = n.dot(l).clamp(0.0, 1.0);
88+
let _dot_lh = l.dot(h).clamp(0.0, 1.0);
89+
let dot_nh = n.dot(h).clamp(0.0, 1.0);
90+
91+
// Light color fixed
92+
let light_color = vec3(1.0, 1.0, 1.0);
93+
94+
let mut color = vec3(0.0, 0.0, 0.0);
95+
96+
if dot_nl > 0.0 {
97+
let rroughness = roughness.max(0.05);
98+
// D = Normal distribution (Distribution of the microfacets)
99+
let d = d_ggx(dot_nh, roughness);
100+
// G = Geometric shadowing term (Microfacets shadowing)
101+
let g = g_schlicksmith_ggx(dot_nl, dot_nv, rroughness);
102+
// F = Fresnel factor (Reflectance depending on angle of incidence)
103+
let f = f_schlick(dot_nv, metallic, material);
104+
105+
let spec = d * f * g / (4.0 * dot_nl * dot_nv);
106+
107+
color += spec * dot_nl * light_color;
108+
}
109+
110+
color
111+
}
112+
113+
#[spirv(fragment)]
114+
pub fn main_fs(
115+
in_world_pos: Vec3,
116+
in_normal: Vec3,
117+
#[spirv(uniform, descriptor_set = 0, binding = 0)] ubo: &Ubo,
118+
#[spirv(uniform, descriptor_set = 0, binding = 1)] ubo_params: &UboShared,
119+
#[spirv(push_constant)] material: &FragmentPushConsts,
120+
out_color: &mut Vec4,
121+
) {
122+
let n = in_normal.normalize();
123+
let v = (ubo.cam_pos - in_world_pos).normalize();
124+
125+
let roughness = material.roughness;
126+
127+
// Specular contribution
128+
let mut lo = vec3(0.0, 0.0, 0.0);
129+
for i in 0..4 {
130+
let l = (ubo_params.lights[i].xyz() - in_world_pos).normalize();
131+
lo += brdf(l, v, n, material.metallic, roughness, material);
132+
}
133+
134+
// Combine with ambient
135+
let mut color = material_color(material) * 0.02;
136+
color += lo;
137+
138+
// Gamma correct
139+
color = color.powf(0.4545);
140+
141+
*out_color = vec4(color.x, color.y, color.z, 1.0);
142+
}

0 commit comments

Comments
 (0)