DiVec3 DiFocusedShadowPolicy::getLSProjViewDir(const DiMat4& lightSpace, const DiCamera& cam, const PointListBody& bodyLVS) const { // goal is to construct a view direction // because parallel lines are not parallel any more after perspective projection we have to transform // a ray to point us the viewing direction // fetch a point near the camera const DiVec3 e_world = getNearCameraPoint_ws(cam.GetViewMatrix(), bodyLVS); // plus the direction results in a second point const DiVec3 b_world = e_world + cam.GetDerivedDirection(); // transformation into light space const DiVec3 e_ls = lightSpace * e_world; const DiVec3 b_ls = lightSpace * b_world; // calculate the projection direction, which is the subtraction of // b_ls from e_ls. The y component is set to 0 to project the view // direction into the shadow map plane. DiVec3 projectionDir(b_ls - e_ls); projectionDir.y = 0; // deal with Y-only vectors return DiMath::RealEqual(projectionDir.length(), 0.0f) ? DiVec3::NEGATIVE_UNIT_Z : projectionDir.normalisedCopy(); }
//----------------------------------------------------------------------- Matrix4 LiSPSMShadowCameraSetup::calculateLiSPSM(const Matrix4& lightSpace, const PointListBody& bodyB, const PointListBody& bodyLVS, const SceneManager& sm, const Camera& cam, const Light& light) const { // set up bodyB AAB in light space AxisAlignedBox bodyBAAB_ls; for (size_t i = 0; i < bodyB.getPointCount(); ++i) { bodyBAAB_ls.merge(lightSpace * bodyB.getPoint(i)); } // near camera point in light space const Vector3 e_ls = lightSpace * getNearCameraPoint_ws(cam.getViewMatrix(), bodyLVS); // C_start has x and y of e and z from the bodyABB_ls (we look down the negative z axis, so take the maximum z value) const Vector3 C_start_ls(e_ls.x, e_ls.y, bodyBAAB_ls.getMaximum().z); // calculate the optimal distance between origin and near plane Real n_opt; if (mUseSimpleNOpt) n_opt = calculateNOptSimple(bodyLVS, cam); else n_opt = calculateNOpt(lightSpace, bodyBAAB_ls, bodyLVS, cam); // in case n_opt is null, uniform shadow mapping will be done if (n_opt <= 0.0) { return Matrix4::IDENTITY; } // calculate the projection center C which is n units behind the near plane of P // we look into the negative z direction so add n const Vector3 C(C_start_ls + n_opt * Vector3::UNIT_Z); // set up a transformation matrix to transform the light space to its new origin Matrix4 lightSpaceTranslation(Matrix4::IDENTITY); lightSpaceTranslation.setTrans(-C); // range from bMin to bMax; d = |B_z_far - B_z_near| Real d = Math::Abs(bodyBAAB_ls.getMaximum().z - bodyBAAB_ls.getMinimum().z); // set up the LiSPSM perspective transformation // build up frustum to map P onto the unit cube with (-1/-1/-1) and (+1/+1/+1) Matrix4 P = buildFrustumProjection(-1, 1, -1, 1, n_opt, n_opt + d); return P * lightSpaceTranslation; }
//----------------------------------------------------------------------- Real LiSPSMShadowCameraSetup::calculateNOptSimple(const PointListBody& bodyLVS, const Camera& cam) const { // get view matrix const Matrix4& viewMatrix = cam.getViewMatrix(); // calculate e_es const Vector3 e_ws = getNearCameraPoint_ws(viewMatrix, bodyLVS); const Vector3 e_es = viewMatrix * e_ws; // according to the new formula (mainly for directional lights) // n_opt = zn + sqrt(z0 * z1); // zn is set to Abs(near eye point) // z0 is set to the near camera clip distance // z1 is set to the far camera clip distance return (Math::Abs(e_es.z) + Math::Sqrt(cam.getNearClipDistance() * cam.getFarClipDistance())) * mOptAdjustFactor; }
//----------------------------------------------------------------------- Real LiSPSMShadowCameraSetup::calculateNOpt(const Matrix4& lightSpace, const AxisAlignedBox& bodyBABB_ls, const PointListBody& bodyLVS, const Camera& cam) const { // get inverse light space matrix Matrix4 invLightSpace = lightSpace.inverse(); // get view matrix const Matrix4& viewMatrix = cam.getViewMatrix(); // calculate z0_ls const Vector3 e_ws = getNearCameraPoint_ws(viewMatrix, bodyLVS); const Vector3 z0_ls = calculateZ0_ls(lightSpace, e_ws, bodyBABB_ls.getMaximum().z, cam); // z1_ls has the same x and y values as z0_ls and the minimum z values of bodyABB_ls const Vector3 z1_ls = Vector3(z0_ls.x, z0_ls.y, bodyBABB_ls.getMinimum().z); // world const Vector3 z0_ws = invLightSpace * z0_ls; const Vector3 z1_ws = invLightSpace * z1_ls; // eye const Vector3 z0_es = viewMatrix * z0_ws; const Vector3 z1_es = viewMatrix * z1_ws; const Real z0 = z0_es.z; const Real z1 = z1_es.z; // check if we have to do uniform shadow mapping if ((z0 < 0 && z1 > 0) || (z1 < 0 && z0 > 0)) { // apply uniform shadow mapping return 0.0; } return cam.getNearClipDistance() + Math::Sqrt(z0 * z1) * mOptAdjustFactor; }