commit bbf61fdd93fb8e5a352d58fd7577bc86a52caa54
parent 4fdfd4f1ecc51530bfe22e21e92c7890baf7a143
Author: Christian Ermann <christianermann@gmail.com>
Date: Wed, 10 Jul 2024 19:20:44 -0400
Add PBR mesh shader
Diffstat:
3 files changed, 148 insertions(+), 14 deletions(-)
diff --git a/src/main.zig b/src/main.zig
@@ -292,6 +292,7 @@ const InstanceData = struct {
};
pub const UniformData = struct {
+ pw_camera: f32x3,
view_matrix: mat4,
proj_matrix: mat4,
};
@@ -515,7 +516,7 @@ pub const Mesh = struct {
.{ 0, 0, self.scale, 0 },
.{ self.offset[0], self.offset[1], self.offset[2], 1 },
},
- .material = .{ .albedo = 0, .normal = 1 },
+ .material = .{ .albedo = 0, .normal = 1, .roughness = 2, .metalness = 3 },
};
}
};
@@ -563,7 +564,7 @@ pub fn main() !void {
_ = try load_obj.loadFile(.{
.allocator = allocator,
.mesh_buffer = &mesh_buffer,
- .path = "cottage/cottage-fixed.obj",
+ .path = "Cottage_Clean/cottage.obj",
.scale = 1,
.offset = .{ 0, -5, 15 },
});
@@ -579,8 +580,10 @@ pub fn main() !void {
_ = try Material.init(
&textures,
- "cottage/cottage_diffuse.png",
- "cottage/cottage_normal-fixed.png",
+ "Cottage_Clean/Cottage_Clean_Base_Color_fixed.png",
+ "Cottage_Clean/Cottage_Clean_Normal_fixed.png",
+ "Cottage_Clean/Cottage_Clean_Roughness_fixed.png",
+ "Cottage_Clean/Cottage_Clean_Metallic_fixed.png",
app.queue,
);
@@ -596,6 +599,7 @@ pub fn main() !void {
try app.inputs.append(camera.input.userInput());
var uniform_data = UniformData{
+ .pw_camera = undefined,
.view_matrix = undefined,
.proj_matrix = undefined,
};
@@ -611,6 +615,7 @@ pub fn main() !void {
camera.update(0.01);
camera.view(&uniform_data.view_matrix);
camera.proj(&uniform_data.proj_matrix);
+ uniform_data.pw_camera = camera.position;
app.queue.writeBuffer(rp.uniform_buffer, 0, &[1]UniformData{uniform_data});
camera.invView(&skybox_uniform.inv_view);
diff --git a/src/material.zig b/src/material.zig
@@ -4,6 +4,8 @@ const Textures = @import("textures.zig");
albedo: u32,
normal: u32,
+roughness: u32,
+metalness: u32,
const Self = @This();
@@ -11,12 +13,18 @@ pub fn init(
textures: *Textures,
albedo_path: []const u8,
normal_path: []const u8,
+ roughness_path: []const u8,
+ metalness_path: []const u8,
queue: *gpu.Queue,
) !Self {
const albedo = try textures.loadFile(albedo_path, queue);
const normal = try textures.loadFile(normal_path, queue);
+ const roughness = try textures.loadFile(roughness_path, queue);
+ const metalness = try textures.loadFile(metalness_path, queue);
return .{
.albedo = albedo,
.normal = normal,
+ .roughness = roughness,
+ .metalness = metalness,
};
}
diff --git a/src/render_pipeline.zig b/src/render_pipeline.zig
@@ -134,6 +134,7 @@ pub const MeshRenderPipeline = struct {
\\ @group(0) @binding(1)
\\ var<uniform> uniform_data: UniformData;
\\ struct UniformData {
+ \\ pw_camera: vec3<f32>,
\\ view_matrix: mat4x4<f32>,
\\ proj_matrix: mat4x4<f32>,
\\ };
@@ -148,15 +149,22 @@ pub const MeshRenderPipeline = struct {
\\ let t = normalize((model_matrix * vec4(in.tangent, 0.0)).xyz);
\\ let b = normalize((model_matrix * vec4(in.bitangent, 0.0)).xyz);
\\ let n = normalize((model_matrix * vec4(in.normal, 0.0)).xyz);
+ \\ let tangent_matrix = transpose(mat3x3<f32>(t, b, n));
\\
\\ let world_position = model_matrix * in.position;
+ \\ let tangent_position = tangent_matrix * world_position.xyz;
+ \\
+ \\ let light_position = vec3<f32>(20, 80, 20);
+ \\ let tangent_light_position = tangent_matrix * light_position;
+ \\
+ \\ let pt_camera = tangent_matrix * uniform_data.pw_camera;
\\
\\ return VertexOutput(
\\ camera_matrix * world_position,
\\ in.tex_coord,
- \\ t,
- \\ b,
- \\ n,
+ \\ tangent_position,
+ \\ tangent_light_position,
+ \\ pt_camera,
\\ idx,
\\ );
\\ }
@@ -175,9 +183,9 @@ pub const MeshRenderPipeline = struct {
\\ struct VertexOutput {
\\ @builtin(position) position: vec4<f32>,
\\ @location(0) tex_coord: vec3<f32>,
- \\ @location(1) t: vec3<f32>,
- \\ @location(2) b: vec3<f32>,
- \\ @location(3) n: vec3<f32>,
+ \\ @location(1) pt_fragment: vec3<f32>,
+ \\ @location(2) pt_light: vec3<f32>,
+ \\ @location(3) pt_camera: vec3<f32>,
\\ @location(4) @interpolate(flat) instance_id: u32,
\\ };
\\
@@ -190,19 +198,132 @@ pub const MeshRenderPipeline = struct {
\\ struct Material {
\\ albedo: u32,
\\ normal: u32,
+ \\ roughness: u32,
+ \\ metalness: u32,
\\ };
\\
\\ @group(0) @binding(2) var material_sampler: sampler;
\\ @group(0) @binding(3) var material_texture: texture_2d_array<f32>;
\\
+ \\ fn diffuse_lambert(albedo: vec3<f32>) -> vec3<f32> {
+ \\ return albedo * 0.31830988618;
+ \\ }
+ \\
+ \\ fn distribution_trowbridge_reitz_ggx(
+ \\ n_dot_h: f32,
+ \\ alpha: f32,
+ \\ ) -> f32 {
+ \\ let alpha_squared = alpha * alpha;
+ \\ let n_dot_h_squared = n_dot_h * n_dot_h;
+ \\ let denom = n_dot_h_squared * (alpha_squared - 1) + 1;
+ \\ let denom_squared = denom * denom;
+ \\ return alpha_squared / (3.14159265359 * denom_squared);
+ \\ }
+ \\
+ \\ fn geometry_schlick_ggx(
+ \\ n_dot_x: f32,
+ \\ alpha: f32,
+ \\ ) -> f32 {
+ \\ let k = 0.5 * alpha;
+ \\ let denom = n_dot_x * (1.0 - k) + k;
+ \\ return n_dot_x / denom;
+ \\ }
+ \\
+ \\ fn geometry_smith(
+ \\ n_dot_l: f32,
+ \\ n_dot_v: f32,
+ \\ alpha: f32,
+ \\ ) -> f32 {
+ \\ let g1 = geometry_schlick_ggx(n_dot_l, alpha);
+ \\ let g2 = geometry_schlick_ggx(n_dot_v, alpha);
+ \\ return g1 * g2;
+ \\ }
+ \\
+ \\ fn fresnel_schlick(
+ \\ f0: vec3<f32>,
+ \\ v_dot_h: f32,
+ \\ ) -> vec3<f32> {
+ \\ return f0 + (1.0 - f0) * pow(1.0 - v_dot_h, 5.0);
+ \\ }
+ \\
+ \\ fn cook_torrance(
+ \\ n_dot_v: f32,
+ \\ n_dot_l: f32,
+ \\ n_dot_h: f32,
+ \\ roughness: f32,
+ \\ ) -> f32 {
+ \\ let alpha = roughness * roughness;
+ \\ let distribution = distribution_trowbridge_reitz_ggx(
+ \\ n_dot_h,
+ \\ alpha,
+ \\ );
+ \\ let geometry = geometry_smith(
+ \\ n_dot_l,
+ \\ n_dot_v,
+ \\ alpha,
+ \\ );
+ \\ let denom = 4.0 * n_dot_v * n_dot_l + 0.000001;
+ \\ return (distribution * geometry) / denom;
+ \\ }
+ \\
+ \\ fn lighting(
+ \\ normal: vec3<f32>,
+ \\ p_fragment: vec3<f32>,
+ \\ p_camera: vec3<f32>,
+ \\ p_light: vec3<f32>,
+ \\ c_light: vec3<f32>,
+ \\ albedo: vec3<f32>,
+ \\ roughness: f32,
+ \\ metalness: f32,
+ \\ ) -> vec3<f32> {
+ \\ let v = normalize(p_camera - p_fragment);
+ \\ let l = normalize(p_light - p_fragment);
+ \\ let h = normalize(v + l);
+ \\
+ \\ let n_dot_v = max(dot(normal, v), 0.0);
+ \\ let n_dot_l = max(dot(normal, l), 0.0);
+ \\ let n_dot_h = max(dot(normal, h), 0.0);
+ \\ let v_dot_h = max(dot(v, h), 0.0);
+ \\
+ \\ let f0 = mix(vec3<f32>(0.04), albedo, metalness);
+ \\ let k_s = fresnel_schlick(f0, v_dot_h);
+ \\ let k_d = 1.0 - k_s;
+ \\
+ \\ let diffuse = k_d * diffuse_lambert(albedo);
+ \\ let specular = k_s * cook_torrance(n_dot_v, n_dot_l, n_dot_h, roughness);
+ \\ let brdf = (diffuse + specular) * n_dot_l;
+ \\ return brdf * c_light;
+ \\ }
+ \\
\\ @fragment fn main(in: VertexOutput) -> @location(0) vec4<f32> {
\\ let material = instances[in.instance_id].material;
\\ let uv = vec2<f32>(in.tex_coord.x, 1 - in.tex_coord.y);
- \\ let albedo = textureSample(material_texture, material_sampler, uv, material.albedo);
+ \\ var albedo = textureSample(material_texture, material_sampler, uv, material.albedo).rgb;
+ \\ albedo = pow(albedo, vec3<f32>(2.2));
+ \\ var normal = textureSample(material_texture, material_sampler, uv, material.normal).rgb;
+ \\ normal = normalize(normal * 2.0 - 1.0);
+ \\ var roughness = textureSample(material_texture, material_sampler, uv, material.roughness).r;
+ \\ var metalness = textureSample(material_texture, material_sampler, uv, material.metalness).r;
+ \\
+ \\ let light_color = normalize(vec3<f32>(23.47, 21.31, 20.79));
+ \\ var c = lighting(
+ \\ normal,
+ \\ in.pt_fragment,
+ \\ in.pt_camera,
+ \\ in.pt_light,
+ \\ light_color,
+ \\ albedo.rgb,
+ \\ roughness,
+ \\ metalness,
+ \\ );
+ \\
+ \\ let ambient = vec3<f32>(0.03) * albedo;
+ \\ c += ambient;
+ \\
+ \\ c = c / (c + 1.0);
+ \\ c = pow(c, vec3<f32>(1.0/2.2));
\\
- \\ let tangent_matrix = mat3x3<f32>(in.t, in.b, in.n);
- \\ let normal = textureSample(material_texture, material_sampler, uv, material.normal);
- \\ return vec4<f32>((tangent_matrix * normal.rgb + 1.0) * 0.5, 1.0);
+ \\ return vec4<f32>(c, 1.0);
\\ }
;
const fs_module = app.device.createShaderModuleWGSL("default fragment shader", fs);