zgpu

git clone git://git.electrosoup.com/zgpu
Log | Files | Refs | Submodules | README

commit 4de3a72e087469ad6f9afcd22bea3b0de18c49b9
parent e0dffa92a68c08dda96c7b8974f8b4b6f0c746a1
Author: Christian Ermann <christian.ermann@joescan.com>
Date:   Wed, 16 Jul 2025 21:57:46 -0700

Add perspective camera

Diffstat:
Msrc/math.zig | 4++--
Asrc/shader-sandbox/camera.zig | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/shader-sandbox/main.zig | 23++++++++++-------------
Msrc/shader-sandbox/render_pipeline.zig | 12+++++-------
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(&.{