void csm_calc_minsphere(struct sphere* bounds, const struct frustum* f, const struct mat3f* view, const struct mat3f* view_inv) { /* reference: * http://www.gamedev.net/topic/604797-minimum-bounding-sphere-of-a-frustum/ */ struct vec3f a; struct vec3f b; struct vec3f p; struct vec3f tmp; vec3_transformsrt(&a, &f->points[1], view); vec3_transformsrt(&b, &f->points[5], view); float z = (vec3_dot(&b, &b) - vec3_dot(&a, &a))/(2.0f*(b.z - a.z)); vec3_setf(&p, 0.0f, 0.0f, z); vec3_transformsrt(&p, &p, view_inv); sphere_setf(bounds, p.x, p.y, p.z, vec3_len(vec3_sub(&tmp, &f->points[5], &p)) + 0.01f); }
const struct vec4f* gfx_csm_get_cascades(const struct mat3f* view) { static struct vec4f cascades[CSM_CASCADE_CNT]; struct vec4f center; for (uint i = 0; i < CSM_CASCADE_CNT; i++) { const struct sphere* s = &g_csm->cascades[i].bounds; vec3_setf(¢er, s->x, s->y, s->z); vec3_transformsrt(¢er, ¢er, view); vec4_setf(&cascades[i], center.x, center.y, center.z, s->r); } return cascades; }
struct sphere* sphere_xform(struct sphere* rs, const struct sphere* s, const struct mat3f* m) { /* we have to transform radius by scale value of transform matrix * to determine scale value, we find the highest scale component of the matrix */ float xlen = m->m11*m->m11 + m->m12*m->m12 + m->m13*m->m13; float ylen = m->m21*m->m21 + m->m22*m->m22 + m->m23*m->m23; float zlen = m->m31*m->m31 + m->m32*m->m32 + m->m33*m->m33; float isqr_scale = maxf(xlen, ylen); isqr_scale = maxf(isqr_scale, zlen); /* transform center */ struct vec4f c; vec3_setf(&c, s->x, s->y, s->z); vec3_transformsrt(&c, &c, m); return sphere_setf(rs, c.x, c.y, c.z, s->r*sqrtf(isqr_scale)); }
void gfx_csm_prepare(const struct gfx_view_params* params, const struct vec3f* light_dir, const struct aabb* world_bounds) { static const float tolerance = 10.0f; struct vec3f dir; vec3_setv(&dir, light_dir); float texoffset_x = 0.5f + (0.5f/g_csm->shadowmap_size); float texoffset_y = 0.5f + (0.5f/g_csm->shadowmap_size); struct mat3f view_inv; struct mat4f tex_mat; struct mat4f tmp_mat; float splits[CSM_CASCADE_CNT+1]; mat3_setf(&view_inv, params->view.m11, params->view.m21, params->view.m31, params->view.m12, params->view.m22, params->view.m32, params->view.m13, params->view.m23, params->view.m33, params->cam_pos.x, params->cam_pos.y, params->cam_pos.z); mat4_setf(&tex_mat, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, texoffset_x, texoffset_y, 0.0f, 1.0f); float csm_far = minf(CSM_FAR_MAX, params->cam->ffar); csm_split_range(params->cam->fnear, csm_far, splits); /* calculate cascades */ struct frustum f; /* frustum points for cascades */ struct plane vp_planes[6]; for (uint i = 0; i < CSM_CASCADE_CNT; i++) { cam_calc_frustumcorners(params->cam, (struct vec3f*)f.points, &splits[i], &splits[i+1]); csm_calc_minsphere(&g_csm->cascades[i].bounds, &f, ¶ms->view, &view_inv); memcpy(&g_csm->cascade_frusts[i], &f, sizeof(f)); /* cascade matrixes: first we find two extreme points of the world, related to cascade */ struct vec3f scenter; struct ray r; struct plane upper_plane; struct plane lower_plane; struct vec3f upper_pt; struct vec3f lower_pt; struct vec3f xaxis; struct vec3f yaxis; struct vec3f viewpos; struct vec3f tmp; struct vec3f lowerpt_vs; float sr = g_csm->cascades[i].bounds.r; vec3_setf(&scenter, g_csm->cascades[i].bounds.x, g_csm->cascades[i].bounds.y, g_csm->cascades[i].bounds.z); ray_setv(&r, &scenter, &dir); plane_setv(&upper_plane, &g_vec3_unity_neg, fabs(world_bounds->maxpt.y)); plane_setv(&lower_plane, &g_vec3_unity, fabs(world_bounds->minpt.y)); vec3_sub(&upper_pt, &r.pt, vec3_muls(&tmp, &dir, fabs(ray_intersect_plane(&r, &upper_plane)) + tolerance)); vec3_add(&lower_pt, &r.pt, vec3_muls(&tmp, &dir, fabs(ray_intersect_plane(&r, &lower_plane)) + tolerance)); /* view matrix of light view for the cascade : * dir = light_dir * up = (1, 0, 0) * pos = sphere center */ vec3_norm(&xaxis, vec3_cross(&tmp, &g_vec3_unitx, &dir)); vec3_cross(&yaxis, &dir, &xaxis); vec3_sub(&viewpos, &upper_pt, vec3_muls(&tmp, &dir, tolerance)); mat3_setf(&g_csm->cascades[i].view, xaxis.x, yaxis.x, dir.x, xaxis.y, yaxis.y, dir.y, xaxis.z, yaxis.z, dir.z, -vec3_dot(&xaxis, &viewpos), -vec3_dot(&yaxis, &viewpos), -vec3_dot(&dir, &viewpos)); /* orthographic projection matrix for cascade */ vec3_transformsrt(&lowerpt_vs, &lower_pt, &g_csm->cascades[i].view); float nnear = tolerance*0.5f; float nfar = lowerpt_vs.z + sr; csm_calc_orthoproj(&g_csm->cascades[i].proj, sr*2.0f + 1.0f/g_csm->shadowmap_size, sr*2.0f + 1.0f/g_csm->shadowmap_size, nnear, nfar); g_csm->cascades[i].nnear = nnear; g_csm->cascades[i].nfar = nfar; /* calculate final matrix */ mat3_mul4(&g_csm->cascade_vps[i], &g_csm->cascades[i].view, &g_csm->cascades[i].proj); cam_calc_frustumplanes(vp_planes, &g_csm->cascade_vps[i]); csm_calc_cascadeplanes(&g_csm->cascade_planes[i*4], vp_planes, &g_csm->cascade_vps[i]); csm_round_mat(&g_csm->cascade_vps[i], &g_csm->cascade_vps[i], g_csm->shadowmap_size); mat4_mul(&g_csm->shadow_mats[i], mat3_mul4(&tmp_mat, &view_inv, &g_csm->cascade_vps[i]), &tex_mat); } /* caculate shadow area bounds (aabb) */ struct aabb cascade_near; struct aabb cascade_far; aabb_setzero(&g_csm->frustum_bounds); aabb_from_sphere(&cascade_near, &g_csm->cascades[0].bounds); aabb_from_sphere(&cascade_far, &g_csm->cascades[CSM_CASCADE_CNT-1].bounds); aabb_merge(&g_csm->frustum_bounds, &cascade_near, &cascade_far); vec3_setv(&g_csm->light_dir, &dir); }