Refactor lights
This commit is contained in:
parent
f1123a1e15
commit
6b4fe69505
198
src/Render.zig
198
src/Render.zig
@ -16,8 +16,9 @@ const Quat = za.Quat;
|
||||
const Vec2_i32 = za.Vec2_i32;
|
||||
|
||||
pub const MAX_FRAMES_QUEUED = 3;
|
||||
pub const MAX_POINT_LIGHTS = 8;
|
||||
pub const MAX_LIGHTS = 8;
|
||||
pub const MAX_DRAW_COMMANDS = 4096;
|
||||
pub const MAX_LIGHT_COMMANDS = 2048;
|
||||
|
||||
pub const Render = @This();
|
||||
|
||||
@ -33,7 +34,9 @@ gl_fences: [MAX_FRAMES_QUEUED]?gl.GLsync = [_]?gl.GLsync{null} ** MAX_FRAMES_QUE
|
||||
camera_ubo: gl.GLuint = 0,
|
||||
camera_matrices: []u8 = &.{},
|
||||
point_lights_ubo: gl.GLuint = 0,
|
||||
point_lights: []u8 = &.{},
|
||||
point_lights: []u8 = &.{}, // TODO: remove
|
||||
lights: [MAX_LIGHT_COMMANDS]LightCommand = undefined,
|
||||
light_count: usize = 0,
|
||||
command_buffer: [MAX_DRAW_COMMANDS]DrawCommand = undefined,
|
||||
command_count: usize = 0,
|
||||
ubo_align: usize = 0,
|
||||
@ -136,7 +139,7 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm
|
||||
gl.createBuffers(1, &render.point_lights_ubo);
|
||||
std.debug.assert(render.camera_ubo != 0);
|
||||
|
||||
const buf_size = render.uboAlignedSizeOf(PointLightArray) * MAX_FRAMES_QUEUED;
|
||||
const buf_size = render.uboAlignedSizeOf(LightArray) * MAX_FRAMES_QUEUED;
|
||||
gl.namedBufferStorage(
|
||||
render.point_lights_ubo,
|
||||
@intCast(buf_size),
|
||||
@ -193,7 +196,7 @@ pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, assetm
|
||||
checkGLError();
|
||||
std.debug.assert(render.cube_shadow_texture_array != 0);
|
||||
|
||||
gl.textureStorage3D(render.cube_shadow_texture_array, 1, gl.DEPTH_COMPONENT16, 512, 512, MAX_POINT_LIGHTS * 6);
|
||||
gl.textureStorage3D(render.cube_shadow_texture_array, 1, gl.DEPTH_COMPONENT16, 512, 512, MAX_LIGHTS * 6);
|
||||
checkGLError();
|
||||
|
||||
gl.textureParameteri(render.cube_shadow_texture_array, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE);
|
||||
@ -346,6 +349,7 @@ fn updateScreenBufferSize(self: *Render, width: c_int, height: c_int) void {
|
||||
|
||||
pub fn begin(self: *Render) void {
|
||||
self.command_count = 0;
|
||||
self.light_count = 0;
|
||||
self.tripple_buffer_index = (self.tripple_buffer_index + 1) % MAX_FRAMES_QUEUED;
|
||||
|
||||
gl.enable(gl.CULL_FACE);
|
||||
@ -375,10 +379,11 @@ pub fn begin(self: *Render) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPointLights(self: *Render) *PointLightArray {
|
||||
return @alignCast(@ptrCast(self.point_lights[self.tripple_buffer_index * self.uboAlignedSizeOf(PointLightArray) ..].ptr));
|
||||
fn getLightBuffer(self: *Render) *LightArray {
|
||||
return @alignCast(@ptrCast(self.point_lights[self.tripple_buffer_index * self.uboAlignedSizeOf(LightArray) ..].ptr));
|
||||
}
|
||||
|
||||
// TODO: get rid of this
|
||||
pub fn flushUBOs(self: *Render) void {
|
||||
const idx = self.tripple_buffer_index;
|
||||
|
||||
@ -387,12 +392,37 @@ pub fn flushUBOs(self: *Render) void {
|
||||
gl.UNIFORM_BUFFER,
|
||||
UBO.PointLights.value(),
|
||||
self.point_lights_ubo,
|
||||
idx * self.uboAlignedSizeOf(PointLightArray),
|
||||
@intCast(self.uboAlignedSizeOf(PointLightArray)),
|
||||
idx * self.uboAlignedSizeOf(LightArray),
|
||||
@intCast(self.uboAlignedSizeOf(LightArray)),
|
||||
);
|
||||
checkGLError();
|
||||
}
|
||||
|
||||
pub const LightKind = enum {
|
||||
directional,
|
||||
point,
|
||||
// Spot, // TODO
|
||||
};
|
||||
|
||||
pub const PointLight = struct {
|
||||
color: Vec3,
|
||||
pos: Vec3,
|
||||
radius: f32,
|
||||
};
|
||||
|
||||
pub const LightCommand = union(LightKind) {
|
||||
directional: struct {
|
||||
color: Vec3,
|
||||
dir: Vec3,
|
||||
},
|
||||
point: PointLight,
|
||||
};
|
||||
|
||||
pub fn drawLight(self: *Render, cmd: LightCommand) void {
|
||||
self.lights[self.light_count] = cmd;
|
||||
self.light_count += 1;
|
||||
}
|
||||
|
||||
pub fn draw(self: *Render, cmd: DrawCommand) void {
|
||||
self.command_buffer[self.command_count] = cmd;
|
||||
self.command_count += 1;
|
||||
@ -401,79 +431,64 @@ pub fn draw(self: *Render, cmd: DrawCommand) void {
|
||||
pub fn finish(self: *Render) void {
|
||||
const ginit = globals.g_init;
|
||||
|
||||
const lights = self.getPointLights();
|
||||
const lights = self.lights[0..self.light_count];
|
||||
|
||||
// Sort lights: directional first
|
||||
{
|
||||
std.mem.sortUnstable(LightCommand, lights, {}, struct {
|
||||
pub fn lessThan(_: void, lhs: LightCommand, rhs: LightCommand) bool {
|
||||
_ = rhs; // autofix
|
||||
return switch (lhs) {
|
||||
.directional => true,
|
||||
.point => false,
|
||||
};
|
||||
}
|
||||
}.lessThan);
|
||||
}
|
||||
|
||||
const lights_buf = self.getLightBuffer();
|
||||
lights_buf.count = 0;
|
||||
|
||||
// Light shadow maps
|
||||
{
|
||||
gl.bindVertexArray(self.shadow_vao);
|
||||
|
||||
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, self.shadow_framebuffer);
|
||||
|
||||
for (lights.lights[0..lights.count], 0..) |*light, i| {
|
||||
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.shadow).program);
|
||||
// Directional light
|
||||
if (std.math.approxEqAbs(f32, light.pos.w(), 0, std.math.floatEps(f32))) {
|
||||
gl.namedFramebufferTextureLayer(self.shadow_framebuffer, gl.DEPTH_ATTACHMENT, self.shadow_texture_array, 0, 0);
|
||||
const check_fbo_status = gl.checkNamedFramebufferStatus(self.shadow_framebuffer, gl.DRAW_FRAMEBUFFER);
|
||||
if (check_fbo_status != gl.FRAMEBUFFER_COMPLETE) {
|
||||
std.log.debug("Shadow Framebuffer Incomplete: {}\n", .{check_fbo_status});
|
||||
}
|
||||
var finished_dir_lights = false;
|
||||
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.shadow).program);
|
||||
|
||||
gl.viewport(0, 0, 2048, 2048);
|
||||
for (lights) |light_cmd| {
|
||||
const i = lights_buf.count;
|
||||
if (i == lights_buf.lights.len) break;
|
||||
|
||||
const camera_matrix = &self.shadow_matrices;
|
||||
camera_matrix.* = .{
|
||||
.projection = math.orthographic(-4, 4, -4, 4, -5, 5),
|
||||
.view = Mat4.lookAt(
|
||||
Vec3.new(light.pos.x(), light.pos.y(), light.pos.z()).scale(-1),
|
||||
Vec3.zero(),
|
||||
Vec3.up(),
|
||||
),
|
||||
};
|
||||
const light = &lights_buf.lights[i];
|
||||
lights_buf.count += 1;
|
||||
|
||||
const shadow_view_proj = camera_matrix.projection.mul(camera_matrix.view);
|
||||
const light_frustum = math.Frustum.new(shadow_view_proj);
|
||||
light.shadow_vp = shadow_view_proj;
|
||||
|
||||
gl.namedBufferSubData(self.shadow_matrices_buffer, 0, @sizeOf(CameraMatrices), std.mem.asBytes(&self.shadow_matrices));
|
||||
checkGLError();
|
||||
|
||||
gl.clear(gl.DEPTH_BUFFER_BIT);
|
||||
gl.bindBufferBase(gl.UNIFORM_BUFFER, UBO.CameraMatrices.value(), self.shadow_matrices_buffer);
|
||||
|
||||
self.renderShadow(&light_frustum);
|
||||
} else {
|
||||
// Point Light
|
||||
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.cube_shadow).program);
|
||||
|
||||
const pos = Vec3.new(light.pos.x(), light.pos.y(), light.pos.z());
|
||||
light.shadow_vp = Mat4.fromTranslate(pos.negate());
|
||||
// For each cube face
|
||||
for (cube_camera_dirs, 0..) |cam_dir, face| {
|
||||
gl.namedFramebufferTextureLayer(self.shadow_framebuffer, gl.DEPTH_ATTACHMENT, self.cube_shadow_texture_array, 0, @intCast(i * 6 + face));
|
||||
switch (light_cmd) {
|
||||
.directional => |dir_light| {
|
||||
light.pos = dir_light.dir.toVec4(0);
|
||||
light.color_radius = dir_light.color.toVec4(0);
|
||||
gl.namedFramebufferTextureLayer(self.shadow_framebuffer, gl.DEPTH_ATTACHMENT, self.shadow_texture_array, 0, 0);
|
||||
const check_fbo_status = gl.checkNamedFramebufferStatus(self.shadow_framebuffer, gl.DRAW_FRAMEBUFFER);
|
||||
if (check_fbo_status != gl.FRAMEBUFFER_COMPLETE) {
|
||||
std.log.debug("Shadow Framebuffer Incomplete: {}\n", .{check_fbo_status});
|
||||
}
|
||||
|
||||
gl.viewport(0, 0, 512, 512);
|
||||
gl.viewport(0, 0, 2048, 2048);
|
||||
|
||||
const near_far = Vec2.new(0.1, 10);
|
||||
const camera_matrix = &self.shadow_matrices;
|
||||
camera_matrix.* = .{
|
||||
.projection = math.perspective(90, 1, near_far.x(), near_far.y()),
|
||||
.projection = math.orthographic(-4, 4, -4, 4, -5, 5),
|
||||
.view = Mat4.lookAt(
|
||||
pos,
|
||||
pos.add(cam_dir.target),
|
||||
cam_dir.up,
|
||||
dir_light.dir.scale(-1),
|
||||
Vec3.zero(),
|
||||
Vec3.up(),
|
||||
),
|
||||
};
|
||||
|
||||
const shadow_view_proj = camera_matrix.projection.mul(camera_matrix.view);
|
||||
const light_frustum = math.Frustum.new(shadow_view_proj);
|
||||
//light.shadow_vp = shadow_view_proj;
|
||||
light.near_far = near_far;
|
||||
gl.uniform2f(Uniform.NearFarPlanes.value(), near_far.x(), near_far.y());
|
||||
light.shadow_vp = shadow_view_proj;
|
||||
|
||||
gl.namedBufferSubData(self.shadow_matrices_buffer, 0, @sizeOf(CameraMatrices), std.mem.asBytes(&self.shadow_matrices));
|
||||
checkGLError();
|
||||
@ -482,13 +497,62 @@ pub fn finish(self: *Render) void {
|
||||
gl.bindBufferBase(gl.UNIFORM_BUFFER, UBO.CameraMatrices.value(), self.shadow_matrices_buffer);
|
||||
|
||||
self.renderShadow(&light_frustum);
|
||||
}
|
||||
},
|
||||
.point => |point_light| {
|
||||
if (!finished_dir_lights) {
|
||||
finished_dir_lights = true;
|
||||
gl.useProgram(self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.cube_shadow).program);
|
||||
}
|
||||
|
||||
const pos = point_light.pos;
|
||||
light.pos = pos.toVec4(1);
|
||||
light.color_radius = point_light.color.toVec4(point_light.radius);
|
||||
|
||||
light.shadow_vp = Mat4.fromTranslate(pos.negate());
|
||||
// For each cube face
|
||||
for (cube_camera_dirs, 0..) |cam_dir, face| {
|
||||
gl.namedFramebufferTextureLayer(self.shadow_framebuffer, gl.DEPTH_ATTACHMENT, self.cube_shadow_texture_array, 0, @intCast(i * 6 + face));
|
||||
const check_fbo_status = gl.checkNamedFramebufferStatus(self.shadow_framebuffer, gl.DRAW_FRAMEBUFFER);
|
||||
if (check_fbo_status != gl.FRAMEBUFFER_COMPLETE) {
|
||||
std.log.debug("Shadow Framebuffer Incomplete: {}\n", .{check_fbo_status});
|
||||
}
|
||||
|
||||
gl.viewport(0, 0, 512, 512);
|
||||
|
||||
const range = pointLightRange(&point_light);
|
||||
|
||||
const near_far = Vec2.new(0.1, range);
|
||||
const camera_matrix = &self.shadow_matrices;
|
||||
camera_matrix.* = .{
|
||||
.projection = math.perspective(90, 1, near_far.x(), near_far.y()),
|
||||
.view = Mat4.lookAt(
|
||||
pos,
|
||||
pos.add(cam_dir.target),
|
||||
cam_dir.up,
|
||||
),
|
||||
};
|
||||
|
||||
const shadow_view_proj = camera_matrix.projection.mul(camera_matrix.view);
|
||||
const light_frustum = math.Frustum.new(shadow_view_proj);
|
||||
light.shadow_vp = shadow_view_proj;
|
||||
light.near_far = near_far;
|
||||
gl.uniform2f(Uniform.NearFarPlanes.value(), near_far.x(), near_far.y());
|
||||
|
||||
gl.namedBufferSubData(self.shadow_matrices_buffer, 0, @sizeOf(CameraMatrices), std.mem.asBytes(&self.shadow_matrices));
|
||||
checkGLError();
|
||||
|
||||
gl.clear(gl.DEPTH_BUFFER_BIT);
|
||||
gl.bindBufferBase(gl.UNIFORM_BUFFER, UBO.CameraMatrices.value(), self.shadow_matrices_buffer);
|
||||
|
||||
self.renderShadow(&light_frustum);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Light world space to view space
|
||||
for (lights.lights[0..lights.count]) |*light| {
|
||||
for (lights_buf.lights[0..lights_buf.count]) |*light| {
|
||||
light.pos = self.camera.view_mat.mulByVec4(light.pos);
|
||||
}
|
||||
|
||||
@ -708,6 +772,14 @@ pub fn finish(self: *Render) void {
|
||||
//c.SDL_Delay(1);
|
||||
}
|
||||
|
||||
pub fn pointLightRange(self: *const PointLight) f32 {
|
||||
const color = self.color;
|
||||
const light_intensity = @max(color.x(), color.y(), color.z());
|
||||
|
||||
const cutoff = 0.005;
|
||||
return self.radius * (@sqrt(light_intensity / cutoff) - 1);
|
||||
}
|
||||
|
||||
const CubeCameraDir = struct {
|
||||
face: gl.GLenum,
|
||||
target: Vec3,
|
||||
@ -869,7 +941,7 @@ const CameraMatrices = extern struct {
|
||||
projection: Mat4 = Mat4.identity(),
|
||||
view: Mat4 = Mat4.identity(),
|
||||
};
|
||||
pub const PointLight = extern struct {
|
||||
pub const Light = extern struct {
|
||||
pos: Vec4, // x, y, z, w - vPos
|
||||
color_radius: Vec4, // x, y, z - color, w - radius
|
||||
shadow_vp: Mat4 = Mat4.identity(),
|
||||
@ -877,8 +949,8 @@ pub const PointLight = extern struct {
|
||||
};
|
||||
|
||||
// TODO: rename
|
||||
pub const PointLightArray = extern struct {
|
||||
lights: [MAX_POINT_LIGHTS]PointLight,
|
||||
pub const LightArray = extern struct {
|
||||
lights: [MAX_LIGHTS]Light,
|
||||
count: c_uint,
|
||||
};
|
||||
|
||||
|
65
src/game.zig
65
src/game.zig
@ -434,44 +434,6 @@ export fn game_update() bool {
|
||||
|
||||
// Collect point lights
|
||||
{
|
||||
const point_lights = gmem.render.getPointLights();
|
||||
point_lights.count = 0;
|
||||
|
||||
for (0..gmem.world.entity_count) |i| {
|
||||
const ent = &gmem.world.entities[i];
|
||||
if (!ent.data.flags.active) continue;
|
||||
|
||||
if (ent.data.flags.point_light) {
|
||||
const pos = ent.globalMatrix(&gmem.world).extractTranslation();
|
||||
var pos4 = Vec4.new(pos.x(), pos.y(), pos.z(), 1.0);
|
||||
|
||||
const color = ent.data.light.premultipliedColor();
|
||||
point_lights.lights[point_lights.count] = .{
|
||||
.pos = Vec4.new(pos4.x(), pos4.y(), pos4.z(), 1),
|
||||
.color_radius = Vec4.new(color.x(), color.y(), color.z(), ent.data.point_light.radius),
|
||||
};
|
||||
point_lights.count += 1;
|
||||
if (point_lights.count == Render.MAX_POINT_LIGHTS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ent.data.flags.dir_light) {
|
||||
const dir4 = ent.globalMatrix(&gmem.world).mulByVec4(Vec4.forward());
|
||||
const color = ent.data.light.premultipliedColor();
|
||||
point_lights.lights[point_lights.count] = .{
|
||||
.pos = dir4,
|
||||
.color_radius = Vec4.new(color.x(), color.y(), color.z(), 1),
|
||||
};
|
||||
point_lights.count += 1;
|
||||
if (point_lights.count == Render.MAX_POINT_LIGHTS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gmem.render.flushUBOs();
|
||||
|
||||
// Render meshes and lights
|
||||
for (0..gmem.world.entity_count) |i| {
|
||||
const ent = &gmem.world.entities[i];
|
||||
if (!ent.data.flags.active) continue;
|
||||
@ -490,7 +452,34 @@ export fn game_update() bool {
|
||||
.transform = ent.globalMatrix(&gmem.world).*,
|
||||
});
|
||||
}
|
||||
|
||||
if (ent.data.flags.point_light) {
|
||||
const pos = ent.globalMatrix(&gmem.world).extractTranslation();
|
||||
const color = ent.data.light.premultipliedColor();
|
||||
|
||||
gmem.render.drawLight(.{
|
||||
.point = .{
|
||||
.pos = pos,
|
||||
.radius = ent.data.point_light.radius,
|
||||
.color = color,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (ent.data.flags.dir_light) {
|
||||
const dir4 = ent.globalMatrix(&gmem.world).mulByVec4(Vec4.forward());
|
||||
const color = ent.data.light.premultipliedColor();
|
||||
|
||||
gmem.render.drawLight(.{
|
||||
.directional = .{
|
||||
.dir = dir4.toVec3(),
|
||||
.color = color,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: get rid of this
|
||||
gmem.render.flushUBOs();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user