Start implementing alpha mask instead of alpha blend for everything,

add debug bounds rendering
This commit is contained in:
sergeypdev 2024-08-24 23:26:01 +04:00
parent 8031b77c8a
commit 8e9cb3fa5b
10 changed files with 257 additions and 38 deletions

29
assets/shaders/debug.glsl Normal file
View File

@ -0,0 +1,29 @@
// UBOs
layout(std140, binding = 0) uniform Matrices {
mat4 projection;
mat4 view;
};
layout(location = 2) uniform vec3 color;
// Input, output blocks
#if VERTEX_SHADER
layout(location = 0) in vec3 aPos;
void main() {
gl_Position = projection * view * vec4(aPos.xyz, 1.0);
}
#endif // VERTEX_SHADER
#if FRAGMENT_SHADER
out vec4 FragColor;
void main() {
FragColor = vec4(vec3(1.0), 1.0f);
}
#endif // FRAGMNET_SHADER

View File

@ -0,0 +1,5 @@
{
"shader": "debug.glsl",
"vertex": true,
"fragment": true
}

View File

@ -341,6 +341,10 @@ vec3 ibl(int matIdx, vec3 N, vec3 V) {
void main() {
int matIdx = draw_data[DrawID].materialIdx;
if (getAlbedo(matIdx).a < 0.5) {
FragColor = vec4(0);
return;
}
sampler2D normal_map = materials[matIdx].normal_map;
vec2 normal_map_uv_scale = materials[matIdx].normal_map_uv_scale;
@ -354,7 +358,8 @@ void main() {
vec3 finalColor = vec3(0);
int n_lights = clamp(int(lights_count), 0, MAX_POINT_LIGHTS);
for (int i = 0; i < n_lights; i++) {
for (int i = 0; i < MAX_POINT_LIGHTS; i++) {
if (i > lights_count) break;
finalColor += microfacetModel(matIdx, i, VertexOut.vPos, N);
}

View File

@ -25,7 +25,6 @@ void main() {
void main() {
//gl_FragDepth = gl_FragCoord.z / gl_FragCoord.w;
}

View File

@ -1,3 +1,4 @@
#extension GL_ARB_bindless_texture : enable
struct DrawCmdData {
mat4 transform;
@ -15,21 +16,63 @@ layout(std430, binding = 3) readonly buffer DrawCmdDatas {
DrawCmdData draw_data[];
};
VERTEX_EXPORT flat uint DrawID;
VERTEX_EXPORT VertexData {
vec2 uv;
} VertexOut;
#if VERTEX_SHADER
layout(location = 0) in vec3 aPos;
layout(location = 2) in vec2 aUV;
void main() {
mat4 model = draw_data[gl_BaseInstance + gl_InstanceID].transform;
mat4 viewModel = view * model;
vec4 vPos = viewModel * vec4(aPos.xyz, 1.0);
gl_Position = projection * vPos;
DrawID = gl_BaseInstance + gl_InstanceID;
mat4 model = draw_data[DrawID].transform;
mat4 viewModel = view * model;
vec4 vPos = viewModel * vec4(aPos.xyz, 1.0);
gl_Position = projection * vPos;
VertexOut.uv = aUV;
}
#endif
#if FRAGMENT_SHADER
void main() {}
struct Material {
vec4 albedo;
sampler2D albedo_map;
vec2 albedo_map_uv_scale;
sampler2D normal_map;
vec2 normal_map_uv_scale;
float metallic;
sampler2D metallic_map;
vec2 metallic_map_uv_scale;
float roughness;
sampler2D roughness_map;
vec2 roughness_map_uv_scale;
vec3 emission;
sampler2D emission_map;
vec2 emission_map_uv_scale;
};
layout(std430, binding = 2) readonly buffer Materials {
uint materials_count;
Material materials[];
};
vec4 getAlbedo(int materialIdx) {
return textureSize(materials[materialIdx].albedo_map, 0) == ivec2(0) ? vec4(pow(materials[materialIdx].albedo.rgb, vec3(2.2)), materials[materialIdx].albedo.a) : texture(materials[materialIdx].albedo_map, VertexOut.uv * materials[materialIdx].albedo_map_uv_scale);
}
out vec4 FragColor;
void main() {
int matIdx = draw_data[DrawID].materialIdx;
if (getAlbedo(matIdx).a < 0.5) {
discard;
}
}
#endif

View File

@ -24,6 +24,7 @@ const checkGLError = @import("Render.zig").checkGLError;
const BuddyAllocator = @import("BuddyAllocator.zig");
const Vec2 = @import("zalgebra").Vec2;
const Vec3 = @import("zalgebra").Vec3;
const Mat4 = @import("zalgebra").Mat4;
const sdl = @import("sdl.zig");
const tracy = @import("tracy");
@ -689,6 +690,35 @@ const LoadedScene = struct {
pub const AABB = struct {
min: Vec3 = Vec3.zero(),
max: Vec3 = Vec3.zero(),
pub fn distance(self: *const AABB, point: Vec3) f32 {
const center = self.min.add(self.max).scale(0.5);
const extent = self.max.sub(self.min).scale(0.5);
var center_to_point = point.sub(center);
center_to_point.data = @abs(center_to_point.data);
var d = center_to_point.sub(extent);
d.data = @max(d.data, @as(@Vector(3, f32), @splat(0.0)));
const sq_dist_to_side = d.dot(d);
if (std.math.approxEqAbs(f32, sq_dist_to_side, 0.0, 0.0001)) {
const diff = point.sub(center);
return diff.dot(diff);
}
return sq_dist_to_side;
}
pub fn transformBy(self: *const AABB, matrix: Mat4) AABB {
var center = self.min.add(self.max).scale(0.5).toVec4(1.0);
var extent = self.max.sub(self.min).scale(0.5).toVec4(0.0);
center = matrix.mulByVec4(center);
extent = matrix.mulByVec4(extent);
return AABB{ .min = center.sub(extent).toVec3(), .max = center.add(extent).toVec3() };
}
};
pub const BufferSlice = struct {

View File

@ -7,6 +7,7 @@ const globals = @import("globals.zig");
pub const Material = @import("formats.zig").Material;
const math = @import("math.zig");
const formats = @import("formats.zig");
const AABB = AssetManager.AABB; // TODO: move AABB out of formats pls
const tracy = @import("tracy");
const za = @import("zalgebra");
@ -39,6 +40,7 @@ frame_arena: std.mem.Allocator,
assetman: *AssetManager,
camera: *Camera = &default_camera,
mesh_vao: gl.GLuint = 0,
z_prepass_vao: gl.GLuint = 0,
tripple_buffer_index: usize = MAX_FRAMES_QUEUED - 1,
gl_fences: [MAX_FRAMES_QUEUED]?gl.GLsync = [_]?gl.GLsync{null} ** MAX_FRAMES_QUEUED,
camera_ubo: gl.GLuint = 0,
@ -85,6 +87,10 @@ camera_view_proj: Mat4 = Mat4.identity(),
world_camera_frustum: math.Frustum = .{},
world_view_frustum_corners: [CSM_SPLITS][8]Vec3 = undefined,
// Debug Draw
debug_drawer: DebugDrawer = undefined,
debug_lines_vao: gl.GLuint = 0,
pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetman: *AssetManager) Render {
var render = Render{
.allocator = allocator,
@ -137,6 +143,38 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm
gl.vertexArrayAttribFormat(vao, Attrib.UV.value(), 2, gl.FLOAT, gl.FALSE, 0);
}
{
// Z Prepass Mesh VAO
var vao: gl.GLuint = 0;
gl.createVertexArrays(1, &vao);
std.debug.assert(vao != 0);
render.z_prepass_vao = vao;
// positions
// gl.vertexArrayVertexBuffer(vao, 0, vertices, 0, @sizeOf(formats.Vector3));
gl.enableVertexArrayAttrib(vao, Attrib.Position.value());
gl.vertexArrayAttribBinding(vao, Attrib.Position.value(), 0);
gl.vertexArrayAttribFormat(vao, Attrib.Position.value(), 3, gl.FLOAT, gl.FALSE, 0);
// uvs
gl.enableVertexArrayAttrib(vao, Attrib.UV.value());
gl.vertexArrayAttribBinding(vao, Attrib.UV.value(), 2);
gl.vertexArrayAttribFormat(vao, Attrib.UV.value(), 2, gl.FLOAT, gl.FALSE, 0);
}
{
// Debug Lines VAO
var vao: gl.GLuint = 0;
gl.createVertexArrays(1, &vao);
std.debug.assert(vao != 0);
render.debug_lines_vao = vao;
// positions
gl.enableVertexArrayAttrib(vao, Attrib.Position.value());
gl.vertexArrayAttribBinding(vao, Attrib.Position.value(), 0);
gl.vertexArrayAttribFormat(vao, Attrib.Position.value(), 3, gl.FLOAT, gl.FALSE, 0);
}
const PERSISTENT_BUFFER_FLAGS: gl.GLbitfield = gl.MAP_PERSISTENT_BIT | gl.MAP_WRITE_BIT | gl.MAP_COHERENT_BIT;
// Camera matrices ubo
@ -372,6 +410,8 @@ pub fn begin(self: *Render) void {
self.light_count = 0;
self.tripple_buffer_index = (self.tripple_buffer_index + 1) % MAX_FRAMES_QUEUED;
self.debug_drawer = DebugDrawer.init(self.frame_arena);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
if (self.gl_fences[self.tripple_buffer_index]) |fence| {
@ -429,9 +469,8 @@ pub const LightCommand = union(LightKind) {
};
const DrawCommandKey = packed struct {
mesh: u16 = 0,
distance: u15 = 0,
transparent: u1 = 0,
distance: u30 = 0,
blend: u2 = 0,
};
pub fn drawLight(self: *Render, cmd: LightCommand) void {
@ -439,18 +478,78 @@ pub fn drawLight(self: *Render, cmd: LightCommand) void {
self.light_count += 1;
}
fn invLerp(aa: f32, b: f32, v: f32) f32 {
return (v - aa) / (b - aa);
}
const DebugDrawer = struct {
const Self = @This();
allocator: std.mem.Allocator,
// Wasting frame arena memory here, maybe redo this
line_list: std.ArrayListUnmanaged([2][3]f32) = .{},
pub fn init(allocator: std.mem.Allocator) Self {
return Self{ .allocator = allocator };
}
pub fn drawAABB(self: *Self, in_aabb: AABB) void {
var aabb = in_aabb;
var new_lines: [12][2][3]f32 = undefined;
var min: [3]f32 = .{ aabb.min.x(), aabb.min.y(), aabb.min.z() };
var max: [3]f32 = .{ aabb.max.x(), aabb.max.y(), aabb.max.z() };
for (0..2) |i| {
new_lines[i * 6 + 0] = .{ min, .{ max[0], min[1], min[2] } };
new_lines[i * 6 + 1] = .{ min, .{ min[0], max[1], min[2] } };
new_lines[i * 6 + 2] = .{ min, .{ min[0], min[1], max[2] } };
new_lines[i * 6 + 3] = .{ max, .{ min[0], max[1], max[2] } };
new_lines[i * 6 + 4] = .{ max, .{ max[0], min[1], max[2] } };
new_lines[i * 6 + 5] = .{ max, .{ max[0], max[1], min[2] } };
std.mem.swap(f32, &min[1], &max[1]);
}
self.line_list.appendSlice(self.allocator, &new_lines) catch {};
}
pub fn uploadLinesBuffer(self: *const Self) gl.GLuint {
var buf: gl.GLuint = 0;
gl.createBuffers(1, &buf);
std.debug.assert(buf != 0);
gl.namedBufferStorage(buf, @intCast(@sizeOf(f32) * 3 * 2 * self.line_list.items.len), self.line_list.items.ptr, 0);
return buf;
}
};
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 aabb = mesh.aabb.transformBy(cmd.transform);
self.debug_drawer.drawAABB(aabb);
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 distance = view_origin.distance(aabb.min.add(aabb.max).scale(0.5));
const alpha = std.math.clamp(invLerp(self.camera.near, self.camera.far, distance), 0.0, 1.0);
const max_value = @as(f32, @floatFromInt(std.math.maxInt(u30)));
const quantized_dist: u30 = @intFromFloat(alpha * max_value);
const inv_quantized_dist: u30 = @intFromFloat((1.0 - alpha) * 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)),
.blend = switch (material.blend_mode) {
.Opaque => 0,
.AlphaMask => 1,
.AlphaBlend => 2,
},
.distance = if (material.blend_mode == .AlphaBlend) inv_quantized_dist else quantized_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;
@ -846,11 +945,11 @@ pub fn finish(self: *Render) void {
var materials_count: usize = 0;
for (self.command_buffer[0..self.command_count]) |*cmd| {
const mesh = self.assetman.resolveMesh(cmd.mesh);
// const aabb = math.AABB.fromMinMax(mesh.aabb.min, mesh.aabb.max);
const aabb = math.AABB.fromMinMax(mesh.aabb.min, mesh.aabb.max);
// if (!self.world_camera_frustum.intersectAABB(aabb.transform(cmd.transform))) {
// continue;
// }
if (!self.world_camera_frustum.intersectAABB(aabb.transform(cmd.transform))) {
continue;
}
const material: Material = if (cmd.material_override) |mat| mat else mesh.material;
@ -919,11 +1018,13 @@ pub fn finish(self: *Render) void {
// Z Prepass
{
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.z_prepass).program);
gl.bindVertexArray(self.shadow_vao);
gl.bindVertexArray(self.z_prepass_vao);
gl.depthFunc(gl.LESS);
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value());
checkGLError();
self.assetman.vertex_heap.uvs.bind(Render.Attrib.UV.value());
checkGLError();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer);
checkGLError();
@ -936,6 +1037,7 @@ pub fn finish(self: *Render) void {
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.mesh).program);
gl.bindVertexArray(self.mesh_vao);
gl.depthFunc(gl.EQUAL);
defer gl.depthFunc(gl.LEQUAL);
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(Uniform.EnvBRDF.value(), self.assetman.resolveTexture(a.Textures.@"ibl_brdf_lut.norm").handle);
gl.GL_ARB_bindless_texture.uniformHandleui64ARB(Uniform.ShadowMap2D.value(), self.shadow_texture_handle);
@ -978,8 +1080,20 @@ pub fn finish(self: *Render) void {
defer gl.polygonMode(gl.FRONT_AND_BACK, gl.FILL);
gl.lineWidth(4);
const debug_lines_buffer = self.debug_drawer.uploadLinesBuffer();
defer gl.deleteBuffers(1, &debug_lines_buffer);
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.debug).program);
gl.bindVertexArray(self.debug_lines_vao);
gl.uniform3f(Uniform.Color.value(), 1.0, 1.0, 1.0);
gl.bindVertexBuffer(Render.Attrib.Position.value(), debug_lines_buffer, 0, @intCast(@sizeOf(f32) * 3));
gl.drawArrays(gl.LINES, 0, @intCast(self.debug_drawer.line_list.items.len * 2));
// Frustum debug stuff, drawn only when view frustum is fixed
if (!self.update_view_frustum) {
if (false and !self.update_view_frustum) {
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.unlit).program);
// Draw wire frustum cubes
@ -1162,9 +1276,9 @@ const cube_camera_dirs = [6]CubeCameraDir{
};
fn renderShadow(self: *Render, frustum: *const math.Frustum) void {
_ = frustum; // autofix
const zone = tracy.initZone(@src(), .{ .name = "Render.renderShadow" });
defer zone.deinit();
_ = frustum; // autofix
self.assetman.vertex_heap.vertices.bind(Render.Attrib.Position.value());
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.assetman.vertex_heap.indices.buffer);

View File

@ -369,8 +369,8 @@ test "write and read scene" {
pub const Material = extern struct {
pub const BlendMode = enum(u8) {
Opaque = 0,
AlphaBlend = 1,
AlphaMask = 2,
AlphaMask = 1,
AlphaBlend = 2,
};
albedo: Vec4 = Vec4.one(),

View File

@ -84,19 +84,14 @@ pub const AABB = struct {
return AABB{ .origin = origin, .extents = extents };
}
// TODO: optimize
pub fn transform(self: *const AABB, matrix: Mat4) AABB {
var min = Vec3.new(std.math.floatMax(f32), std.math.floatMax(f32), std.math.floatMax(f32));
var max = Vec3.new(std.math.floatMin(f32), std.math.floatMin(f32), std.math.floatMin(f32));
var origin = self.origin.toVec4(1.0);
var extents = self.extents.toVec4(0.0);
inline for (box_corners) |corner| {
const corner_pos = matrix.mulByVec4(self.origin.add(self.extents.mul(corner)).toVec4(1));
const corner_pos3 = corner_pos.toVec3();
min = corner_pos3.min(min);
max = corner_pos3.max(max);
}
origin = matrix.mulByVec4(origin);
extents = matrix.mulByVec4(extents);
return AABB.fromMinMax(min, max);
return AABB{ .origin = origin.toVec3(), .extents = extents.toVec3() };
}
pub fn toSphere(self: *const AABB) BoundingSphere {
@ -232,6 +227,7 @@ pub const Frustum = struct {
}
pub fn intersectAABB(self: *const Frustum, aabb: AABB) bool {
@setRuntimeSafety(false);
return self.intersectAABBInternal(aabb, false);
}

View File

@ -218,10 +218,8 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
// TODO: rgba
mat_output.albedo = Vec4.new(base_color.r, base_color.g, base_color.b, base_color.a);
if (std.math.approxEqAbs(f32, base_color.a, 0.0, std.math.floatEps(f32))) {
if (base_color.a < 1.0) {
mat_output.blend_mode = .AlphaMask;
} else if (base_color.a < 1.0) {
mat_output.blend_mode = .AlphaBlend;
}
}
@ -231,7 +229,7 @@ fn processScene(allocator: std.mem.Allocator, input: []const u8, output_dir: std
const entry = mat_texture.path.resolveAssetListEntry(texture_outputs, &has_alpha);
mat_output.albedo_map.id = entry.getAssetId();
if (has_alpha) {
mat_output.blend_mode = .AlphaBlend;
mat_output.blend_mode = .AlphaMask;
}
}