Use radix sort for sorting draw commands, add ambient light, tweak lighting

This commit is contained in:
sergeypdev 2024-08-03 09:44:09 +04:00
parent 9a6ba0dc7f
commit 37f603dc8a
3 changed files with 105 additions and 28 deletions

View File

@ -299,7 +299,7 @@ vec3 microfacetModel(EvalMaterial mat, int light_idx, vec3 P, vec3 N) {
shadow_mult = sum / 16.0; 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); 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); 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); FragColor = vec4(finalColor, material.albedo.a);
} }

View File

@ -428,6 +428,12 @@ pub const LightCommand = union(LightKind) {
point: PointLight, point: PointLight,
}; };
const DrawCommandKey = packed struct {
transparent: u1 = 0,
distance: u15 = 0,
mesh: u16 = 0,
};
pub fn drawLight(self: *Render, cmd: LightCommand) void { pub fn drawLight(self: *Render, cmd: LightCommand) void {
self.lights[self.light_count] = cmd; self.lights[self.light_count] = cmd;
self.light_count += 1; self.light_count += 1;
@ -435,9 +441,99 @@ pub fn drawLight(self: *Render, cmd: LightCommand) void {
pub fn draw(self: *Render, cmd: DrawCommand) void { pub fn draw(self: *Render, cmd: DrawCommand) void {
self.command_buffer[self.command_count] = cmd; 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; 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 { pub fn finish(self: *Render) void {
const zone = tracy.initZone(@src(), .{ .name = "Render.finish" }); const zone = tracy.initZone(@src(), .{ .name = "Render.finish" });
defer zone.deinit(); defer zone.deinit();
@ -450,32 +546,7 @@ pub fn finish(self: *Render) void {
const zoneSort = tracy.initZone(@src(), .{ .name = "Render.finish_sortDraws" }); const zoneSort = tracy.initZone(@src(), .{ .name = "Render.finish_sortDraws" });
defer zoneSort.deinit(); defer zoneSort.deinit();
const cmds = self.command_buffer[0..self.command_count]; self.sortCommands(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);
} }
if (self.update_view_frustum) { if (self.update_view_frustum) {
@ -1129,6 +1200,7 @@ pub fn checkGLError() void {
} }
pub const DrawCommand = struct { pub const DrawCommand = struct {
key: DrawCommandKey = .{},
mesh: AssetManager.Handle.Mesh, mesh: AssetManager.Handle.Mesh,
material_override: ?Material, material_override: ?Material,
transform: Mat4, transform: Mat4,

View File

@ -191,7 +191,7 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
_ = globals.g_mem.world.addEntity(.{ _ = globals.g_mem.world.addEntity(.{
.flags = .{ .dir_light = true, .rotate = true }, .flags = .{ .dir_light = true, .rotate = true },
.transform = .{ .rot = Quat.fromEulerAngles(Vec3.new(70, 0, 0)) }, .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 }, .rotate = .{ .axis = Vec3.up(), .rate = -10 },
}); });