/* * Matrix4_ObliqueNearClipping * * Lengyel, Eric. "Oblique View Frustum Depth Projection and Clipping" * Journal of Game Development, Vol. 1, No. 2 (2005), Charles River Media, pp. 5–16. * * Version that works both for perspective and orthographic projections. */ void Matrix4_ObliqueNearClipping( const vec3_t normal, const vec_t dist, const mat4_t cm, mat4_t pm ) { mat4_t tm1, tm2; vec4_t c, q, p; vec_t d; // The clipping plane in object coordinates q[0] = normal[0]; q[1] = normal[1]; q[2] = normal[2]; q[3] = dist; // The plane is transformed by the transpose of the inverse of the // camera matrix and stored in the resulting eye coordinates Matrix4_Invert( cm, tm1 ); Matrix4_Transpose( tm1, tm2 ); Matrix4_Multiply_Vector( tm2, q, c ); if( c[3] >= 0.0f ) { // the techinique only works when the w-coordinate is negative // (corresponding to the camera being on the negative side of the plane) return; } // Calculate the clip-space corner point opposite the clipping plane // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and // transform it into camera space by multiplying it // by the inverse of the projection matrix p[0] = Q_sign(c[0]); p[1] = Q_sign(c[1]); p[2] = 1.0f; p[3] = 1.0f; // Calculate the scaled plane vector Matrix4_Invert( pm, tm1 ); Matrix4_Multiply_Vector( tm1, p, q ); d = 2.0f / (c[0] * q[0] + c[1] * q[1] + c[2] * q[2] + c[3] * q[3]); // Replace the third row of the projection matrix pm[ 2] = c[0] * d - pm[ 3]; pm[ 6] = c[1] * d - pm[ 7]; pm[10] = c[2] * d - pm[11]; pm[14] = c[3] * d - pm[15]; }
/* * RB_ScissorForBounds */ bool RB_ScissorForBounds( vec3_t bbox[8], int *x, int *y, int *w, int *h ) { int i; int ix1, iy1, ix2, iy2; float x1, y1, x2, y2; vec4_t corner = { 0, 0, 0, 1 }, proj = { 0, 0, 0, 1 }, v = { 0, 0, 0, 1 }; mat4_t cameraProjectionMatrix; Matrix4_Multiply( rb.projectionMatrix, rb.cameraMatrix, cameraProjectionMatrix ); x1 = y1 = 999999; x2 = y2 = -999999; for( i = 0; i < 8; i++ ) { // compute and rotate the full bounding box VectorCopy( bbox[i], corner ); Matrix4_Multiply_Vector( cameraProjectionMatrix, corner, proj ); if( proj[3] ) { v[0] = ( proj[0] / proj[3] + 1.0f ) * 0.5f * rb.gl.viewport[2]; v[1] = ( proj[1] / proj[3] + 1.0f ) * 0.5f * rb.gl.viewport[3]; v[2] = ( proj[2] / proj[3] + 1.0f ) * 0.5f; // [-1..1] -> [0..1] } else { v[0] = 999999.0f; v[1] = 999999.0f; v[2] = 999999.0f; } x1 = min( x1, v[0] ); y1 = min( y1, v[1] ); x2 = max( x2, v[0] ); y2 = max( y2, v[1] ); } ix1 = max( x1 - 1.0f, 0 ); ix2 = min( x2 + 1.0f, rb.gl.viewport[2] ); if( ix1 >= ix2 ) return false; // FIXME iy1 = max( y1 - 1.0f, 0 ); iy2 = min( y2 + 1.0f, rb.gl.viewport[3] ); if( iy1 >= iy2 ) return false; // FIXME *x = ix1; *y = rb.gl.viewport[3] - iy2; *w = ix2 - ix1; *h = iy2 - iy1; return true; }
void RF_TransformVectorToScreen( const refdef_t *rd, const vec3_t in, vec2_t out ) { mat4_t p, m; vec4_t temp, temp2; if( !rd || !in || !out ) return; temp[0] = in[0]; temp[1] = in[1]; temp[2] = in[2]; temp[3] = 1.0f; if( rd->rdflags & RDF_USEORTHO ) { Matrix4_OrthogonalProjection( rd->ortho_x, rd->ortho_x, rd->ortho_y, rd->ortho_y, -4096.0f, 4096.0f, p ); } else { Matrix4_InfinitePerspectiveProjection( rd->fov_x, rd->fov_y, Z_NEAR, rrf.cameraSeparation, p, glConfig.depthEpsilon ); } if( rd->rdflags & RDF_FLIPPED ) { p[0] = -p[0]; } Matrix4_Modelview( rd->vieworg, rd->viewaxis, m ); Matrix4_Multiply_Vector( m, temp, temp2 ); Matrix4_Multiply_Vector( p, temp2, temp ); if( !temp[3] ) return; out[0] = rd->x + ( temp[0] / temp[3] + 1.0f ) * rd->width * 0.5f; out[1] = glConfig.height - (rd->y + ( temp[1] / temp[3] + 1.0f ) * rd->height * 0.5f); }
/* * R_FitOccluder * * returns farclip value */ static float R_FitOccluder( const shadowGroup_t *group, refdef_t *refdef ) { int i; float x1, x2, y1, y2, z1, z2; int ix1, ix2, iy1, iy2, iz1, iz2; int sizex = refdef->width, sizey = refdef->height; int diffx, diffy; mat4_t cameraMatrix, projectionMatrix, cameraProjectionMatrix; bool useOrtho = refdef->rdflags & RDF_USEORTHO ? true : false; Matrix4_Modelview( refdef->vieworg, refdef->viewaxis, cameraMatrix ); // use current view settings for first approximation if( useOrtho ) { Matrix4_OrthogonalProjection( -refdef->ortho_x, refdef->ortho_x, -refdef->ortho_y, refdef->ortho_y, -group->projDist, group->projDist, projectionMatrix ); } else { Matrix4_PerspectiveProjection( refdef->fov_x, refdef->fov_y, Z_NEAR, group->projDist, rf.cameraSeparation, projectionMatrix ); } Matrix4_Multiply( projectionMatrix, cameraMatrix, cameraProjectionMatrix ); // compute optimal fov to increase depth precision (so that shadow group objects are // as close to the nearplane as possible) // note that it's suboptimal to use bbox calculated in worldspace (FIXME) x1 = y1 = z1 = 999999; x2 = y2 = z2 = -999999; for( i = 0; i < 8; i++ ) { // compute and rotate a full bounding box vec3_t v; vec4_t temp, temp2; temp[0] = ( ( i & 1 ) ? group->mins[0] : group->maxs[0] ); temp[1] = ( ( i & 2 ) ? group->mins[1] : group->maxs[1] ); temp[2] = ( ( i & 4 ) ? group->mins[2] : group->maxs[2] ); temp[3] = 1.0f; // transform to screen space Matrix4_Multiply_Vector( cameraProjectionMatrix, temp, temp2 ); if( temp2[3] ) { v[0] = ( temp2[0] / temp2[3] + 1.0f ) * 0.5f * refdef->width; v[1] = ( temp2[1] / temp2[3] + 1.0f ) * 0.5f * refdef->height; v[2] = ( temp2[2] / temp2[3] + 1.0f ) * 0.5f * group->projDist; } else { v[0] = 999999; v[1] = 999999; v[2] = 999999; } x1 = min( x1, v[0] ); y1 = min( y1, v[1] ); z1 = min( z1, v[2] ); x2 = max( x2, v[0] ); y2 = max( y2, v[1] ); z2 = max( z2, v[2] ); } // give it 1 pixel gap on both sides ix1 = x1 - 1.0f; ix2 = x2 + 1.0f; iy1 = y1 - 1.0f; iy2 = y2 + 1.0f; iz1 = z1 - 1.0f; iz2 = z2 + 1.0f; diffx = sizex - min( ix1, sizex - ix2 ) * 2; diffy = sizey - min( iy1, sizey - iy2 ) * 2; // adjust fov (for perspective projection) refdef->fov_x = 2 * RAD2DEG( atan( (float)diffx / (float)sizex ) ); refdef->fov_y = 2 * RAD2DEG( atan( (float)diffy / (float)sizey ) ); // adjust ortho clipping settings refdef->ortho_x = ix2 - ix1 + SHADOWMAP_ORTHO_NUDGE; refdef->ortho_y = iy2 - iy1 + SHADOWMAP_ORTHO_NUDGE; return useOrtho ? max( iz1, iz2 ) : group->projDist; }