我最终能够通过管理我自己的矩阵和改编 SGI 的 OpenGL Cookbook 中的代码来达到预期的效果。该代码使用来自他的 DarkPlaces Quake 引擎的 LordHavoc 矩阵库。内联 cmets 列出了主要步骤。完整代码如下:
/*
* @brief Projects the model view matrix for the given entity onto the shadow
* plane. A perspective shear is then applied using the standard planar shadow
* deformation from SGI's cookbook, adjusted for Quake's negative planes:
*
* ftp://ftp.sgi.com/opengl/contrib/blythe/advanced99/notes/node192.html
*/
static void R_RotateForMeshShadow_default(const r_entity_t *e, r_shadow_t *s) {
vec4_t pos, normal;
matrix4x4_t proj, shear;
vec_t dot;
if (!e) {
glPopMatrix();
return;
}
const cm_bsp_plane_t *p = &s->plane;
// project the entity onto the shadow plane
vec3_t vx, vy, vz, t;
Matrix4x4_ToVectors(&e->matrix, vx, vy, vz, t);
dot = DotProduct(vx, p->normal);
VectorMA(vx, -dot, p->normal, vx);
dot = DotProduct(vy, p->normal);
VectorMA(vy, -dot, p->normal, vy);
dot = DotProduct(vz, p->normal);
VectorMA(vz, -dot, p->normal, vz);
dot = DotProduct(t, p->normal) - p->dist;
VectorMA(t, -dot, p->normal, t);
Matrix4x4_FromVectors(&proj, vx, vy, vz, t);
glPushMatrix();
glMultMatrixf((GLfloat *) proj.m);
// transform the light position and shadow plane into model space
Matrix4x4_Transform(&e->inverse_matrix, s->illumination->light.origin, pos);
pos[3] = 1.0;
const vec_t *n = p->normal;
Matrix4x4_TransformPositivePlane(&e->inverse_matrix, n[0], n[1], n[2], p->dist, normal);
// calculate shearing, accounting for Quake's negative plane equation
normal[3] = -normal[3];
dot = DotProduct(pos, normal) + pos[3] * normal[3];
shear.m[0][0] = dot - pos[0] * normal[0];
shear.m[1][0] = 0.0 - pos[0] * normal[1];
shear.m[2][0] = 0.0 - pos[0] * normal[2];
shear.m[3][0] = 0.0 - pos[0] * normal[3];
shear.m[0][1] = 0.0 - pos[1] * normal[0];
shear.m[1][1] = dot - pos[1] * normal[1];
shear.m[2][1] = 0.0 - pos[1] * normal[2];
shear.m[3][1] = 0.0 - pos[1] * normal[3];
shear.m[0][2] = 0.0 - pos[2] * normal[0];
shear.m[1][2] = 0.0 - pos[2] * normal[1];
shear.m[2][2] = dot - pos[2] * normal[2];
shear.m[3][2] = 0.0 - pos[2] * normal[3];
shear.m[0][3] = 0.0 - pos[3] * normal[0];
shear.m[1][3] = 0.0 - pos[3] * normal[1];
shear.m[2][3] = 0.0 - pos[3] * normal[2];
shear.m[3][3] = dot - pos[3] * normal[3];
glMultMatrixf((GLfloat *) shear.m);
Matrix4x4_Copy(&s->matrix, &proj);
}
这里有完整的实现:
https://github.com/jdolan/quake2world/blob/master/src/client/renderer/r_mesh_shadow.c