[Help Wanted] Parallel split shadow map (PSSM) Project

Announce new projects or updates of Irrlicht Engine related tools, games, and applications.
Also check the Wiki
Post Reply
wing64
Competition winner
Posts: 242
Joined: Wed Jul 23, 2008 2:35 am
Location: Thailand
Contact:

[Help Wanted] Parallel split shadow map (PSSM) Project

Post by wing64 »

Hello
I try port Parallel split shadow map (PSSM) to irrLicht engine. This PSSM method have 5 states below.
1.) Calculate camera far plane from PSSM_AdjustCameraPlanes() then return newFar.
2.) Calulate split distance use newFar from 1.) use function PSSM_CalculateSplitDistances() then return array of splits part.
3.) Loop each split area for calculate frustum corners from PSSM_CalculateFrustumCorners() pass new Near and Far value then return 8 points corners.
4.) Use frustum corners from 3.) to calculate a new light frustum use function PSSM_CalculateLightForFrustum() then return new light crop projection matrix.
5.) Render depth map with depth shader and pass new light view and projection as parameter.
I don't know where i miss process. Could u please suggest me about this problem ? I need help to fix it. :'-(
Result is:

Split 3 parts.
Image

Split 1 part.
Image

best regards,

sourcecode&binary:
http://www.easy-share.com/1903741199/PSSM.zip
mirror:
http://www.upload-thai.com/download.php ... bab0c0dc75
http://www.geocities.com/zaros64/PSSM.zip


PSSM_AdjustCameraPlanes()

Code: Select all

float PSSM_AdjustCameraPlanes( const matrix4 &cameraView, float cameraNear, aabbox3df &scenebox )
{
	matrix4 cameraview = cameraView;
	vector3df _vSceneAABB[8];
	_vSceneAABB[0]=vector3df(scenebox.MinEdge.X,scenebox.MinEdge.Y,scenebox.MinEdge.Z);
	_vSceneAABB[1]=vector3df(scenebox.MaxEdge.X,scenebox.MinEdge.Y,scenebox.MinEdge.Z);
	_vSceneAABB[2]=vector3df(scenebox.MinEdge.X,scenebox.MinEdge.Y,scenebox.MaxEdge.Z);
	_vSceneAABB[3]=vector3df(scenebox.MaxEdge.X,scenebox.MinEdge.Y,scenebox.MaxEdge.Z);
	_vSceneAABB[4]=vector3df(scenebox.MinEdge.X,scenebox.MaxEdge.Y,scenebox.MinEdge.Z);
	_vSceneAABB[5]=vector3df(scenebox.MaxEdge.X,scenebox.MaxEdge.Y,scenebox.MinEdge.Z);
	_vSceneAABB[6]=vector3df(scenebox.MinEdge.X,scenebox.MaxEdge.Y,scenebox.MaxEdge.Z);
	_vSceneAABB[7]=vector3df(scenebox.MaxEdge.X,scenebox.MaxEdge.Y,scenebox.MaxEdge.Z);

	// Find the most distant point of AABB
	//
	float fMaxZ=0;
	float fZ = 0;
	for(int i=0;i<8;i++)
	{
		// transform z coordinate with view matrix
		vector3df SceneAABox;
		cameraview.transformVect( SceneAABox, _vSceneAABB[i] );
		//fZ=_vSceneAABB[i].X*cameraview[2] + _vSceneAABB[i].Y*cameraview[6]+_vSceneAABB[i].Z*cameraview[10]+1*cameraview[14];
		fZ = SceneAABox.Z;

		// check if its largest
		if(fZ>fMaxZ) 
			fMaxZ=fZ;
	}

	// use largest Z coordinate as new far plane
	return (fMaxZ+cameraNear);
}
PSSM_CalculateSplitDistances()

Code: Select all

float* PSSM_CalculateSplitDistances( float fNear, float fFar, int iSplit, float fLamda )
{
	// Reallocate array in case the split count has changed
	//
	int _iNumSplits			= iSplit;
	float _fCameraFar		= fFar;
	float _fCameraNear		= fNear;
	float* _pSplitDistances = NULL;
	float _fSplitSchemeLambda=clamp(fLamda,0.0f,1.0f);

	_pSplitDistances=new float[_iNumSplits+1];

	// Practical split scheme:
	//
	// CLi = n*(f/n)^(i/numsplits)
	// CUi = n + (f-n)*(i/numsplits)
	// Ci = CLi*(lambda) + CUi*(1-lambda)
	//
	// lambda scales between logarithmic and uniform
	//
	for(int i=0;i<_iNumSplits;i++)
	{
		float fIDM=i/(float)_iNumSplits;
		float fLog=_fCameraNear*powf((_fCameraFar/_fCameraNear),fIDM);
		float fUniform=_fCameraNear+(_fCameraFar-_fCameraNear)*fIDM;
		_pSplitDistances[i]=fLog*_fSplitSchemeLambda+fUniform*(1-_fSplitSchemeLambda);
	}

	// make sure border values are right
	_pSplitDistances[0]=_fCameraNear;
	_pSplitDistances[_iNumSplits]=_fCameraFar;

	return _pSplitDistances;
}
PSSM_CalculateFrustumCorners()

Code: Select all

void PSSM_CalculateFrustumCorners(vector3df *pPoints, // --> vector3df pPoints[8];
								 const vector3df &cameraSource,
								 const vector3df &cameraTarget,
								 const vector3df &cameraUp,
								 float fNear, float fFar,
								 float cameraFOV, float cameraAspect, float fScale )
{
	vector3df vSource	= cameraSource;
	vector3df vTarget	= cameraTarget;
	vector3df vUp		= cameraUp;
	float fFOV			= cameraFOV;
	float fAspect		= cameraAspect;

	vector3df vZ=vTarget-vSource;
	vZ.normalize( );

	vector3df vX;
	vX = vUp.crossProduct( vZ );
	vX.normalize( );

	vector3df vY;
	vY = vZ.crossProduct( vX );

	float fNearPlaneHeight = tanf((fFOV) * 0.5f) * fNear;
	float fNearPlaneWidth = fNearPlaneHeight * fAspect;

	float fFarPlaneHeight = tanf((fFOV) * 0.5f) * fFar;
	float fFarPlaneWidth = fFarPlaneHeight * fAspect;

	vector3df vNearPlaneCenter = vSource + vZ * fNear;
	vector3df vFarPlaneCenter = vSource + vZ * fFar;

	pPoints[0]=vector3df(vNearPlaneCenter - vX*fNearPlaneWidth - vY*fNearPlaneHeight);
	pPoints[1]=vector3df(vNearPlaneCenter - vX*fNearPlaneWidth + vY*fNearPlaneHeight);
	pPoints[2]=vector3df(vNearPlaneCenter + vX*fNearPlaneWidth + vY*fNearPlaneHeight);
	pPoints[3]=vector3df(vNearPlaneCenter + vX*fNearPlaneWidth - vY*fNearPlaneHeight);

	pPoints[4]=vector3df(vFarPlaneCenter - vX*fFarPlaneWidth - vY*fFarPlaneHeight);
	pPoints[5]=vector3df(vFarPlaneCenter - vX*fFarPlaneWidth + vY*fFarPlaneHeight);
	pPoints[6]=vector3df(vFarPlaneCenter + vX*fFarPlaneWidth + vY*fFarPlaneHeight);
	pPoints[7]=vector3df(vFarPlaneCenter + vX*fFarPlaneWidth - vY*fFarPlaneHeight);

	// calculate center of points
	vector3df vCenter(0,0,0);
	for(int i=0;i<8;i++) 
		vCenter+=pPoints[i];
	vCenter/=8;

	// for each point
	for(int i=0;i<8;i++)
		// scale by adding offset from center
		pPoints[i]+=(pPoints[i]-vCenter)*(fScale-1);
}
PSSM_CalculateLightForFrustum()

Code: Select all

void PSSM_CalculateLightForFrustum( matrix4 *out_lightProj, vector3df *pCorners, SShadowLight& shadow_light )
{
	// 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=-FLT_MAX;
	float fMaxY=-FLT_MAX;
	float fMinX=FLT_MAX;
	float fMinY=FLT_MAX;
	float fMaxZ=0;

	matrix4 _mLightView = shadow_light.getViewMatrix() ;
	matrix4 _mLightProj = shadow_light.getProjectionMatrix();
	matrix4 mLightViewProj=_mLightProj;
	mLightViewProj *=_mLightView;

	// for each corner point	
	for(int i=0;i<8;i++)
	{
		// transform point
		vector3df vTransformed;
		mLightViewProj.transformVect( vTransformed, pCorners[i] );
		float w = pCorners[i].X*mLightViewProj[3] + pCorners[i].Y*mLightViewProj[7] + pCorners[i].Z*mLightViewProj[11] + mLightViewProj[15];

		// project x and y
		vTransformed.X/=w;
		vTransformed.Y/=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=clamp(fMaxX,-1.0f,1.0f);
	fMaxY=clamp(fMaxY,-1.0f,1.0f);
	fMinX=clamp(fMinX,-1.0f,1.0f);
	fMinY=clamp(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.
	//
	float _fLightNear = shadow_light.getNearValue( );
	float _fLightFar=fMaxZ+_fLightNear+1.25f;

	// Next we build a special matrix for cropping the lights view
	// to only contain points of the current frustum split
	//

	float fScaleX=2.0f/(fMaxX-fMinX);
	float fScaleY=2.0f/(fMaxY-fMinY);

	float fOffsetX=-0.5f*(fMaxX+fMinX)*fScaleX;
	float fOffsetY=-0.5f*(fMaxY+fMinY)*fScaleY;

	float mCropView[] = {  fScaleX,     0.0f,  0.0f,   0.0f,
						 	  0.0f,  fScaleY,  0.0f,   0.0f,
							  0.0f,     0.0f,  1.0f,   0.0f,
						  fOffsetX, fOffsetY,  0.0f,   1.0f  };
	matrix4 mCrop;
	mCrop.makeIdentity( );
	mCrop.setM( mCropView );

	// re-calculate lights matrices with the new far plane
	matrix4 newm;
	newm.buildProjectionMatrixPerspectiveFovLH( shadow_light.getFov(), 1, _fLightNear, _fLightFar );

	// multiply the projection matrix with it
	//_mLightProj = mCrop * _mLightProj;
	_mLightProj = mCrop;
	_mLightProj *= newm;

	// finally modify projection matrix for linearized depth
	_mLightProj[10]/=_fLightFar;
	_mLightProj[14]/=_fLightFar;

	// return value
	*out_lightProj = _mLightProj;
}
shadowdepthV.hlsl

Code: Select all

matrix	mWorldViewProj;
struct VSOUTPUT_SHADOW
{
	float4 vPosition	: POSITION;
	float  fDepth		: TEXCOORD0;
};
VSOUTPUT_SHADOW vertexMain( float4 inPosition : POSITION )
{
	// Output struct
	VSOUTPUT_SHADOW OUT = (VSOUTPUT_SHADOW)0;

	// Output the transformed position	
	OUT.vPosition = mul( inPosition, mWorldViewProj );

	// Output the scene depth
	OUT.fDepth = OUT.vPosition.z/OUT.vPosition.w;

	return OUT;
}
shadowdepthP.hlsl

Code: Select all

struct VSOUTPUT_SHADOW
{
	float4 vPosition	: POSITION;
	float  fDepth		: TEXCOORD0;
};

float4  pixelMain( VSOUTPUT_SHADOW IN ) : COLOR0
{
	// Output the scene depth
	return float4( IN.fDepth, IN.fDepth, IN.fDepth, 1.0 );
}
wing64
Competition winner
Posts: 242
Joined: Wed Jul 23, 2008 2:35 am
Location: Thailand
Contact:

Post by wing64 »

Hello again :cry:
Now i try another technique LiPSM (http://www.cg.tuwien.ac.at/research/vr/lispsm/) I download sample (http://www.cg.tuwien.ac.at/research/vr/ ... lispSM.zip) and try port to irrlicht.
This sample use 3 Input from outside 1.) inverseEyeViewProj matrix 2.) eye dir and light dir 3.) Scene Bounding box and then call updateLightMtx()
it will return a new lightView and lightProjection matrix for depth map. 555 it look easy but... when render depthmap and shadowmap If i move camera (use ICameraSceneNodeFPS)
by mouse or keyboard around object then shadow will lost (look below image).
Now question are i wrong at light matrix or irrlicht matrix system not support opengl matrix system (in this download sample) ?
best regards,

ps. I hope someone answer or suggest me. Please.... :-(
Image
Image
Image
Image
Image
Image
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Stick to one thing! Try to do PSSM but with only 1 split.
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
Nadro
Posts: 1648
Joined: Sun Feb 19, 2006 9:08 am
Location: Warsaw, Poland

Post by Nadro »

I ported this version of PSSM to Irrlicht and all works good, but I use n+1 passes method. I will upload it to trunk version of IrrCg tomorrow.
Library helping with network requests, tasks management, logger etc in desktop and mobile apps: https://github.com/GrupaPracuj/hermes
Post Reply