From 37f603dc8adcf54c0f1c9fcd09e3dc169bf7eb42 Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Sat, 3 Aug 2024 09:44:09 +0400 Subject: [PATCH] Use radix sort for sorting draw commands, add ambient light, tweak lighting --- assets/shaders/mesh.glsl | 7 ++- src/Render.zig | 124 +++++++++++++++++++++++++++++++-------- src/game.zig | 2 +- 3 files changed, 105 insertions(+), 28 deletions(-) diff --git a/assets/shaders/mesh.glsl b/assets/shaders/mesh.glsl index 7a6bb9b..123c031 100644 --- a/assets/shaders/mesh.glsl +++ b/assets/shaders/mesh.glsl @@ -299,7 +299,7 @@ vec3 microfacetModel(EvalMaterial mat, int light_idx, vec3 P, vec3 N) { shadow_mult = sum / 16.0; } - shadow_mult = clamp(shadow_mult, 0.3, 1.0); + shadow_mult = clamp(shadow_mult, 0.0, 1.0); vec3 specBrdf = 0.25 * ggxDistribution(mat, NDotH) * schlickFresnel(mat, LDotH) * geomSmith(mat, NDotL) * geomSmith(mat, NDotV); @@ -327,6 +327,11 @@ void main() { finalColor += microfacetModel(material, i, VertexOut.vPos, N); } + float ambient_a = dot(VertexOut.wNormal, vec3(0, 1, 0)) * 0.5 + 0.5; + + // ambient + finalColor += material.albedo.rgb * mix(vec3(0.0116, 0.0127, 0.0200), vec3(0.185, 0.198, 0.250), ambient_a); + FragColor = vec4(finalColor, material.albedo.a); } diff --git a/src/Render.zig b/src/Render.zig index 0dd687d..e0f8b7c 100644 --- a/src/Render.zig +++ b/src/Render.zig @@ -428,6 +428,12 @@ pub const LightCommand = union(LightKind) { point: PointLight, }; +const DrawCommandKey = packed struct { + transparent: u1 = 0, + distance: u15 = 0, + mesh: u16 = 0, +}; + pub fn drawLight(self: *Render, cmd: LightCommand) void { self.lights[self.light_count] = cmd; self.light_count += 1; @@ -435,9 +441,99 @@ pub fn drawLight(self: *Render, cmd: LightCommand) void { pub fn draw(self: *Render, cmd: DrawCommand) void { self.command_buffer[self.command_count] = cmd; + // TODO: don't load the whole mesh here + const mesh = self.assetman.resolveMesh(cmd.mesh); + const material: Material = if (cmd.material_override) |mat| mat else mesh.material; + const view_origin = self.camera.view_mat.extractTranslation(); + const max_value = @as(f32, @floatFromInt(std.math.maxInt(u15))); + const dist: u15 = @intFromFloat(std.math.clamp(view_origin.distance(cmd.transform.extractTranslation()) / max_value, 0.0, max_value)); + const key = DrawCommandKey{ + .transparent = if (material.blend_mode == .AlphaBlend) 1 else 0, + .distance = if (material.blend_mode == .AlphaBlend) ~dist else dist, // TODO: calculate distance. Opaque should be front to back, transparent back to front + .mesh = @intCast(cmd.mesh.id % std.math.maxInt(u16)), + }; + self.command_buffer[self.command_count].key = key; self.command_count += 1; } +// Multipass radix sort for u32 +fn sortCommands(self: *Render, in_cmds: []DrawCommand) void { + var cmds = in_cmds; + var aux = self.frame_arena.alloc(DrawCommand, cmds.len) catch @panic("OOM"); + + var cnt1: [256]usize = std.mem.zeroes([256]usize); + var cnt2: [256]usize = std.mem.zeroes([256]usize); + var cnt3: [256]usize = std.mem.zeroes([256]usize); + var cnt4: [256]usize = std.mem.zeroes([256]usize); + + // Find counts + for (cmds) |*cmd| { + const key: u32 = @bitCast(cmd.key); + + cnt1[(key >> 0) & 0xFF] += 1; + cnt2[(key >> 8) & 0xFF] += 1; + cnt3[(key >> 16) & 0xFF] += 1; + cnt4[(key >> 24) & 0xFF] += 1; + } + + var a1: usize = 0; + var a2: usize = 0; + var a3: usize = 0; + var a4: usize = 0; + for (0..256) |i| { + const b1 = cnt1[i]; + const b2 = cnt2[i]; + const b3 = cnt3[i]; + const b4 = cnt4[i]; + + cnt1[i] = a1; + cnt2[i] = a2; + cnt3[i] = a3; + cnt4[i] = a4; + + a1 += b1; + a2 += b2; + a3 += b3; + a4 += b4; + } + + for (0..cmds.len) |i| { + const key: u32 = @bitCast(cmds[i].key); + const k = (key >> 0) & 0xFF; + const dst = cnt1[k]; + cnt1[k] += 1; + aux[dst] = cmds[i]; + } + std.mem.swap([]DrawCommand, &cmds, &aux); + + for (0..cmds.len) |i| { + const key: u32 = @bitCast(cmds[i].key); + const k = (key >> 8) & 0xFF; + const dst = cnt2[k]; + cnt2[k] += 1; + aux[dst] = cmds[i]; + } + std.mem.swap([]DrawCommand, &cmds, &aux); + + for (0..cmds.len) |i| { + const key: u32 = @bitCast(cmds[i].key); + const k = (key >> 16) & 0xFF; + const dst = cnt3[k]; + cnt3[k] += 1; + aux[dst] = cmds[i]; + } + std.mem.swap([]DrawCommand, &cmds, &aux); + + for (0..cmds.len) |i| { + const key: u32 = @bitCast(cmds[i].key); + const k = (key >> 24) & 0xFF; + const dst = cnt4[k]; + cnt4[k] += 1; + aux[dst] = cmds[i]; + } + std.mem.swap([]DrawCommand, &cmds, &aux); +} + pub fn finish(self: *Render) void { const zone = tracy.initZone(@src(), .{ .name = "Render.finish" }); defer zone.deinit(); @@ -450,32 +546,7 @@ pub fn finish(self: *Render) void { const zoneSort = tracy.initZone(@src(), .{ .name = "Render.finish_sortDraws" }); defer zoneSort.deinit(); - const cmds = self.command_buffer[0..self.command_count]; - std.mem.sortUnstable(DrawCommand, cmds, self, struct { - pub fn lessThan(render: *const Render, lhs: DrawCommand, rhs: DrawCommand) bool { - const lhs_mesh = render.assetman.resolveMesh(lhs.mesh); - const rhs_mesh = render.assetman.resolveMesh(rhs.mesh); - const lhs_material: Material = if (lhs.material_override) |mat| mat else lhs_mesh.material; - const rhs_material: Material = if (rhs.material_override) |mat| mat else rhs_mesh.material; - - const lhs_blend_num = @intFromEnum(lhs_material.blend_mode); - const rhs_blend_num = @intFromEnum(rhs_material.blend_mode); - - if (lhs_material.blend_mode == .Opaque and rhs_material.blend_mode == .Opaque) { - return lhs.mesh.id < rhs.mesh.id; - } - - if (lhs_material.blend_mode == .AlphaBlend and rhs_material.blend_mode == .AlphaBlend) { - const lhs_view_pos = render.camera.view_mat.mulByVec4(lhs.transform.extractTranslation().toVec4(1)); - const rhs_view_pos = render.camera.view_mat.mulByVec4(rhs.transform.extractTranslation().toVec4(1)); - - // Back to front sorting. View pos has negative Z - return lhs_view_pos.z() < rhs_view_pos.z(); - } - - return lhs_blend_num < rhs_blend_num; - } - }.lessThan); + self.sortCommands(self.command_buffer[0..self.command_count]); } if (self.update_view_frustum) { @@ -1129,6 +1200,7 @@ pub fn checkGLError() void { } pub const DrawCommand = struct { + key: DrawCommandKey = .{}, mesh: AssetManager.Handle.Mesh, material_override: ?Material, transform: Mat4, diff --git a/src/game.zig b/src/game.zig index ccdf884..844a394 100644 --- a/src/game.zig +++ b/src/game.zig @@ -191,7 +191,7 @@ export fn game_init(global_allocator: *std.mem.Allocator) void { _ = globals.g_mem.world.addEntity(.{ .flags = .{ .dir_light = true, .rotate = true }, .transform = .{ .rot = Quat.fromEulerAngles(Vec3.new(70, 0, 0)) }, - .light = .{ .color_intensity = Vec4.new(std.math.pow(f32, 1, 2.2), std.math.pow(f32, 0.9568627450980393, 2.2), std.math.pow(f32, 0.9176470588235294, 2.2), 1.0) }, + .light = .{ .color_intensity = Vec4.new(1.00, 0.885, 0.570, 1.0) }, .rotate = .{ .axis = Vec3.up(), .rate = -10 }, });