Files

455 lines
14 KiB
C++
Raw Permalink Normal View History

2025-05-13 02:46:21 +03:00
#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__