455 lines
14 KiB
C++
455 lines
14 KiB
C++
#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__
|