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

239
src/k3.c
View File

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

View File

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