commit 1494036a2c88de72e059e729880321db83593b55
parent 1392f56b6a10e64db00d73e6b24a12f76ed8d57d
Author: Christian Ermann <christianermann@gmail.com>
Date: Fri, 12 Jul 2024 15:40:54 -0400
Split MeshRenderPipeline into separate file and embed shaders from file
Diffstat:
4 files changed, 381 insertions(+), 392 deletions(-)
diff --git a/src/main.zig b/src/main.zig
@@ -6,7 +6,7 @@ const builtin = @import("builtin");
const RenderPass = @import("render_pass.zig").RenderPass;
const ForwardScreenPass = @import("render_pass.zig").ForwardScreenPass;
const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
-const MeshRenderPipeline = @import("render_pipeline.zig").MeshRenderPipeline;
+const MeshRenderPipeline = @import("mesh_render_pipeline.zig");
const SkyBoxRenderPipeline = @import("render_pipeline.zig").SkyBoxRenderPipeline;
const CubeMap = @import("cubemap.zig");
const Textures = @import("textures.zig");
@@ -613,7 +613,7 @@ pub fn main() !void {
//.offset = .{ 0, -0.5, 0 },
});
- var rp = try MeshRenderPipeline.init(&app, &mesh_buffer, &textures);
+ var rp = try MeshRenderPipeline.init(&app, &mesh_buffer, &textures, "shaders/mesh_pbr.wgsl");
const material = try Material.init(
&textures,
diff --git a/src/mesh_render_pipeline.zig b/src/mesh_render_pipeline.zig
@@ -0,0 +1,187 @@
+const std = @import("std");
+const gpu = @import("mach_gpu");
+
+const App = @import("main.zig").App;
+const MeshBuffer = @import("main.zig").MeshBuffer;
+const UniformData = @import("main.zig").UniformData;
+const f32x4 = @import("main.zig").f32x4;
+const mat4 = @import("main.zig").mat4;
+const CubeMap = @import("cubemap.zig");
+const Textures = @import("textures.zig");
+const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
+
+const MeshRenderPipeline = @This();
+
+gpu_pipeline: *gpu.RenderPipeline,
+mesh_buffer: *const MeshBuffer,
+bind_group: *gpu.BindGroup,
+uniform_buffer: *gpu.Buffer,
+
+pub fn init(
+ app: *App,
+ mesh_buffer: *const MeshBuffer,
+ textures: *const Textures,
+ comptime shader_path: []const u8,
+) !MeshRenderPipeline {
+ const src = @embedFile(shader_path);
+
+ const module = app.device.createShaderModuleWGSL("mesh shader", src);
+ defer module.release();
+
+ const layouts = MeshRenderPipeline.bufferLayouts();
+ const vertex = gpu.VertexState.init(.{
+ .module = module,
+ .entry_point = "vertex",
+ .buffers = &layouts,
+ });
+
+ const blend: gpu.BlendState = .{};
+ const color_target = gpu.ColorTargetState{
+ .format = app.swap_chain.format,
+ .blend = &blend,
+ .write_mask = gpu.ColorWriteMaskFlags.all,
+ };
+ const fragment = gpu.FragmentState.init(.{
+ .module = module,
+ .entry_point = "fragment",
+ .targets = &.{color_target},
+ });
+
+ const buffer_descriptor = gpu.Buffer.Descriptor{
+ .size = @sizeOf(UniformData),
+ .usage = .{ .uniform = true, .copy_dst = true },
+ .mapped_at_creation = .false,
+ };
+ const uniform_buffer = app.device.createBuffer(&buffer_descriptor);
+
+ const bind_group_layout = MeshRenderPipeline.bindGroupLayout(app.device);
+ const bind_group_descriptor = gpu.BindGroup.Descriptor.init(.{
+ .layout = bind_group_layout,
+ .entries = &.{
+ gpu.BindGroup.Entry.buffer(0, mesh_buffer.instances.data, 0, mesh_buffer.instances.size),
+ gpu.BindGroup.Entry.buffer(1, uniform_buffer, 0, @sizeOf(UniformData)),
+ gpu.BindGroup.Entry.sampler(2, textures.sampler),
+ gpu.BindGroup.Entry.textureView(3, textures.view),
+ },
+ });
+ const bind_group = app.device.createBindGroup(&bind_group_descriptor);
+
+ const pipeline_layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
+ .bind_group_layouts = &.{bind_group_layout},
+ });
+ const pipeline_layout = app.device.createPipelineLayout(&pipeline_layout_descriptor);
+ defer pipeline_layout.release();
+
+ const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
+ .label = "mesh render pipeline",
+ .fragment = &fragment,
+ .layout = pipeline_layout,
+ .depth_stencil = &.{
+ .format = .depth24_plus,
+ .depth_write_enabled = .true,
+ .depth_compare = .less,
+ },
+ .vertex = vertex,
+ .multisample = .{},
+ .primitive = .{
+ .topology = .triangle_list,
+ .front_face = .ccw,
+ .cull_mode = .back,
+ },
+ };
+
+ return .{
+ .gpu_pipeline = app.device.createRenderPipeline(&pipeline_descriptor),
+ .mesh_buffer = mesh_buffer,
+ .bind_group = bind_group,
+ .uniform_buffer = uniform_buffer,
+ };
+}
+
+fn bufferLayouts() [5]gpu.VertexBufferLayout {
+ const positions = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x4, .shader_location = 0, .offset = 0 },
+ },
+ });
+ const normals = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x3, .shader_location = 1, .offset = 0 },
+ },
+ });
+ const tex_coords = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x3, .shader_location = 2, .offset = 0 },
+ },
+ });
+ const tangents = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x3, .shader_location = 3, .offset = 0 },
+ },
+ });
+ const bitangents = gpu.VertexBufferLayout.init(.{
+ .array_stride = @sizeOf(f32x4),
+ .step_mode = .vertex,
+ .attributes = &.{
+ .{ .format = .float32x3, .shader_location = 4, .offset = 0 },
+ },
+ });
+ return .{ positions, normals, tex_coords, tangents, bitangents };
+}
+
+fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
+ const descriptor = gpu.BindGroupLayout.Descriptor.init(.{
+ .entries = &.{
+ gpu.BindGroupLayout.Entry.buffer(0, .{
+ .vertex = true,
+ .fragment = true,
+ }, .read_only_storage, false, 0),
+ gpu.BindGroupLayout.Entry.buffer(1, .{
+ .vertex = true,
+ .fragment = false,
+ }, .uniform, false, 0),
+ gpu.BindGroupLayout.Entry.sampler(2, .{
+ .vertex = false,
+ .fragment = true,
+ }, .filtering),
+ gpu.BindGroupLayout.Entry.texture(3, .{
+ .vertex = false,
+ .fragment = true,
+ }, .float, .dimension_2d_array, false),
+ },
+ });
+ return device.createBindGroupLayout(&descriptor);
+}
+
+pub fn frame(ptr: *anyopaque, pass: *gpu.RenderPassEncoder) void {
+ const self: *MeshRenderPipeline = @ptrCast(@alignCast(ptr));
+ pass.setPipeline(self.gpu_pipeline);
+
+ pass.setVertexBuffer(0, self.mesh_buffer.positions.data, 0, self.mesh_buffer.positions.size);
+ pass.setVertexBuffer(1, self.mesh_buffer.normals.data, 0, self.mesh_buffer.normals.size);
+ pass.setVertexBuffer(2, self.mesh_buffer.tex_coords.data, 0, self.mesh_buffer.tex_coords.size);
+ pass.setVertexBuffer(3, self.mesh_buffer.tangents.data, 0, self.mesh_buffer.tangents.size);
+ pass.setVertexBuffer(4, self.mesh_buffer.bitangents.data, 0, self.mesh_buffer.bitangents.size);
+ pass.setBindGroup(0, self.bind_group, null);
+ pass.setIndexBuffer(self.mesh_buffer.indices.data, .uint32, 0, self.mesh_buffer.indices.size);
+
+ for (0..self.mesh_buffer.meshes.items.len) |mesh_idx| {
+ const offset = mesh_idx * 5 * @sizeOf(u32);
+ pass.drawIndexedIndirect(self.mesh_buffer.indirect.data, offset);
+ }
+}
+
+pub fn pipeline(self: *MeshRenderPipeline) RenderPipeline {
+ return .{
+ .ptr = self,
+ .frameFn = frame,
+ };
+}
diff --git a/src/render_pipeline.zig b/src/render_pipeline.zig
@@ -91,396 +91,6 @@ pub const TriangleRenderPipeline = struct {
}
};
-pub const MeshRenderPipeline = struct {
- gpu_pipeline: *gpu.RenderPipeline,
- mesh_buffer: *const MeshBuffer,
- bind_group: *gpu.BindGroup,
- uniform_buffer: *gpu.Buffer,
-
- pub fn init(
- app: *App,
- mesh_buffer: *const MeshBuffer,
- textures: *const Textures,
- ) !MeshRenderPipeline {
- const vs =
- \\ struct VertexInput {
- \\ @location(0) position: vec4<f32>,
- \\ @location(1) normal: vec3<f32>,
- \\ @location(2) tex_coord: vec3<f32>,
- \\ @location(3) tangent: vec3<f32>,
- \\ @location(4) bitangent: vec3<f32>,
- \\ };
- \\
- \\ struct VertexOutput {
- \\ @builtin(position) clip_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(4) @interpolate(flat) instance_id: u32,
- \\ };
- \\
- \\ @group(0) @binding(0)
- \\ var<storage, read> instances: array<Instance>;
- \\ struct Instance {
- \\ model_matrix: mat4x4<f32>,
- \\ material: Material,
- \\ };
- \\ struct Material {
- \\ albedo: u32,
- \\ normal: u32,
- \\ };
- \\
- \\ @group(0) @binding(1)
- \\ var<uniform> uniform_data: UniformData;
- \\ struct UniformData {
- \\ pw_camera: vec3<f32>,
- \\ view_matrix: mat4x4<f32>,
- \\ proj_matrix: mat4x4<f32>,
- \\ };
- \\
- \\ @vertex fn main(
- \\ in: VertexInput,
- \\ @builtin(instance_index) idx: u32,
- \\ ) -> VertexOutput {
- \\ let model_matrix = instances[idx].model_matrix;
- \\ let camera_matrix = uniform_data.proj_matrix * uniform_data.view_matrix;
- \\
- \\ 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,
- \\ tangent_position,
- \\ tangent_light_position,
- \\ pt_camera,
- \\ idx,
- \\ );
- \\ }
- ;
- const vs_module = app.device.createShaderModuleWGSL("default vertex shader", vs);
- defer vs_module.release();
-
- const layouts = MeshRenderPipeline.bufferLayouts();
- const vertex = gpu.VertexState.init(.{
- .module = vs_module,
- .entry_point = "main",
- .buffers = &layouts,
- });
-
- const fs =
- \\ struct VertexOutput {
- \\ @builtin(position) position: vec4<f32>,
- \\ @location(0) tex_coord: 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,
- \\ };
- \\
- \\ @group(0) @binding(0)
- \\ var<storage, read> instances: array<Instance>;
- \\ struct Instance {
- \\ model_matrix: mat4x4<f32>,
- \\ material: Material,
- \\ };
- \\ 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);
- \\ 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));
- \\
- \\ return vec4<f32>(c, 1.0);
- \\ }
- ;
- const fs_module = app.device.createShaderModuleWGSL("default fragment shader", fs);
- defer fs_module.release();
-
- const blend: gpu.BlendState = .{};
- const color_target = gpu.ColorTargetState{
- .format = app.swap_chain.format,
- .blend = &blend,
- .write_mask = gpu.ColorWriteMaskFlags.all,
- };
- const fragment = gpu.FragmentState.init(.{
- .module = fs_module,
- .entry_point = "main",
- .targets = &.{color_target},
- });
-
- const buffer_descriptor = gpu.Buffer.Descriptor{
- .size = @sizeOf(UniformData),
- .usage = .{ .uniform = true, .copy_dst = true },
- .mapped_at_creation = .false,
- };
- const uniform_buffer = app.device.createBuffer(&buffer_descriptor);
-
- const bind_group_layout = MeshRenderPipeline.bindGroupLayout(app.device);
- const bind_group_descriptor = gpu.BindGroup.Descriptor.init(.{
- .layout = bind_group_layout,
- .entries = &.{
- gpu.BindGroup.Entry.buffer(0, mesh_buffer.instances.data, 0, mesh_buffer.instances.size),
- gpu.BindGroup.Entry.buffer(1, uniform_buffer, 0, @sizeOf(UniformData)),
- gpu.BindGroup.Entry.sampler(2, textures.sampler),
- gpu.BindGroup.Entry.textureView(3, textures.view),
- },
- });
- const bind_group = app.device.createBindGroup(&bind_group_descriptor);
-
- const pipeline_layout_descriptor = gpu.PipelineLayout.Descriptor.init(.{
- .bind_group_layouts = &.{bind_group_layout},
- });
- const pipeline_layout = app.device.createPipelineLayout(&pipeline_layout_descriptor);
- defer pipeline_layout.release();
-
- const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
- .label = "default render pipeline",
- .fragment = &fragment,
- .layout = pipeline_layout,
- .depth_stencil = &.{
- .format = .depth24_plus,
- .depth_write_enabled = .true,
- .depth_compare = .less,
- },
- .vertex = vertex,
- .multisample = .{},
- .primitive = .{
- .topology = .triangle_list,
- .front_face = .ccw,
- .cull_mode = .back,
- },
- };
-
- return .{
- .gpu_pipeline = app.device.createRenderPipeline(&pipeline_descriptor),
- .mesh_buffer = mesh_buffer,
- .bind_group = bind_group,
- .uniform_buffer = uniform_buffer,
- };
- }
-
- fn bufferLayouts() [5]gpu.VertexBufferLayout {
- const positions = gpu.VertexBufferLayout.init(.{
- .array_stride = @sizeOf(f32x4),
- .step_mode = .vertex,
- .attributes = &.{
- .{ .format = .float32x4, .shader_location = 0, .offset = 0 },
- },
- });
- const normals = gpu.VertexBufferLayout.init(.{
- .array_stride = @sizeOf(f32x4),
- .step_mode = .vertex,
- .attributes = &.{
- .{ .format = .float32x3, .shader_location = 1, .offset = 0 },
- },
- });
- const tex_coords = gpu.VertexBufferLayout.init(.{
- .array_stride = @sizeOf(f32x4),
- .step_mode = .vertex,
- .attributes = &.{
- .{ .format = .float32x3, .shader_location = 2, .offset = 0 },
- },
- });
- const tangents = gpu.VertexBufferLayout.init(.{
- .array_stride = @sizeOf(f32x4),
- .step_mode = .vertex,
- .attributes = &.{
- .{ .format = .float32x3, .shader_location = 3, .offset = 0 },
- },
- });
- const bitangents = gpu.VertexBufferLayout.init(.{
- .array_stride = @sizeOf(f32x4),
- .step_mode = .vertex,
- .attributes = &.{
- .{ .format = .float32x3, .shader_location = 4, .offset = 0 },
- },
- });
- return .{ positions, normals, tex_coords, tangents, bitangents };
- }
-
- fn bindGroupLayout(device: *gpu.Device) *gpu.BindGroupLayout {
- const descriptor = gpu.BindGroupLayout.Descriptor.init(.{
- .entries = &.{
- gpu.BindGroupLayout.Entry.buffer(0, .{
- .vertex = true,
- .fragment = true,
- }, .read_only_storage, false, 0),
- gpu.BindGroupLayout.Entry.buffer(1, .{
- .vertex = true,
- .fragment = false,
- }, .uniform, false, 0),
- gpu.BindGroupLayout.Entry.sampler(2, .{
- .vertex = false,
- .fragment = true,
- }, .filtering),
- gpu.BindGroupLayout.Entry.texture(3, .{
- .vertex = false,
- .fragment = true,
- }, .float, .dimension_2d_array, false),
- },
- });
- return device.createBindGroupLayout(&descriptor);
- }
-
- pub fn frame(ptr: *anyopaque, pass: *gpu.RenderPassEncoder) void {
- const self: *MeshRenderPipeline = @ptrCast(@alignCast(ptr));
- pass.setPipeline(self.gpu_pipeline);
-
- pass.setVertexBuffer(0, self.mesh_buffer.positions.data, 0, self.mesh_buffer.positions.size);
- pass.setVertexBuffer(1, self.mesh_buffer.normals.data, 0, self.mesh_buffer.normals.size);
- pass.setVertexBuffer(2, self.mesh_buffer.tex_coords.data, 0, self.mesh_buffer.tex_coords.size);
- pass.setVertexBuffer(3, self.mesh_buffer.tangents.data, 0, self.mesh_buffer.tangents.size);
- pass.setVertexBuffer(4, self.mesh_buffer.bitangents.data, 0, self.mesh_buffer.bitangents.size);
- pass.setBindGroup(0, self.bind_group, null);
- pass.setIndexBuffer(self.mesh_buffer.indices.data, .uint32, 0, self.mesh_buffer.indices.size);
-
- for (0..self.mesh_buffer.meshes.items.len) |mesh_idx| {
- const offset = mesh_idx * 5 * @sizeOf(u32);
- pass.drawIndexedIndirect(self.mesh_buffer.indirect.data, offset);
- }
- }
-
- pub fn pipeline(self: *MeshRenderPipeline) RenderPipeline {
- return .{
- .ptr = self,
- .frameFn = frame,
- };
- }
-};
-
pub const SkyBoxRenderPipeline = struct {
gpu_pipeline: *gpu.RenderPipeline,
uniform_buffer: *gpu.Buffer,
diff --git a/src/shaders/mesh_pbr.wgsl b/src/shaders/mesh_pbr.wgsl
@@ -0,0 +1,192 @@
+struct VertexInput {
+ @location(0) position: vec4<f32>,
+ @location(1) normal: vec3<f32>,
+ @location(2) tex_coord: vec3<f32>,
+ @location(3) tangent: vec3<f32>,
+ @location(4) bitangent: vec3<f32>,
+};
+
+struct VertexOutput {
+ @builtin(position) clip_position: vec4<f32>,
+ @location(0) tex_coord: 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,
+};
+
+@group(0) @binding(0)
+var<storage, read> instances: array<Instance>;
+struct Instance {
+ model_matrix: mat4x4<f32>,
+ material: Material,
+};
+struct Material {
+ albedo: u32,
+ normal: u32,
+ roughness: u32,
+ metalness: u32,
+};
+
+@group(0) @binding(1)
+var<uniform> uniform_data: UniformData;
+struct UniformData {
+ pw_camera: vec3<f32>,
+ view_matrix: mat4x4<f32>,
+ proj_matrix: mat4x4<f32>,
+};
+
+@vertex fn vertex(
+ in: VertexInput,
+ @builtin(instance_index) idx: u32,
+) -> VertexOutput {
+ let model_matrix = instances[idx].model_matrix;
+ let camera_matrix = uniform_data.proj_matrix * uniform_data.view_matrix;
+
+ 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,
+ tangent_position,
+ tangent_light_position,
+ pt_camera,
+ idx,
+ );
+}
+
+
+@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 fragment(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);
+ 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));
+
+ return vec4<f32>(c, 1.0);
+}