Cascaded shadow maps working!

This commit is contained in:
sergeypdev 2024-03-12 16:33:24 +04:00
parent 5748f3b27e
commit acbaec8e1d
3 changed files with 49 additions and 13 deletions

View File

@ -43,7 +43,7 @@ int getCSMSplitCount(int lightIdx) {
int getCSMSplit(int lightIdx, float depth) {
int totalSplits = getCSMSplitCount(lightIdx);
for (int i = 1; i < totalSplits; i++) {
for (int i = 0; i < totalSplits; i++) {
if (depth > lights[lightIdx].csm_split_points[i]) {
return i;
}
@ -180,11 +180,21 @@ vec2 poissonDisk[4] = vec2[](
vec2( 0.34495938, 0.29387760 )
);
const vec3 csm_split_colors[4] = vec3[](
vec3(0f, 1f, 0.17f),
vec3(1f, 0.2f, 0.6f),
vec3(0.17f, 0.78f, 1f),
vec3(0.91f, 0.93f, 0.64f)
);
float map(float value, float min1, float max1, float min2, float max2) {
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
vec3 microfacetModel(Material mat, int light_idx, Light light, vec3 P, vec3 N) {
int csm_split_idx = getCSMSplit(light_idx, P.z);
mat.albedo = mix(mat.albedo, csm_split_colors[csm_split_idx], 0.8);
vec3 diffuseBrdf = vec3(0); // metallic
if (!mat.metallic) {
diffuseBrdf = mat.albedo;
@ -251,8 +261,6 @@ vec3 microfacetModel(Material mat, int light_idx, Light light, vec3 P, vec3 N) {
// Directional shadow
vec2 shadow_map_texel_size = 1.0 / vec2(textureSize(shadow_maps, 0));
shadow_offset *= shadow_map_texel_size.x;
float view_depth = (light.view_mat * vec4(VertexOut.wPos, 1.0)).z;
int csm_split_idx = getCSMSplit(light_idx, view_depth);
vec4 shadow_pos = light.view_proj_mats[csm_split_idx] * vec4(VertexOut.wPos, 1.0);
shadow_pos.xy = (light.view_proj_mats[csm_split_idx] * (vec4(VertexOut.wPos, 1.0) + shadow_offset)).xy;
shadow_pos /= shadow_pos.w;

View File

@ -19,7 +19,12 @@ 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 = 2;
pub const CSM_SPLITS = 4;
// affects how cascades are split
// 0 - exponential
// 1 - uniform
// 0.5 - mix between the two
pub const CSM_EXPO_UNIFORM_FACTOR = 0.8;
pub const Render = @This();
@ -467,6 +472,9 @@ pub fn finish(self: *Render) void {
// Light shadow maps
{
gl.enable(gl.DEPTH_CLAMP);
defer gl.disable(gl.DEPTH_CLAMP);
gl.bindVertexArray(self.shadow_vao);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, self.shadow_framebuffer);
@ -500,10 +508,21 @@ pub fn finish(self: *Render) void {
light.params.shadow_map_idx = shadow_map_idx;
light.params.csm_split_count = @floatFromInt(CSM_SPLITS);
const csm_split_z_diff = (self.camera.far - self.camera.near) / @as(f32, @floatFromInt(CSM_SPLITS));
var splits: [CSM_SPLITS + 1]f32 = undefined;
const splits_count_f: f32 = @floatFromInt(CSM_SPLITS);
for (0..CSM_SPLITS + 1) |split_idx| {
const split_idx_f: f32 = @floatFromInt(split_idx);
const split_i_over_n = split_idx_f / splits_count_f;
const expo_split = self.camera.near * std.math.pow(f32, self.camera.far / self.camera.near, split_i_over_n);
const uniform_split = self.camera.near + split_i_over_n * (self.camera.far - self.camera.near);
const split = CSM_EXPO_UNIFORM_FACTOR * expo_split + (1.0 - CSM_EXPO_UNIFORM_FACTOR) * uniform_split;
splits[split_idx] = split;
}
for (0..CSM_SPLITS) |split_idx| {
var split_far: f32 = 0;
const split_near = splits[split_idx];
const split_far = splits[split_idx + 1];
gl.namedFramebufferTextureLayer(self.shadow_framebuffer, gl.DEPTH_ATTACHMENT, self.shadow_texture_array, 0, @intCast(shadow_map_idx * CSM_SPLITS + split_idx));
const check_fbo_status = gl.checkNamedFramebufferStatus(self.shadow_framebuffer, gl.DRAW_FRAMEBUFFER);
@ -516,9 +535,8 @@ pub fn finish(self: *Render) void {
{
if (self.update_view_frustum) {
var camera = self.camera.*;
camera.near = camera.near + csm_split_z_diff * @as(f32, @floatFromInt(split_idx));
camera.far = camera.near + csm_split_z_diff;
split_far = camera.far;
camera.near = split_near;
camera.far = split_far;
const inv_csm_proj = camera.projection().mul(camera.view_mat).inv();
for (math.ndc_box_corners, 0..) |corner, corner_idx| {
@ -939,7 +957,7 @@ fn renderShadow(self: *Render, frustum: *const math.Frustum) void {
const mesh = self.assetman.resolveMesh(cmd.mesh);
const aabb = math.AABB.fromMinMax(mesh.aabb.min, mesh.aabb.max);
if (!frustum.intersectAABB(aabb.transform(cmd.transform))) {
if (!frustum.intersectAABBSkipNear(aabb.transform(cmd.transform))) {
continue;
}

View File

@ -159,12 +159,12 @@ pub const Frustum = struct {
return !(self.top.isUnder(point) or self.right.isUnder(point) or self.bottom.isUnder(point) or self.left.isUnder(point) or self.near.isUnder(point) or self.far.isUnder(point));
}
pub fn intersectAABB(self: *const Frustum, aabb: AABB) bool {
fn intersectAABBInternal(self: *const Frustum, aabb: AABB, comptime skip_near: bool) bool {
var outside_top_plane = true;
var outside_bottom_plane = true;
var outside_left_plane = true;
var outside_right_plane = true;
var outside_near_plane = true;
var outside_near_plane = !skip_near;
var outside_far_plane = true;
inline for (box_corners) |corner| {
const p = aabb.origin.add(aabb.extents.mul(corner));
@ -173,12 +173,22 @@ pub const Frustum = struct {
outside_bottom_plane = outside_bottom_plane and self.bottom.isUnder(p);
outside_left_plane = outside_left_plane and self.left.isUnder(p);
outside_right_plane = outside_right_plane and self.right.isUnder(p);
outside_near_plane = outside_near_plane and self.near.isUnder(p);
if (!skip_near) {
outside_near_plane = outside_near_plane and self.near.isUnder(p);
}
outside_far_plane = outside_far_plane and self.far.isUnder(p);
}
return !(outside_left_plane or outside_right_plane or outside_bottom_plane or outside_top_plane or outside_near_plane or outside_far_plane);
}
pub fn intersectAABB(self: *const Frustum, aabb: AABB) bool {
return self.intersectAABBInternal(aabb, false);
}
pub fn intersectAABBSkipNear(self: *const Frustum, aabb: AABB) bool {
return self.intersectAABBInternal(aabb, true);
}
};
pub fn checkAABBIntersectionNDC(aabb: *const AABB, mvp: *const Mat4) bool {