*
This commit is contained in:
@@ -0,0 +1,455 @@
|
||||
#ifndef passPSSM_h__
|
||||
#define passPSSM_h__
|
||||
|
||||
#include "Globals.h"
|
||||
#include "math/LifeMath.h"
|
||||
#include "camera/Camera.h"
|
||||
#include <vector>
|
||||
|
||||
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<LifeMath::float4> 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<float>(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__
|
||||
Reference in New Issue
Block a user