Cascaded shadow maps working!
This commit is contained in:
parent
5748f3b27e
commit
acbaec8e1d
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
16
src/math.zig
16
src/math.zig
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user