commit 4de3a72e087469ad6f9afcd22bea3b0de18c49b9
parent e0dffa92a68c08dda96c7b8974f8b4b6f0c746a1
Author: Christian Ermann <christian.ermann@joescan.com>
Date: Wed, 16 Jul 2025 21:57:46 -0700
Add perspective camera
Diffstat:
4 files changed, 75 insertions(+), 22 deletions(-)
diff --git a/src/math.zig b/src/math.zig
@@ -178,7 +178,7 @@ pub fn lookAtLH(eye: Vec4, target: Vec4, up: Vec4) Mat4 {
return lookToLH(eye, target - eye, up);
}
-fn perspectiveLH(fovy: f32, aspect: f32, near: f32, far: f32) Mat4 {
+pub fn perspectiveLH(fovy: f32, aspect: f32, near: f32, far: f32) Mat4 {
// we'll split the formation of our perspective matrix into 5 steps:
// 1. translate the frustum apex to (0, 0, 0)
// 2. map depth to (NDC_Z_MIN, NDC_Z_MAX) or (NDC_Z_MAX, NDC_Z_MIN)
@@ -450,7 +450,7 @@ test "perspectiveLH" {
try std.testing.expectApproxEqAbs(1.0, far_tr[3], 0.000001);
}
-fn invPerspectiveLH(fovy: f32, aspect: f32, near: f32, far: f32) Mat4 {
+pub fn invPerspectiveLH(fovy: f32, aspect: f32, near: f32, far: f32) Mat4 {
const inv_a = (far - near) / (near * far);
const b = near / (near - far);
const tan_half_fov = @tan(std.math.degreesToRadians(fovy * 0.5));
diff --git a/src/shader-sandbox/camera.zig b/src/shader-sandbox/camera.zig
@@ -0,0 +1,58 @@
+const std = @import("std");
+const math = @import("math");
+
+pub const PerspectiveCamera = struct {
+ fovy: f32 = 60,
+ aspect: f32 = 1,
+ near: f32 = 0.01,
+ far: f32 = 100,
+ yaw: f32 = 90.0,
+ pitch: f32 = 0.0,
+ position: math.Vec4 = .{ 0, 0, 0, 1 },
+
+ pub fn view(self: *const PerspectiveCamera) math.Mat4 {
+ const cos_yaw = @cos(std.math.degreesToRadians(self.yaw));
+ const sin_yaw = @sin(std.math.degreesToRadians(self.yaw));
+ const cos_pitch = @cos(std.math.degreesToRadians(self.pitch));
+ const sin_pitch = @sin(std.math.degreesToRadians(self.pitch));
+ const forward = math.normalize(math.Vec4{
+ cos_yaw * cos_pitch,
+ sin_pitch,
+ sin_yaw * cos_pitch,
+ 0,
+ });
+ return math.lookToLH(self.position, forward, .{ 0, 1, 0, 0 });
+ }
+
+ pub fn invView(self: *const PerspectiveCamera) math.Mat4 {
+ const cos_yaw = @cos(std.math.degreesToRadians(self.yaw));
+ const sin_yaw = @sin(std.math.degreesToRadians(self.yaw));
+ const cos_pitch = @cos(std.math.degreesToRadians(self.pitch));
+ const sin_pitch = @sin(std.math.degreesToRadians(self.pitch));
+ const forward = math.normalize(math.Vec4{
+ cos_yaw * cos_pitch,
+ sin_pitch,
+ sin_yaw * cos_pitch,
+ 0,
+ });
+ return math.invLookToLH(self.position, forward, .{ 0, 1, 0, 0 });
+ }
+
+ pub fn proj(self: *const PerspectiveCamera) math.Mat4 {
+ return math.perspectiveLH(
+ self.fovy,
+ self.aspect,
+ self.near,
+ self.far,
+ );
+ }
+
+ pub fn invProj(self: *const PerspectiveCamera) math.Mat4 {
+ return math.invPerspectiveLH(
+ self.fovy,
+ self.aspect,
+ self.near,
+ self.far,
+ );
+ }
+};
diff --git a/src/shader-sandbox/main.zig b/src/shader-sandbox/main.zig
@@ -3,6 +3,7 @@ const std = @import("std");
const glfw = @import("glfw");
const wgpu = @import("wgpu");
+const math = @import("math");
const Callback1 = wgpu.Callback1;
const DeviceCallback = wgpu.DeviceCallback;
@@ -11,6 +12,7 @@ const Device = wgpu.Device;
const Instance = wgpu.Instance;
const StringView = wgpu.StringView;
+const PerspectiveCamera = @import("camera.zig").PerspectiveCamera;
const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
const TriangleRenderPipeline = @import("render_pipeline.zig").TriangleRenderPipeline;
@@ -165,22 +167,17 @@ pub fn main() !void {
.present_mode = .fifo,
});
+
var triangle_rp = try TriangleRenderPipeline.init(device);
+
+ const camera = PerspectiveCamera{
+ .yaw = 100.0,
+ .pitch = -10.0,
+ };
const uniform_data = TriangleRenderPipeline.UniformData{
- .view_matrix = .{
- .{0.5, 0, 0, 0},
- .{ 0, 0.5, 0, 0},
- .{ 0, 0, 1, 0},
- .{ 0, 0, 0, 1},
- },
- .proj_matrix = .{
- .{1, 0, 0, 0},
- .{0, 1, 0, 0},
- .{0, 0, 1, 0},
- .{0, 0, 0, 1},
- },
+ .view_matrix = camera.view(),
+ .proj_matrix = camera.proj(),
};
-
triangle_rp.setUniform(queue, &uniform_data);
const pipelines = [_]RenderPipeline{
diff --git a/src/shader-sandbox/render_pipeline.zig b/src/shader-sandbox/render_pipeline.zig
@@ -40,16 +40,14 @@ pub const TriangleRenderPipeline = struct {
\\ @vertex fn main(
\\ @builtin(vertex_index) vtx_idx : u32
\\ ) -> @builtin(position) vec4<f32> {
- \\ let camera_matrix = uniform_data.proj * uniform_data.view;
+ \\ let camera_matrix = transpose(
+ \\ uniform_data.view * uniform_data.proj
+ \\ );
\\ let x = f32(i32(vtx_idx) - 1);
\\ let y = f32(i32(vtx_idx & 1u) * 2 - 1);
- \\ let position_ws = vec4<f32>(x, y, 0.0, 1.0);
- \\ let mat = mat4x4<f32>( 1, 0, 0, 0,
- \\ 0, 1, 0, 0,
- \\ 0, 0, 1, 0,
- \\ 0, 0, 0, 1 );
+ \\ let position_ws = vec4<f32>(x, y, 50.0, 1.0);
\\ let position_cs = camera_matrix * position_ws;
- \\ return camera_matrix * position_ws;
+ \\ return position_cs;
\\ }
;
const vs_module = device.createShaderModule(&.{