Cascaded shadow mapping support
This commit is contained in:
parent
68264fcf87
commit
f20d32eb3a
239
src/k3.c
239
src/k3.c
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user