Use view frustum to calculate directional light projection, add a bunch of debug stuff for frustums
This commit is contained in:
parent
6b4fe69505
commit
bb8b29263f
38
assets/cube.obj
Normal file
38
assets/cube.obj
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Blender 4.0.2
|
||||||
|
# www.blender.org
|
||||||
|
o Cube
|
||||||
|
v 1.000000 1.000000 -1.000000
|
||||||
|
v 1.000000 -1.000000 -1.000000
|
||||||
|
v 1.000000 1.000000 1.000000
|
||||||
|
v 1.000000 -1.000000 1.000000
|
||||||
|
v -1.000000 1.000000 -1.000000
|
||||||
|
v -1.000000 -1.000000 -1.000000
|
||||||
|
v -1.000000 1.000000 1.000000
|
||||||
|
v -1.000000 -1.000000 1.000000
|
||||||
|
vn -0.0000 1.0000 -0.0000
|
||||||
|
vn -0.0000 -0.0000 1.0000
|
||||||
|
vn -1.0000 -0.0000 -0.0000
|
||||||
|
vn -0.0000 -1.0000 -0.0000
|
||||||
|
vn 1.0000 -0.0000 -0.0000
|
||||||
|
vn -0.0000 -0.0000 -1.0000
|
||||||
|
vt 0.625000 0.500000
|
||||||
|
vt 0.875000 0.500000
|
||||||
|
vt 0.875000 0.750000
|
||||||
|
vt 0.625000 0.750000
|
||||||
|
vt 0.375000 0.750000
|
||||||
|
vt 0.625000 1.000000
|
||||||
|
vt 0.375000 1.000000
|
||||||
|
vt 0.375000 0.000000
|
||||||
|
vt 0.625000 0.000000
|
||||||
|
vt 0.625000 0.250000
|
||||||
|
vt 0.375000 0.250000
|
||||||
|
vt 0.125000 0.500000
|
||||||
|
vt 0.375000 0.500000
|
||||||
|
vt 0.125000 0.750000
|
||||||
|
s 0
|
||||||
|
f 1/1/1 5/2/1 7/3/1 3/4/1
|
||||||
|
f 4/5/2 3/4/2 7/6/2 8/7/2
|
||||||
|
f 8/8/3 7/9/3 5/10/3 6/11/3
|
||||||
|
f 6/12/4 2/13/4 4/5/4 8/14/4
|
||||||
|
f 2/13/5 1/1/5 3/4/5 4/5/5
|
||||||
|
f 6/11/6 5/10/6 1/1/6 2/13/6
|
34
assets/shaders/unlit.glsl
Normal file
34
assets/shaders/unlit.glsl
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#extension GL_ARB_bindless_texture : enable
|
||||||
|
|
||||||
|
// UBOs
|
||||||
|
layout(std140, binding = 0) uniform Matrices {
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Uniforms
|
||||||
|
layout(location = 1) uniform mat4 model;
|
||||||
|
|
||||||
|
layout(location = 2) uniform vec3 color;
|
||||||
|
|
||||||
|
// Input, output blocks
|
||||||
|
|
||||||
|
#if VERTEX_SHADER
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 aPos;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = projection * view * model * vec4(aPos.xyz, 1.0);
|
||||||
|
}
|
||||||
|
#endif // VERTEX_SHADER
|
||||||
|
|
||||||
|
#if FRAGMENT_SHADER
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
FragColor = vec4(color, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // FRAGMNET_SHADER
|
5
assets/shaders/unlit.prog
Normal file
5
assets/shaders/unlit.prog
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"shader": "unlit.glsl",
|
||||||
|
"vertex": true,
|
||||||
|
"fragment": true
|
||||||
|
}
|
@ -590,6 +590,10 @@ pub const IndexSlice = struct {
|
|||||||
offset: gl.GLuint,
|
offset: gl.GLuint,
|
||||||
count: gl.GLsizei,
|
count: gl.GLsizei,
|
||||||
type: gl.GLenum,
|
type: gl.GLenum,
|
||||||
|
|
||||||
|
pub fn bind(self: *const IndexSlice) void {
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.buffer);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ShaderType = enum {
|
pub const ShaderType = enum {
|
||||||
|
117
src/Render.zig
117
src/Render.zig
@ -19,6 +19,7 @@ pub const MAX_FRAMES_QUEUED = 3;
|
|||||||
pub const MAX_LIGHTS = 8;
|
pub const MAX_LIGHTS = 8;
|
||||||
pub const MAX_DRAW_COMMANDS = 4096;
|
pub const MAX_DRAW_COMMANDS = 4096;
|
||||||
pub const MAX_LIGHT_COMMANDS = 2048;
|
pub const MAX_LIGHT_COMMANDS = 2048;
|
||||||
|
pub const CSM_SPLITS = 4;
|
||||||
|
|
||||||
pub const Render = @This();
|
pub const Render = @This();
|
||||||
|
|
||||||
@ -63,6 +64,11 @@ post_process_vao: gl.GLuint = 0,
|
|||||||
// Bloom
|
// Bloom
|
||||||
screen_bloom_sampler: gl.GLuint = 0,
|
screen_bloom_sampler: gl.GLuint = 0,
|
||||||
|
|
||||||
|
update_view_frustum: bool = true,
|
||||||
|
camera_view_proj: Mat4 = Mat4.identity(),
|
||||||
|
world_camera_frustum: math.Frustum = .{},
|
||||||
|
world_view_frustum_corners: [8]Vec3 = [_]Vec3{Vec3.new(0, 0, 0)} ** 8,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetman: *AssetManager) Render {
|
pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetman: *AssetManager) Render {
|
||||||
var render = Render{
|
var render = Render{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
@ -165,7 +171,7 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm
|
|||||||
checkGLError();
|
checkGLError();
|
||||||
std.debug.assert(render.shadow_texture_array != 0);
|
std.debug.assert(render.shadow_texture_array != 0);
|
||||||
|
|
||||||
gl.textureStorage3D(render.shadow_texture_array, 1, gl.DEPTH_COMPONENT16, 2048, 2048, 1);
|
gl.textureStorage3D(render.shadow_texture_array, 1, gl.DEPTH_COMPONENT16, 2048, 2048, CSM_SPLITS);
|
||||||
checkGLError();
|
checkGLError();
|
||||||
|
|
||||||
gl.textureParameteri(render.shadow_texture_array, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE);
|
gl.textureParameteri(render.shadow_texture_array, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE);
|
||||||
@ -431,6 +437,22 @@ pub fn draw(self: *Render, cmd: DrawCommand) void {
|
|||||||
pub fn finish(self: *Render) void {
|
pub fn finish(self: *Render) void {
|
||||||
const ginit = globals.g_init;
|
const ginit = globals.g_init;
|
||||||
|
|
||||||
|
const camera_projection = self.camera.projection();
|
||||||
|
const view_proj = camera_projection.mul(self.camera.view_mat);
|
||||||
|
if (self.update_view_frustum) {
|
||||||
|
self.camera_view_proj = view_proj;
|
||||||
|
self.world_camera_frustum = math.Frustum.new(view_proj);
|
||||||
|
}
|
||||||
|
|
||||||
|
const inv_view_proj = view_proj.inv();
|
||||||
|
|
||||||
|
if (self.update_view_frustum) {
|
||||||
|
for (math.ndc_box_corners, 0..) |corner, i| {
|
||||||
|
const pos4 = inv_view_proj.mulByVec4(corner.toVec4(1));
|
||||||
|
self.world_view_frustum_corners[i] = pos4.toVec3().scale(1.0 / pos4.w());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const lights = self.lights[0..self.light_count];
|
const lights = self.lights[0..self.light_count];
|
||||||
|
|
||||||
// Sort lights: directional first
|
// Sort lights: directional first
|
||||||
@ -449,6 +471,10 @@ pub fn finish(self: *Render) void {
|
|||||||
const lights_buf = self.getLightBuffer();
|
const lights_buf = self.getLightBuffer();
|
||||||
lights_buf.count = 0;
|
lights_buf.count = 0;
|
||||||
|
|
||||||
|
var dir_aabb_min = Vec3.zero();
|
||||||
|
var dir_aabb_max = Vec3.zero();
|
||||||
|
var dir_view_proj_mat = Mat4.identity();
|
||||||
|
|
||||||
// Light shadow maps
|
// Light shadow maps
|
||||||
{
|
{
|
||||||
gl.bindVertexArray(self.shadow_vao);
|
gl.bindVertexArray(self.shadow_vao);
|
||||||
@ -476,17 +502,32 @@ pub fn finish(self: *Render) void {
|
|||||||
|
|
||||||
gl.viewport(0, 0, 2048, 2048);
|
gl.viewport(0, 0, 2048, 2048);
|
||||||
|
|
||||||
|
var projection: Mat4 = undefined;
|
||||||
|
const view = Mat4.lookAt(
|
||||||
|
dir_light.dir.scale(-1),
|
||||||
|
Vec3.zero(),
|
||||||
|
Vec3.up(),
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
for (self.world_view_frustum_corners) |corner| {
|
||||||
|
const pos4 = view.mulByVec4(corner.toVec4(1));
|
||||||
|
const pos = pos4.toVec3();
|
||||||
|
dir_aabb_min = pos.min(dir_aabb_min);
|
||||||
|
dir_aabb_max = pos.max(dir_aabb_max);
|
||||||
|
}
|
||||||
|
projection = math.orthographic(dir_aabb_min.x(), dir_aabb_max.x(), dir_aabb_min.y(), dir_aabb_max.y(), -dir_aabb_max.z(), -dir_aabb_min.z());
|
||||||
|
//projection = math.orthographic(-1, 1, -5, 5, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
const camera_matrix = &self.shadow_matrices;
|
const camera_matrix = &self.shadow_matrices;
|
||||||
camera_matrix.* = .{
|
camera_matrix.* = .{
|
||||||
.projection = math.orthographic(-4, 4, -4, 4, -5, 5),
|
.view = view,
|
||||||
.view = Mat4.lookAt(
|
.projection = projection,
|
||||||
dir_light.dir.scale(-1),
|
|
||||||
Vec3.zero(),
|
|
||||||
Vec3.up(),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const shadow_view_proj = camera_matrix.projection.mul(camera_matrix.view);
|
const shadow_view_proj = projection.mul(view);
|
||||||
|
dir_view_proj_mat = shadow_view_proj;
|
||||||
const light_frustum = math.Frustum.new(shadow_view_proj);
|
const light_frustum = math.Frustum.new(shadow_view_proj);
|
||||||
light.shadow_vp = shadow_view_proj;
|
light.shadow_vp = shadow_view_proj;
|
||||||
|
|
||||||
@ -578,12 +619,10 @@ pub fn finish(self: *Render) void {
|
|||||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
const projection = self.camera.projection();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const camera_matrix: *CameraMatrices = @alignCast(@ptrCast(self.camera_matrices[self.tripple_buffer_index * self.uboAlignedSizeOf(CameraMatrices) ..].ptr));
|
const camera_matrix: *CameraMatrices = @alignCast(@ptrCast(self.camera_matrices[self.tripple_buffer_index * self.uboAlignedSizeOf(CameraMatrices) ..].ptr));
|
||||||
camera_matrix.* = .{
|
camera_matrix.* = .{
|
||||||
.projection = projection,
|
.projection = camera_projection,
|
||||||
.view = self.camera.view_mat,
|
.view = self.camera.view_mat,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -601,15 +640,12 @@ pub fn finish(self: *Render) void {
|
|||||||
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.mesh).program);
|
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.mesh).program);
|
||||||
gl.bindVertexArray(self.mesh_vao);
|
gl.bindVertexArray(self.mesh_vao);
|
||||||
|
|
||||||
const view_proj = projection.mul(self.camera.view_mat);
|
|
||||||
const world_camera_frustum = math.Frustum.new(view_proj);
|
|
||||||
|
|
||||||
var rendered_count: usize = 0;
|
var rendered_count: usize = 0;
|
||||||
for (self.command_buffer[0..self.command_count]) |*cmd| {
|
for (self.command_buffer[0..self.command_count]) |*cmd| {
|
||||||
const mesh = self.assetman.resolveMesh(cmd.mesh);
|
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 (!world_camera_frustum.intersectAABB(aabb.transform(cmd.transform))) {
|
if (!self.world_camera_frustum.intersectAABB(aabb.transform(cmd.transform))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rendered_count += 1;
|
rendered_count += 1;
|
||||||
@ -681,6 +717,55 @@ pub fn finish(self: *Render) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug stuff
|
||||||
|
{
|
||||||
|
gl.polygonMode(gl.FRONT_AND_BACK, gl.LINE);
|
||||||
|
defer gl.polygonMode(gl.FRONT_AND_BACK, gl.FILL);
|
||||||
|
gl.lineWidth(4);
|
||||||
|
|
||||||
|
// Frustum debug stuff, drawn only when view frustum is fixed
|
||||||
|
if (!self.update_view_frustum) {
|
||||||
|
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.unlit).program);
|
||||||
|
|
||||||
|
// Draw wire frustum cubes
|
||||||
|
{
|
||||||
|
const mesh = self.assetman.resolveMesh(a.Meshes.cube.Cube);
|
||||||
|
mesh.positions.bind(Render.Attrib.Position.value());
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.indices.buffer);
|
||||||
|
gl.uniform3fv(Uniform.Color.value(), 1, @ptrCast(&Vec3.one().data));
|
||||||
|
|
||||||
|
const model = Mat4.fromTranslate(Vec3.new(0, 0, 0.5)).mul(Mat4.fromScale(Vec3.new(1, 1, 0.5)));
|
||||||
|
|
||||||
|
const view_proj_matrices = [_]Mat4{ self.camera_view_proj, dir_view_proj_mat };
|
||||||
|
|
||||||
|
for (view_proj_matrices) |frustum_view_proj| {
|
||||||
|
const frustum_model_mat = frustum_view_proj.inv().mul(model);
|
||||||
|
gl.uniformMatrix4fv(Uniform.ModelMatrix.value(), 1, gl.FALSE, @ptrCast(&frustum_model_mat.data));
|
||||||
|
gl.drawElements(
|
||||||
|
gl.TRIANGLES,
|
||||||
|
mesh.indices.count,
|
||||||
|
mesh.indices.type,
|
||||||
|
@ptrFromInt(mesh.indices.offset),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Draw corner positions of view frustum
|
||||||
|
{
|
||||||
|
const mesh = self.assetman.resolveMesh(a.Meshes.sphere.Icosphere);
|
||||||
|
mesh.positions.bind(Attrib.Position.value());
|
||||||
|
mesh.indices.bind();
|
||||||
|
|
||||||
|
gl.uniform3fv(Uniform.Color.value(), 1, @ptrCast(&Vec3.new(1, 0, 0).data));
|
||||||
|
|
||||||
|
for (self.world_view_frustum_corners) |corner| {
|
||||||
|
const model = Mat4.fromTranslate(corner);
|
||||||
|
gl.uniformMatrix4fv(Uniform.ModelMatrix.value(), 1, gl.FALSE, @ptrCast(&model.data));
|
||||||
|
gl.drawElements(gl.TRIANGLES, mesh.indices.count, mesh.indices.type, @ptrFromInt(mesh.indices.offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//std.log.debug("Total draws {}, frustum culled draws {}\n", .{ self.command_count, rendered_count });
|
//std.log.debug("Total draws {}, frustum culled draws {}\n", .{ self.command_count, rendered_count });
|
||||||
|
|
||||||
gl.disable(gl.DEPTH_TEST);
|
gl.disable(gl.DEPTH_TEST);
|
||||||
@ -927,7 +1012,7 @@ pub const Camera = struct {
|
|||||||
fovy: f32 = 60,
|
fovy: f32 = 60,
|
||||||
aspect: f32 = 1,
|
aspect: f32 = 1,
|
||||||
near: f32 = 0.1,
|
near: f32 = 0.1,
|
||||||
far: f32 = 100,
|
far: f32 = 10,
|
||||||
|
|
||||||
view_mat: Mat4 = Mat4.identity(),
|
view_mat: Mat4 = Mat4.identity(),
|
||||||
|
|
||||||
|
16
src/game.zig
16
src/game.zig
@ -335,6 +335,22 @@ export fn game_update() bool {
|
|||||||
ginit.vsync = !ginit.vsync;
|
ginit.vsync = !ginit.vsync;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Freeze view frustum
|
||||||
|
c.SDL_SCANCODE_F8 => {
|
||||||
|
if (event.type == c.SDL_KEYDOWN) {
|
||||||
|
gmem.render.update_view_frustum = !gmem.render.update_view_frustum;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Expand camera far
|
||||||
|
c.SDL_SCANCODE_F7 => {
|
||||||
|
if (event.type == c.SDL_KEYDOWN) {
|
||||||
|
if (gmem.render.camera.far == 10) {
|
||||||
|
gmem.render.camera.far = 100;
|
||||||
|
} else {
|
||||||
|
gmem.render.camera.far = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
c.SDL_SCANCODE_ESCAPE => {
|
c.SDL_SCANCODE_ESCAPE => {
|
||||||
if (event.type == c.SDL_KEYUP) {
|
if (event.type == c.SDL_KEYUP) {
|
||||||
if (ginit.fullscreen) {
|
if (ginit.fullscreen) {
|
||||||
|
28
src/math.zig
28
src/math.zig
@ -18,6 +18,18 @@ pub const box_corners = [8]Vec3{
|
|||||||
Vec3.new(1, 1, 1),
|
Vec3.new(1, 1, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Using DirectX/Vulkan NDC coordinates
|
||||||
|
pub const ndc_box_corners = [8]Vec3{
|
||||||
|
Vec3.new(-1, -1, 0),
|
||||||
|
Vec3.new(-1, -1, 1),
|
||||||
|
Vec3.new(-1, 1, 0),
|
||||||
|
Vec3.new(-1, 1, 1),
|
||||||
|
Vec3.new(1, -1, 0),
|
||||||
|
Vec3.new(1, -1, 1),
|
||||||
|
Vec3.new(1, 1, 0),
|
||||||
|
Vec3.new(1, 1, 1),
|
||||||
|
};
|
||||||
|
|
||||||
pub const Plane = struct {
|
pub const Plane = struct {
|
||||||
// x, y, z - normal, w - distance
|
// x, y, z - normal, w - distance
|
||||||
nd: Vec4 = Vec4.up(),
|
nd: Vec4 = Vec4.up(),
|
||||||
@ -74,12 +86,12 @@ pub const AABB = struct {
|
|||||||
|
|
||||||
pub const Frustum = struct {
|
pub const Frustum = struct {
|
||||||
// Plane normals
|
// Plane normals
|
||||||
top: Plane,
|
top: Plane = .{},
|
||||||
right: Plane,
|
right: Plane = .{},
|
||||||
bottom: Plane,
|
bottom: Plane = .{},
|
||||||
left: Plane,
|
left: Plane = .{},
|
||||||
near: Plane,
|
near: Plane = .{},
|
||||||
far: Plane,
|
far: Plane = .{},
|
||||||
|
|
||||||
/// Extracts frustum planes from matrices using Gribb-Hartmann method
|
/// Extracts frustum planes from matrices using Gribb-Hartmann method
|
||||||
/// If you pass in a projection matrix planes will be in view space.
|
/// If you pass in a projection matrix planes will be in view space.
|
||||||
@ -95,7 +107,7 @@ pub const Frustum = struct {
|
|||||||
const right = row4.sub(row1);
|
const right = row4.sub(row1);
|
||||||
const bottom = row4.add(row2);
|
const bottom = row4.add(row2);
|
||||||
const top = row4.sub(row2);
|
const top = row4.sub(row2);
|
||||||
const near = row4.add(row3);
|
const near = row3;
|
||||||
const far = row4.sub(row3);
|
const far = row4.sub(row3);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@ -195,7 +207,7 @@ pub fn orthographic(left: f32, right: f32, bottom: f32, top: f32, z_near: f32, z
|
|||||||
|
|
||||||
result.data[0][0] = 2 / (right - left);
|
result.data[0][0] = 2 / (right - left);
|
||||||
result.data[1][1] = 2 / (top - bottom);
|
result.data[1][1] = 2 / (top - bottom);
|
||||||
result.data[2][2] = 2 / (z_near - z_far);
|
result.data[2][2] = 1 / (z_near - z_far);
|
||||||
result.data[3][3] = 1;
|
result.data[3][3] = 1;
|
||||||
|
|
||||||
result.data[3][0] = (left + right) / (left - right);
|
result.data[3][0] = (left + right) / (left - right);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user