float4 CShadowHandler::GetShadowProjectionScales(CCamera* cam, const float3& projDir) { float4 projScales; float2 projRadius; // NOTE: // the xy-scaling factors from CalcMinMaxView do not change linearly // or smoothly with camera movements, creating visible artefacts (eg. // large jumps in shadow resolution) // // therefore, EITHER use "fixed" scaling values such that the entire // map barely fits into the sun's frustum (by pretending it is embedded // in a sphere and taking its diameter), OR variable scaling such that // everything that can be seen by the camera maximally fills the sun's // frustum (choice of projection-style is left to the user and can be // changed at run-time) // // the first option means larger maps will have more blurred/aliased // shadows if the depth buffer is kept at the same size, but no (map) // geometry is ever omitted // // the second option means shadows have higher average resolution, but // become less sharp as the viewing volume increases (through eg.camera // rotations) and geometry can be omitted in some cases // // NOTE: // when DynamicSun is enabled, the orbit is always circular in the xz // plane, instead of elliptical when the map has an aspect-ratio != 1 // switch (shadowProMode) { case SHADOWPROMODE_CAM_CENTER: { projScales.x = GetOrthoProjectedFrustumRadius(cam, projMidPos[2]); } break; case SHADOWPROMODE_MAP_CENTER: { projScales.x = GetOrthoProjectedMapRadius(projDir, projMidPos[2]); } break; case SHADOWPROMODE_MIX_CAMMAP: { projRadius.x = GetOrthoProjectedFrustumRadius(cam, projMidPos[0]); projRadius.y = GetOrthoProjectedMapRadius(projDir, projMidPos[1]); projScales.x = std::min(projRadius.x, projRadius.y); // pick the center position (0 or 1) for which radius is smallest projMidPos[2] = projMidPos[projRadius.x >= projRadius.y]; } break; } projScales.y = projScales.x; projScales.z = globalRendering->zNear; projScales.w = globalRendering->viewRange; return (shadowProjScales = projScales); }
float CShadowHandler::GetShadowProjectionRadius(CCamera* cam, float3& proPos, const float3& proDir) const { float radius = 1.0f; switch (shadowProMode) { case SHADOWPROMODE_CAM_CENTER: { radius = GetOrthoProjectedFrustumRadius(cam, proPos); } break; case SHADOWPROMODE_MAP_CENTER: { radius = GetOrthoProjectedMapRadius(proDir, proPos); } break; case SHADOWPROMODE_MIX_CAMMAP: { static float3 opfPos; static float3 opmPos; const float opfRad = GetOrthoProjectedFrustumRadius(cam, opfPos); const float opmRad = GetOrthoProjectedMapRadius(proDir, opmPos); if (opfRad <= opmRad) { radius = opfRad; proPos = opfPos; } if (opmRad <= opfRad) { radius = opmRad; proPos = opmPos; } } break; } return radius; }
void CShadowHandler::CreateShadows() { fb.Bind(); glDisable(GL_BLEND); glDisable(GL_LIGHTING); glDisable(GL_ALPHA_TEST); glDisable(GL_TEXTURE_2D); glShadeModel(GL_FLAT); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_TRUE); glEnable(GL_DEPTH_TEST); glViewport(0, 0, shadowMapSize, shadowMapSize); // glClearColor(0, 0, 0, 0); // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 1, 0, 1, 0, -1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); const ISkyLight* L = sky->GetLight(); // sun direction is in world-space, invert it sunDirZ = -L->GetLightDir(); sunDirX = (sunDirZ.cross(UpVector)).ANormalize(); sunDirY = (sunDirX.cross(sunDirZ)).ANormalize(); SetShadowMapSizeFactors(); // NOTE: // the xy-scaling factors from CalcMinMaxView do not change linearly // or smoothly with camera movements, creating visible artefacts (eg. // large jumps in shadow resolution) // // therefore, EITHER use "fixed" scaling values such that the entire // map barely fits into the sun's frustum (by pretending it is embedded // in a sphere and taking its diameter), OR variable scaling such that // everything that can be seen by the camera maximally fills the sun's // frustum (choice of projection-style is left to the user and can be // changed at run-time) // // the first option means larger maps will have more blurred/aliased // shadows if the depth buffer is kept at the same size, but no (map) // geometry is ever omitted // // the second option means shadows have higher average resolution, but // become less sharp as the viewing volume increases (through eg.camera // rotations) and geometry can be omitted in some cases // // NOTE: // when DynamicSun is enabled, the orbit is always circular in the xz // plane, instead of elliptical when the map has an aspect-ratio != 1 // const float xyScale = (shadowProMode == SHADOWPROMODE_CAM_CENTER)? GetOrthoProjectedFrustumRadius(camera, centerPos): (shadowProMode == SHADOWPROMODE_MAP_CENTER)? GetOrthoProjectedMapRadius(-sunDirZ, centerPos): 1.0f; const float xScale = xyScale; const float yScale = xyScale; const float zScale = globalRendering->viewRange; shadowMatrix[ 0] = sunDirX.x / xScale; shadowMatrix[ 1] = sunDirY.x / yScale; shadowMatrix[ 2] = sunDirZ.x / zScale; shadowMatrix[ 4] = sunDirX.y / xScale; shadowMatrix[ 5] = sunDirY.y / yScale; shadowMatrix[ 6] = sunDirZ.y / zScale; shadowMatrix[ 8] = sunDirX.z / xScale; shadowMatrix[ 9] = sunDirY.z / yScale; shadowMatrix[10] = sunDirZ.z / zScale; // rotate the target position into sun-space for the translation shadowMatrix[12] = (-sunDirX.dot(centerPos) / xScale); shadowMatrix[13] = (-sunDirY.dot(centerPos) / yScale); shadowMatrix[14] = (-sunDirZ.dot(centerPos) / zScale) + 0.5f; glLoadMatrixf(shadowMatrix.m); // set the shadow-parameter registers // NOTE: so long as any part of Spring rendering still uses // ARB programs at run-time, these lines can not be removed // (all ARB programs share the same environment) glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, 16, shadowTexProjCenter.x, shadowTexProjCenter.y, 0.0f, 0.0f); glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, 17, shadowTexProjCenter.z, shadowTexProjCenter.z, 0.0f, 0.0f); glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, 18, shadowTexProjCenter.w, shadowTexProjCenter.w, 0.0f, 0.0f); if (globalRendering->haveGLSL) { for (int i = 0; i < SHADOWGEN_PROGRAM_LAST; i++) { shadowGenProgs[i]->Enable(); shadowGenProgs[i]->SetUniform4fv(0, &shadowTexProjCenter.x); shadowGenProgs[i]->Disable(); } } if (L->GetLightIntensity() > 0.0f) { // move view into sun-space const float3 oldup = camera->up; camera->right = sunDirX; camera->up = sunDirY; DrawShadowPasses(); camera->up = oldup; } glShadeModel(GL_SMOOTH); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // we do this later to save render context switches (this is one of the slowest opengl operations!) // fb.Unbind(); // glViewport(globalRendering->viewPosX,0,globalRendering->viewSizeX,globalRendering->viewSizeY); }