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,
|
||||
count: gl.GLsizei,
|
||||
type: gl.GLenum,
|
||||
|
||||
pub fn bind(self: *const IndexSlice) void {
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
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_DRAW_COMMANDS = 4096;
|
||||
pub const MAX_LIGHT_COMMANDS = 2048;
|
||||
pub const CSM_SPLITS = 4;
|
||||
|
||||
pub const Render = @This();
|
||||
|
||||
@ -63,6 +64,11 @@ post_process_vao: gl.GLuint = 0,
|
||||
// Bloom
|
||||
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 {
|
||||
var render = Render{
|
||||
.allocator = allocator,
|
||||
@ -165,7 +171,7 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm
|
||||
checkGLError();
|
||||
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();
|
||||
|
||||
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 {
|
||||
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];
|
||||
|
||||
// Sort lights: directional first
|
||||
@ -449,6 +471,10 @@ pub fn finish(self: *Render) void {
|
||||
const lights_buf = self.getLightBuffer();
|
||||
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
|
||||
{
|
||||
gl.bindVertexArray(self.shadow_vao);
|
||||
@ -476,17 +502,32 @@ pub fn finish(self: *Render) void {
|
||||
|
||||
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;
|
||||
camera_matrix.* = .{
|
||||
.projection = math.orthographic(-4, 4, -4, 4, -5, 5),
|
||||
.view = Mat4.lookAt(
|
||||
dir_light.dir.scale(-1),
|
||||
Vec3.zero(),
|
||||
Vec3.up(),
|
||||
),
|
||||
.view = view,
|
||||
.projection = projection,
|
||||
};
|
||||
|
||||
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);
|
||||
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.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));
|
||||
camera_matrix.* = .{
|
||||
.projection = projection,
|
||||
.projection = camera_projection,
|
||||
.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.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;
|
||||
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);
|
||||
|
||||
if (!world_camera_frustum.intersectAABB(aabb.transform(cmd.transform))) {
|
||||
if (!self.world_camera_frustum.intersectAABB(aabb.transform(cmd.transform))) {
|
||||
continue;
|
||||
}
|
||||
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 });
|
||||
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
@ -927,7 +1012,7 @@ pub const Camera = struct {
|
||||
fovy: f32 = 60,
|
||||
aspect: f32 = 1,
|
||||
near: f32 = 0.1,
|
||||
far: f32 = 100,
|
||||
far: f32 = 10,
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
// 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 => {
|
||||
if (event.type == c.SDL_KEYUP) {
|
||||
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),
|
||||
};
|
||||
|
||||
// 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 {
|
||||
// x, y, z - normal, w - distance
|
||||
nd: Vec4 = Vec4.up(),
|
||||
@ -74,12 +86,12 @@ pub const AABB = struct {
|
||||
|
||||
pub const Frustum = struct {
|
||||
// Plane normals
|
||||
top: Plane,
|
||||
right: Plane,
|
||||
bottom: Plane,
|
||||
left: Plane,
|
||||
near: Plane,
|
||||
far: Plane,
|
||||
top: Plane = .{},
|
||||
right: Plane = .{},
|
||||
bottom: Plane = .{},
|
||||
left: Plane = .{},
|
||||
near: Plane = .{},
|
||||
far: Plane = .{},
|
||||
|
||||
/// Extracts frustum planes from matrices using Gribb-Hartmann method
|
||||
/// 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 bottom = row4.add(row2);
|
||||
const top = row4.sub(row2);
|
||||
const near = row4.add(row3);
|
||||
const near = row3;
|
||||
const far = row4.sub(row3);
|
||||
|
||||
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[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][0] = (left + right) / (left - right);
|
||||
|
Loading…
x
Reference in New Issue
Block a user