#ifndef passPSSM_h__ #define passPSSM_h__ #include "Globals.h" #include "math/LifeMath.h" #include "camera/Camera.h" #include namespace LifeGraphics { struct PSSM_CONST { LifeMath::float4x4 pssmMat[4]; LifeMath::float4 splitRegions[4]; float pssmSize; LifeMath::float3 dummy_pssm; }; class passPSSM { public: IConstantBuffer* cbPssm; IBlendState* shadowBlendState; IBlendState* restoreBlendState; LifeMath::float3 points[8]; int splitCount; float fSplitDistances[4]; float fSplitSchemeLambda; int depthSize; LifeMath::float4x4 mLightView; LifeMath::float4x4 mLightProj; LifeMath::float3 vLightSource; LifeMath::float3 vLightTarget; float fLightNear; float fLightFar; // dynamically adjusted float fLightFarMax; float fLightFOV; LifeMath::float4x4 lViews[4]; LifeMath::float4x4 lProjs[4]; ITexture2D* depth; IShader* rtdShader; Camera* sceneCam; Frustum f; Light* light; LifeMath::Rect2i viewRegions[4]; float fNear; float fFar; std::vector splitRegions; float clampf(float val, float min, float max) { if(val < min) return min; if(val > max) return max; return val; } ~passPSSM() { if(cbPssm) delete cbPssm; if(depth) delete depth; if(rtdShader) delete rtdShader; } passPSSM() { cbPssm = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PSSM_CONST)); fLightNear = 0.1f; fLightFar = 350.0f; // dynamically adjusted fLightFarMax = 350.0f; fLightFOV = 3.14159265358979f / 2.0f; splitCount = 3; fSplitSchemeLambda = 0.92f; depthSize = 2048; ComputeDepthRegionDistribution(); } void ComputeDepthRegionDistribution() { splitRegions.clear(); LifeMath::float4 region0; LifeMath::float4 region1; LifeMath::float4 region2; LifeMath::float4 region3; printf("FIX RECTANGLE!!!!\n"); switch(splitCount) { case 1: //----------| //| | //| 1 | //| | //| | //----------| region0 = LifeMath::float4(0.0f, 0.0f, 1.0f, 1.0f); viewRegions[0] = LifeMath::Rect2i(0, 0, depthSize, depthSize); splitRegions.push_back(region0); break; case 2: //----------| //| 1 | //| | //----------- //| | //| 2 | //----------| region0 = LifeMath::float4(0.0f, 0.0f, 1.0f, 0.5f); viewRegions[0] = LifeMath::Rect2i(0, 0, depthSize, depthSize / 2); region1 = LifeMath::float4(0.0f, 0.5f, 1.0f, 0.5f); viewRegions[1] = LifeMath::Rect2i(0, depthSize / 2, depthSize, depthSize / 2); splitRegions.push_back(region0); splitRegions.push_back(region1); break; case 3: region0 = LifeMath::float4(0.0f, 0.0f, 1.0f, 0.5f); viewRegions[0] = LifeMath::Rect2i(0, 0, depthSize, depthSize / 2); region1 = LifeMath::float4(0.0f, 0.5f, 0.5f, 0.5f); viewRegions[1] = LifeMath::Rect2i(0, depthSize / 2, depthSize / 2, depthSize / 2); region2 = LifeMath::float4(0.5f, 0.5f, 0.5f, 0.5f); viewRegions[2] = LifeMath::Rect2i(depthSize / 2, depthSize / 2, depthSize / 2, depthSize / 2); splitRegions.push_back(region0); splitRegions.push_back(region1); splitRegions.push_back(region2); break; case 4: region0 = LifeMath::float4(0.0f, 0.0f, 0.5f, 0.5f); viewRegions[0] = LifeMath::Rect2i(0, 0, depthSize / 2, depthSize / 2); region1 = LifeMath::float4(0.5f, 0.0f, 0.5f, 0.5f); viewRegions[1] = LifeMath::Rect2i(depthSize / 2, 0, depthSize / 2, depthSize / 2); region2 = LifeMath::float4(0.0f, 0.5f, 0.5f, 0.5f); viewRegions[2] = LifeMath::Rect2i(0, depthSize / 2, depthSize / 2, depthSize / 2); region3 = LifeMath::float4(0.5f, 0.5f, 0.5f, 0.5f); viewRegions[3] = LifeMath::Rect2i(depthSize / 2, depthSize / 2, depthSize / 2, depthSize / 2); splitRegions.push_back(region0); splitRegions.push_back(region1); splitRegions.push_back(region2); splitRegions.push_back(region3); break; } } void Init() { //cam1 = new Camera(1, 1, ""); depth = CG::me()->GetRenderer()->CreateDepthTarget(depthSize, depthSize, FORMAT_R32_TYPELESS); rtdShader = ResourceManager::me()->CompileUserShaderPS(AS_STRING("RTD_PCF.psh")); shadowBlendState = CG::me()->GetRenderer()->CreateBlendState(false, false, BLEND_OP_ADD, BLEND_ZERO, BLEND_ONE, 0); restoreBlendState = CG::me()->GetRenderer()->CreateBlendState(true, false, BLEND_OP_ADD, BLEND_ONE, BLEND_ZERO, 1 | 2 | 4 | 8); } void Begin(Camera* _sceneCam, Light* _light) { light = _light; sceneCam = _sceneCam; IRenderer* renderer = CG::me()->GetRenderer(); CalculateSplitPlanes(sceneCam); renderer->SetPixelShader(rtdShader); //renderer.SetRenderState(IRenderState.AlphaBlendEnable, false); //renderer.SetRenderState(IRenderState.AlphaTestEnable, false); renderer->SetTargets(NULL, depth); LifeMath::float4 color = LifeMath::float4(1.0f, 1.0f, 1.0f, 1.0f); renderer->ClearDepthStencil(depth,1.0f, 0); renderer->SetBlendState(shadowBlendState); } void UpdateBuffer() { PSSM_CONST c; /*float pssmSize; float pssmNumSplits; LifeMath::float3pssmCamPos; LifeMath::float4x4 lmat[4]; LifeMath::float4x4 pssmMat[4]; LifeMath::float4 splitRegions[4]; LifeMath::float4 splitRegionsSm[4];*/ c.pssmSize = static_cast(depthSize); c.pssmMat[0] = lViews[0] * lProjs[0]; c.pssmMat[1] = lViews[1] * lProjs[1]; c.pssmMat[2] = lViews[2] * lProjs[2]; c.splitRegions[0] = splitRegions[0]; c.splitRegions[1] = splitRegions[1]; c.splitRegions[2] = splitRegions[2]; //printf("ololo: %i\n", sizeof(PSSM_CONST)); cbPssm->Update(&c, sizeof(PSSM_CONST)); } void BeginSplit(int iSplit) { IRenderer* renderer = CG::me()->GetRenderer(); LifeMath::Rect2i scissor = LifeMath::Rect2i(0, 0, depthSize, depthSize); renderer->SetScissorRectangle(scissor); fNear = fSplitDistances[iSplit]; fFar = fSplitDistances[iSplit + 1]; float fScale = 1.1f; CalculateFrustumCorners(sceneCam->getPosition(), sceneCam->getDirection(), sceneCam->getUp(), fNear, fFar, sceneCam->getFov(), sceneCam->getAspect(), fScale, vLightTarget); vLightSource = vLightTarget + light->getDirection() * 200.0f; vLightTarget = vLightTarget + light->getDirection() * -50.0f; LifeMath::float3 diff = points[6] - points[0]; //CalculateLightForFrustum(vLightTarget); float projSize = Vec3Length(&diff); Mat4x4LookAtLH(vLightSource, vLightTarget, LifeMath::float3(0.0f, 1.0f, 0.0f), mLightView); Mat4x4Ortho(projSize, projSize, 0.1f, 350.0, mLightProj); float minDist = 100000.0f; float maxDist = 0.0f; LifeMath::float4x4 mLightViewProj = mLightView * mLightProj; for(int i = 0; i< 8; i++) { LifeMath::float4 vTransformed; Vec3Transform(&points[i], &mLightViewProj, &vTransformed); float dist = vTransformed.Z; if(minDist > dist) minDist = dist; if(maxDist < dist) maxDist = dist; } float vol = 1000.0f - 0.1f; Mat4x4Ortho(projSize, projSize, 1.0f, maxDist * vol, mLightProj); f.UpdateFrustum(mLightView * mLightProj); //cam1.ViewMatrix = g_mLightView; //cam1.ProjectionMatrix = g_mLightProj; //cam1.Frustum = f; //RenderQueue q = scene.CullScene(cam1, true, EffectType.SHADOW_MAPPING ); lViews[iSplit] = mLightView; lProjs[iSplit] = mLightProj; LifeMath::float4 region = splitRegions[iSplit]; printf("CHECK ME TOO!!!\n"); LifeMath::Rect2i tr =viewRegions[iSplit]; renderer->SetViewport(tr); // renderer->SetViewport(splitRegions[iSplit].Region); } void EndSplit() { } void End() { CG::me()->GetRenderer()->SetBlendState(restoreBlendState); } void CalculateSplitPlanes(const Camera* sceneCam) { fSplitSchemeLambda = clampf(fSplitSchemeLambda, 0.0f, 1.0f); for (int i = 0; i < splitCount; i++) { float fIDM = (float)i / (float)splitCount; float fLog = sceneCam->getNear() * pow((sceneCam->getFar() / sceneCam->getNear()), fIDM); float fUniform = sceneCam->getNear() + (sceneCam->getFar() - sceneCam->getNear()) * fIDM; fSplitDistances[i] = fLog * fSplitSchemeLambda + fUniform * (1.0f - fSplitSchemeLambda); /* float fIDM = (float)i / (float)splitCount; float fLog = sceneCam->getNear() * pow((fLightFarMax / sceneCam->getNear()), fIDM); float fUniform = sceneCam->getNear() + (fLightFarMax - sceneCam->getNear()) * fIDM; fSplitDistances[i] = fLog * fSplitSchemeLambda + fUniform * (1.0f - fSplitSchemeLambda);*/ } fSplitDistances[0] = sceneCam->getNear(); fSplitDistances[splitCount] = sceneCam->getFar(); } void CalculateLightForFrustum(LifeMath::float3 frustumCenter) { // calculate standard view and projection matrices for light CalculateViewProj(vLightSource, vLightTarget, LifeMath::float3(0.0f, 1.0f, 0.0f), fLightFOV, fLightNear, fLightFarMax, 1.0f); // Next we will find the min and max values of the current // frustum split in lights post-projection space // (where coordinate range is from -1.0 to 1.0) // float fMaxX = -10000000.0f; float fMaxY = -10000000.0f; float fMinX = 10000000.0f; float fMinY = 10000000.0f; float fMaxZ = 0.0f; LifeMath::float4x4 mLightViewProj = mLightView * mLightProj; // for each corner point for (int i = 0; i < 8; i++) { // transform point LifeMath::float4 vTransformed; Vec3Transform(&points[i], &mLightViewProj, &vTransformed); // project x and y vTransformed.X /= vTransformed.W; vTransformed.Y /= vTransformed.W; // find min and max values if (vTransformed.X > fMaxX) fMaxX = vTransformed.X; if (vTransformed.Y > fMaxY) fMaxY = vTransformed.Y; if (vTransformed.Y < fMinY) fMinY = vTransformed.Y; if (vTransformed.X < fMinX) fMinX = vTransformed.X; // find largest z distance if (vTransformed.Z > fMaxZ) fMaxZ = vTransformed.Z; } // set values to valid range (post-projection) fMaxX = clampf(fMaxX, -1.0f, 1.0f); fMaxY = clampf(fMaxY, -1.0f, 1.0f); fMinX = clampf(fMinX, -1.0f, 1.0f); fMinY = clampf(fMinY, -1.0f, 1.0f); // Adjust the far plane of the light to be at the farthest // point of the frustum split. Some bias may be necessary. // fLightFar = fMaxZ + fLightNear + 1.5f; //_fLightFar = fMaxZ + _fLightNear; // re-calculate lights matrices with the new far plane CalculateViewProj(vLightSource, vLightTarget, LifeMath::float3(0.0f, 1.0f, 0.0f), fLightFOV, fLightNear, fLightFar, 1.0f); // Next we build a special matrix for cropping the lights view // to only contain points of the current frustum split // LifeMath::float4 vTransCenter; Vec3Transform(&frustumCenter, &mLightViewProj, &vTransCenter); float fScaleX = 2.0f / (fMaxX - fMinX); float fScaleY = 2.0f / (fMaxY - fMinY); float fOffsetX = -0.5f * (fMaxX + fMinX) * fScaleX + vTransCenter.X; float fOffsetY = -0.5f * (fMaxY + fMinY) * fScaleY + vTransCenter.Y; LifeMath::float4x4 mCropView; mCropView.M11 = fScaleX; mCropView.M12 = 0.0f; mCropView.M13 = 0.0f; mCropView.M14 = 0.0f; mCropView.M21 = 0.0f; mCropView.M22 = fScaleY; mCropView.M23 = 0.0f; mCropView.M24 = 0.0f; mCropView.M31 = 0.0f; mCropView.M32 = 0.0f; mCropView.M33 = 1.0f; mCropView.M34 = 0.0f; mCropView.M41 = fOffsetX; mCropView.M42 = fOffsetY; mCropView.M43 = 0.0f; mCropView.M44 = 1.0f; // multiply the projection matrix with it mLightProj = mLightProj * mCropView; // finally modify projection matrix for linearized depth mLightProj.M33 /= fLightFar; mLightProj.M43 /= fLightFar; } void CalculateViewProj(LifeMath::float3 vSource, LifeMath::float3 vTarget, LifeMath::float3 vUpVector, float fFOV, float fNear, float fFar, float fAspect) { Mat4x4LookAtLH(vSource, vTarget, vUpVector, mLightView); Mat4x4PerspectiveFOV(fFOV, fAspect, fNear, fFar, mLightProj); } void CalculateFrustumCorners(LifeMath::float3 vSource, LifeMath::float3 vTarget,LifeMath::float3 vUp,float fNear, float fFar, float fFOV, float fAspect, float fScale, LifeMath::float3& center) { vTarget = vTarget + vSource; LifeMath::float3 vZ = vTarget - vSource; LifeMath::Vec3Normalize(&vZ); LifeMath::float3 vX = LifeMath::Vec3Cross(vUp, vZ); LifeMath::Vec3Normalize(&vX); LifeMath::float3 vY = LifeMath::Vec3Cross(vZ, vX); float fNearPlaneHeight = tanf(fFOV * 0.5f) * fNear; float fNearPlaneWidth = fNearPlaneHeight * fAspect; float fFarPlaneHeight = tanf(fFOV * 0.5f) * fFar; float fFarPlaneWidth = fFarPlaneHeight * fAspect; LifeMath::float3 vNearPlaneCenter = vSource + vZ * fNear; LifeMath::float3 vFarPlaneCenter = vSource + vZ * fFar; points[0] = vNearPlaneCenter - vX * fNearPlaneWidth - vY * fNearPlaneHeight; points[1] = vNearPlaneCenter - vX * fNearPlaneWidth + vY * fNearPlaneHeight; points[2] = vNearPlaneCenter + vX * fNearPlaneWidth + vY * fNearPlaneHeight; points[3] = vNearPlaneCenter + vX * fNearPlaneWidth - vY * fNearPlaneHeight; points[4] = vFarPlaneCenter - vX * fFarPlaneWidth - vY * fFarPlaneHeight; points[5] = vFarPlaneCenter - vX * fFarPlaneWidth + vY * fFarPlaneHeight; points[6] = vFarPlaneCenter + vX * fFarPlaneWidth + vY * fFarPlaneHeight; points[7] = vFarPlaneCenter + vX * fFarPlaneWidth - vY * fFarPlaneHeight; // calculate center of points LifeMath::float3 vCenter; vCenter = LifeMath::float3(0.0f, 0.0f, 0.0f); for (int i = 0; i < 8; i++) vCenter += points[i]; vCenter /= 8.0f; // for each point for (int i = 0; i < 8; i++) { // scale by adding offset from center points[i] += (points[i] - vCenter) * (fScale - 1.0f); } center = vCenter; } }; } #endif // passPSSM_h__