Cascaded shadow mapping support

This commit is contained in:
mid 2025-05-10 18:17:33 +03:00
parent 68264fcf87
commit f20d32eb3a
2 changed files with 153 additions and 87 deletions

227
src/k3.c
View File

@ -584,6 +584,16 @@ static struct k3Light *Lights;
void k3SetLights(size_t count, struct k3Light *lightsNew) { void k3SetLights(size_t count, struct k3Light *lightsNew) {
LightCount = count; LightCount = count;
Lights = lightsNew; Lights = lightsNew;
for(int i = 0; i < count; i++) {
if(Lights[i].type == k3_DIRECTIONAL) {
if(Lights[i].dir.cascadeCount == 0) {
Lights[i].dir.cascadeCount = 1;
} else if(Lights[i].dir.cascadeCount > 3) {
Lights[i].dir.cascadeCount = 3;
}
}
}
} }
struct k3Light *k3GetLights(size_t *a) { struct k3Light *k3GetLights(size_t *a) {
@ -617,15 +627,18 @@ static mat4 CamMat;
static mat4 ProjMat; static mat4 ProjMat;
struct LightShadow { struct LightShadow {
mat4 vp; uint8_t vpCount;
mat4 vp[6];
vec4 atlasSegment; vec4 atlasSegment;
#ifdef k3_IRREGULAR_SHADOWS #ifdef k3_IRREGULAR_SHADOWS
GLuint multimapEls; // buffer GLuint multimapEls; // buffer
GLuint multimapHeads; // image GLuint multimapHeads; // image
#endif #endif
} __attribute__((aligned(16))); } __attribute__((aligned(16)));
static size_t LightShadowsCount, LightShadowsCapacity;
static struct LightShadow *LightShadows; static struct LightShadow *LightShadows;
static size_t LightShadowsCount = 0, LightShadowsCapacity = 0;
static struct k3Offscreen *ShadowAtlas; static struct k3Offscreen *ShadowAtlas;
static bool LightShadowIrregularMode = false; static bool LightShadowIrregularMode = false;
@ -818,17 +831,22 @@ static void setup_glsl_lighting_uniforms(GLuint bound, int lightsStart, int ligh
lightsCount = LightCount - lightsStart; lightsCount = LightCount - lightsStart;
} }
vec4 settings1[4]; if(lightsCount) {
vec4 settings2[4]; // raise(SIGINT);
}
vec4 colors[4]; vec4 settings1[4] = {};
memset(colors, 0, sizeof(colors)); vec4 settings2[4] = {};
vec4 colors[4] = {};
for(int i = 0; i < lightsCount; i++) { for(int i = 0; i < lightsCount; i++) {
struct k3Light *l = &Lights[i + lightsStart]; struct k3Light *l = &Lights[i + lightsStart];
if(l->type == k3_DIRECTIONAL) { if(l->type == k3_DIRECTIONAL) {
glm_vec4_copy(l->dir.direction, settings1[i]); glm_vec4_copy(l->dir.direction, settings1[i]);
settings2[i][0] = l->dir.cascadeCount;
} else if(l->type == k3_SPOT) { } else if(l->type == k3_SPOT) {
glm_vec4_copy(l->spot.position, settings1[i]); glm_vec4_copy(l->spot.position, settings1[i]);
@ -879,20 +897,26 @@ static void setup_glsl_shadow_uniforms(GLuint bound, int atlasUnit, int lightsSt
k3Log(k3_ERR, "Max 4 lights per pass"); k3Log(k3_ERR, "Max 4 lights per pass");
} }
if(!LightShadows) return; if(!ShadowAtlas) return;
if(LightShadowIrregularMode) { if(LightShadowIrregularMode) {
assert(k3IsCore); assert(k3IsCore);
glUniform1i(glGetUniformLocation(bound, "u_pixelsinshadow"), 0); glUniform1i(glGetUniformLocation(bound, "u_pixelsinshadow"), 0);
} else { } else {
mat4 m[4]; size_t vpi = 0;
mat4 m[6];
memset(m, 0, sizeof(m)); memset(m, 0, sizeof(m));
vec4 seg[4]; vec4 seg[4];
for(int i = 0; i < lightsCount; i++) { for(int i = 0; i < lightsCount; i++) {
glm_mat4_copy(LightShadows[i + lightsStart].vp, m[i]); for(int z = 0; z < LightShadows[i + lightsStart].vpCount; z++) {
glm_mat4_copy(LightShadows[i + lightsStart].vp[z], m[vpi]);
vpi++;
}
glm_vec4_copy(LightShadows[i + lightsStart].atlasSegment, seg[i]); glm_vec4_copy(LightShadows[i + lightsStart].atlasSegment, seg[i]);
} }
@ -905,12 +929,12 @@ static void setup_glsl_shadow_uniforms(GLuint bound, int atlasUnit, int lightsSt
} }
if(!k3IsCore) { if(!k3IsCore) {
glUniformMatrix4fvARB(glGetUniformLocationARB(bound, "u_shadows0vp"), 4, GL_FALSE, (float*) m); glUniformMatrix4fvARB(glGetUniformLocationARB(bound, "u_shadows0vp"), vpi, GL_FALSE, (float*) m);
glUniform4fvARB(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg); glUniform4fvARB(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg);
glUniform1iARB(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit); glUniform1iARB(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit);
} else { } else {
glUniformMatrix4fv(glGetUniformLocationARB(bound, "u_shadows0vp"), 4, GL_FALSE, (float*) m); glUniformMatrix4fv(glGetUniformLocationARB(bound, "u_shadows0vp"), vpi, GL_FALSE, (float*) m);
glUniform4fv(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg); glUniform4fv(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg);
glUniform1i(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit); glUniform1i(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit);
@ -1659,76 +1683,109 @@ void k3PassDepthOnly(mat4 projection, mat4 cam, int clear, int cull) {
glFrontFace(GL_CCW); glFrontFace(GL_CCW);
} }
static size_t compute_light_views(mat4 **_projs, mat4 **_cams, char **_isCube) { static void split_frustum(mat4 proj, int cascades, mat4 croppeds[]) {
mat4 *projs = _mm_malloc(sizeof(*projs) * LightCount * 6, 16); float fovy = glm_persp_fovy(proj);
mat4 *cams = _mm_malloc(sizeof(*cams) * LightCount * 6, 16); float aspect = glm_persp_aspect(proj);
char *isCube = _mm_malloc(sizeof(*isCube) * LightCount * 6, 1);
size_t s = 0; float near, far;
glm_persp_decomp_near(proj, &near);
glm_persp_decomp_far(proj, &far);
float depthChunk = (far - near) / cascades;
for(int c = 0; c < cascades; c++) {
glm_perspective(fovy, aspect, near + c * depthChunk, near + (c + 1) * depthChunk, croppeds[c]);
}
}
struct LightView {
uint8_t cams;
uint8_t projs;
mat4 c[6];
mat4 p[3];
};
static size_t compute_light_views(struct LightView lv[]) {
size_t totalTilesUsed = 0;
for(int i = 0; i < LightCount; i++) { for(int i = 0; i < LightCount; i++) {
isCube[s] = 0;
struct k3Light *l = &Lights[i]; struct k3Light *l = &Lights[i];
/*if(!l->castShadow) {
continue;
}*/
if(l->type == k3_DIRECTIONAL) { if(l->type == k3_DIRECTIONAL) {
glm_ortho(-60, 60, -60, 60, -60, 60, projs[s]); size_t CASCADE_COUNT = l->dir.cascadeCount;
mat4 invmainproj; lv[i].projs = CASCADE_COUNT;
glm_mat4_inv_fast(ProjMat, invmainproj); lv[i].cams = CASCADE_COUNT;
mat4 frustummat; mat4 croppeds[CASCADE_COUNT];
glm_mat4_mul(CamMat, invmainproj, frustummat); split_frustum(ProjMat, CASCADE_COUNT, croppeds);
vec4 corners[8]; for(int cascade = 0; cascade < CASCADE_COUNT; cascade++) {
glm_frustum_corners(frustummat, corners); mat4 invmainproj;
glm_mat4_inv_fast(croppeds[cascade], invmainproj);
vec4 viewcenter; mat4 frustummat;
glm_frustum_center(corners, viewcenter); glm_mat4_mul(CamMat, invmainproj, frustummat);
mat4 view; vec4 corners[8];
glm_look_anyup(viewcenter, l->dir.direction, view); glm_frustum_corners(frustummat, corners);
glm_mat4_inv_fast(view, cams[s]); vec4 viewcenter;
glm_frustum_center(corners, viewcenter);
s++; mat4 lightview;
glm_look_anyup(viewcenter, l->dir.direction, lightview);
vec4 minaabb = {+HUGE_VALF, +HUGE_VALF, +HUGE_VALF};
vec4 maxaabb = {-HUGE_VALF, -HUGE_VALF, -HUGE_VALF};
for(int c = 0; c < 8; c++) {
vec4 lightspaceCorner;
glm_mat4_mulv(lightview, corners[c], lightspaceCorner);
glm_vec4_minv(minaabb, lightspaceCorner, minaabb);
glm_vec4_maxv(maxaabb, lightspaceCorner, maxaabb);
}
glm_ortho(minaabb[0], maxaabb[0], minaabb[1], maxaabb[1], minaabb[2] - 50, maxaabb[2], lv[i].p[cascade]);
glm_mat4_inv_fast(lightview, lv[i].c[cascade]);
}
} else if(l->type == k3_SPOT || l->type == k3_HALF_OMNI) { } else if(l->type == k3_SPOT || l->type == k3_HALF_OMNI) {
glm_perspective(l->spot.angle, 1, 0.1, l->radius, projs[s]); lv[i].projs = 1;
lv[i].cams = 1;
glm_perspective(l->spot.angle, 1, 0.1, l->radius, lv[i].p[0]);
mat4 view; mat4 view;
glm_look_anyup(l->spot.position, l->spot.direction, view); glm_look_anyup(l->spot.position, l->spot.direction, view);
glm_mat4_inv_fast(view, cams[s]); glm_mat4_inv_fast(view, lv[i].c[0]);
s++;
} else if(l->type == k3_OMNI) { } else if(l->type == k3_OMNI) {
static const vec3 dirs[] = {{0, 0, -1}, {0, 0, 1}, {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}}; static const vec3 dirs[] = {{0, 0, -1}, {0, 0, 1}, {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}};
static const vec3 ups[] = {{0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, -1}}; static const vec3 ups[] = {{0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, -1}};
lv[i].projs = 1;
lv[i].cams = 6;
mat4 proj;
glm_perspective(glm_rad(120), 1, 0.1, l->radius, proj);
glm_mat4_copy(proj, lv[i].p[0]);
for(int d = 0; d < 6; d++) { for(int d = 0; d < 6; d++) {
glm_perspective(glm_rad(120), 1, 0.1, l->radius, projs[s]);
mat4 view; mat4 view;
glm_look(l->omni.position, dirs[d], ups[d], view); glm_look(l->omni.position, dirs[d], ups[d], view);
glm_mat4_inv_fast(view, cams[s]); glm_mat4_inv_fast(view, lv[i].c[d]);
isCube[s] = d != 0;
s++;
} }
} }
totalTilesUsed += lv[i].cams;
} }
*_projs = projs; return totalTilesUsed;
*_cams = cams;
*_isCube = isCube;
return s;
} }
#ifdef k3_IRREGULAR_SHADOWS #ifdef k3_IRREGULAR_SHADOWS
@ -1892,7 +1949,7 @@ void k3PassIrregular(struct k3Offscreen *mainview, mat4 mainproj, mat4 maincam)
mat4 *cams; mat4 *cams;
char *isCube; char *isCube;
size_t count = compute_light_views(&projs, &cams, &isCube); count = compute_light_views(&projs, &cams, &isCube);
LightShadowsCount = count; LightShadowsCount = count;
if(LightShadowsCount > LightShadowsCapacity) { if(LightShadowsCount > LightShadowsCapacity) {
@ -1995,11 +2052,8 @@ void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr) {
glm_mat4_copy(projection, ProjMat); glm_mat4_copy(projection, ProjMat);
glm_mat4_copy(cam, CamMat); glm_mat4_copy(cam, CamMat);
mat4 *projs; struct LightView *views = alloca(sizeof(*views) * LightCount);
mat4 *cams; size_t totalTilesUsed = compute_light_views(views);
char *isCube;
size_t count = compute_light_views(&projs, &cams, &isCube);
ShadowAtlas = offscr; ShadowAtlas = offscr;
@ -2014,7 +2068,7 @@ void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr) {
LightShadowsCapacity = LightShadowsCount; LightShadowsCapacity = LightShadowsCount;
} }
if(count == 0) { if(totalTilesUsed == 0) {
return; return;
} }
@ -2025,45 +2079,56 @@ void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr) {
int cellsPerDimension = 0; int cellsPerDimension = 0;
if(count == 1) { if(totalTilesUsed == 1) {
cellsPerDimension = 1; cellsPerDimension = 1;
} else { } else {
int cellsTotalLog = 1; int cellsTotalSqrt = 1;
while((1 << cellsTotalLog) < count) { while((cellsTotalSqrt * cellsTotalSqrt) < totalTilesUsed) {
cellsTotalLog++; cellsTotalSqrt++;
} }
cellsPerDimension = 1 << ((cellsTotalLog + 1) / 2); cellsPerDimension = cellsTotalSqrt;
} }
uint16_t sz = k3TexSzX(offscr->depth); uint16_t sz = k3TexSzX(offscr->depth);
uint16_t cellSz = sz / cellsPerDimension; float cellSz = (float) sz / cellsPerDimension;
size_t s = 0;
k3BeginOffscreen(offscr); k3BeginOffscreen(offscr);
for(size_t i = 0; i < count; i++) { size_t i = 0;
int cellX = i % cellsPerDimension; for(size_t li = 0; li < LightCount; li++) {
int cellY = i / cellsPerDimension; struct LightView *lv = &views[li];
int vp[] = {cellX * cellSz, cellY * cellSz, cellSz, cellSz}; for(int camIdx = 0; camIdx < lv->cams; camIdx++) {
int cellX = i % cellsPerDimension;
int cellY = i / cellsPerDimension;
mat4 view = GLM_MAT4_IDENTITY_INIT; int vp[] = {
glm_mat4_inv(cams[i], view); (int) roundf(cellX * cellSz),
(int) roundf(cellY * cellSz),
(int) cellSz,
(int) cellSz
};
if(!isCube[i]) { mat4 view = GLM_MAT4_IDENTITY_INIT;
glm_mat4_mul(projs[i], view, LightShadows[s].vp); glm_mat4_inv(lv->c[camIdx], view);
glm_vec4_copy((vec4) {i, (float) cellSz / sz, 0, 0}, LightShadows[s].atlasSegment);
s++; mat4 proj = GLM_MAT4_IDENTITY_INIT;
glm_mat4_copy(lv->projs == 1 ? lv->p[0] : lv->p[camIdx], proj);
LightShadows[li].vpCount = lv->projs;
if(lv->projs > 1 || camIdx == 0) {
glm_mat4_mul(proj, view, LightShadows[li].vp[camIdx]);
}
if(camIdx == 0) {
glm_vec4_copy((vec4) {i, (float) cellSz / sz, 0, 0}, LightShadows[li].atlasSegment);
}
glViewport(vp[0], vp[1], vp[2], vp[3]);
k3PassDepthOnly(proj, lv->c[camIdx], i == 0, false);
i++;
} }
glViewport(vp[0], vp[1], vp[2], vp[3]);
k3PassDepthOnly(projs[i], cams[i], i == 0, false);
} }
k3EndOffscreen(offscr); k3EndOffscreen(offscr);
_mm_free(projs);
_mm_free(cams);
_mm_free(isCube);
} }
void k3BatchClear() { void k3BatchClear() {

View File

@ -136,6 +136,7 @@ struct k3Light {
_Alignas(16) union { _Alignas(16) union {
struct { struct {
vec4 direction; vec4 direction;
uint8_t cascadeCount;
} dir; } dir;
struct { struct {
vec4 position; vec4 position;