Jump to content
  • Advertisement
Sign in to follow this  
Merx

Fitting directional light in view frustum

This topic is 998 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Im trying to optimize my shadow maps by making my directional shadow maps fit in the view frustum, however this is causing me some problems. I tried several things, but all didn't work, or didn't work properly.

 

Assuming i only have the following information:

vec3 describing direction of the directional light

perspective camera with values (fov, width, height, near plane, far plane) and transform matrix

 

How can i use this information to create the correct light space matrix to generate and sample my shadow maps?

 

Any help would be greatly appreciated!

Share this post


Link to post
Share on other sites
Advertisement
Hi.
Not sure how to fully do this, but just some thoughts:

- the directional light has no range (basically to infinity)
- I would assume you want to cast shadows for objects that are inside the frustum, or better, within a specific range of the viewer
-- there's a margin for objects which bounding volume is outside the frustum but the shadow might be (partially) inside, for this you have to decide what you want
- a potential performance thing could be processing to many objects, watch out for this

Hope this helps a bit

Share this post


Link to post
Share on other sites

If you want perfect fit for your shadowmap you have to use the depth buffer as information and find the min/max, this method is called SDSM.

Directional light shadow map needs cascade to works on all distance correctly, you can find all informations on internet about it.

If you go for the depth buffer method, don't use a sphere for your shadow map, you will only loose quality.

Edited by Alundra

Share this post


Link to post
Share on other sites

I'm probably asking for too much here, but would anyone be able to write pseudo code for me? I would've liked to post my current progress here, but my project files are on my desktop, which i won't have access to until after the weekend. Doesn't have to be anything fancy (obviously) like "pancaking" or csm, or anything of the sort. I'm fairly sure i can get that to work myself once i properly understand this.

If anyone could do that for me that would be amazing!

Edited by Merx

Share this post


Link to post
Share on other sites

Here the code to compute the 8 corners of the camera frustum in world-space :

void CCameraComponent::ComputeFrustumCornerPoints( CVector3* Points ) const
{
  // Get property values.
  const float NearClipPlane = GetNearClipPlane();
  const float FarClipPlane = GetFarClipPlane();

  // Compute the projection inverse scale factor.
  const float ScaleXInv = 1.0f / m_ProjectionMatrix( 0, 0 );
  const float ScaleYInv = 1.0f / m_ProjectionMatrix( 1, 1 );

  // Compute corners in view space.
  CVector3 CornersVS[ 8 ];
  const float NearX = ScaleXInv * NearClipPlane;
  const float NearY = ScaleYInv * NearClipPlane;
  CornersVS[ 0 ] = CVector3( -NearX, +NearY, NearClipPlane );
  CornersVS[ 1 ] = CVector3( +NearX, +NearY, NearClipPlane );
  CornersVS[ 2 ] = CVector3( +NearX, -NearY, NearClipPlane );
  CornersVS[ 3 ] = CVector3( -NearX, -NearY, NearClipPlane );
  const float FarX = ScaleXInv * FarClipPlane;
  const float FarY = ScaleYInv * FarClipPlane;
  CornersVS[ 4 ] = CVector3( -FarX, +FarY, FarClipPlane );
  CornersVS[ 5 ] = CVector3( +FarX, +FarY, FarClipPlane );
  CornersVS[ 6 ] = CVector3( +FarX, -FarY, FarClipPlane );
  CornersVS[ 7 ] = CVector3( -FarX, -FarY, FarClipPlane );

  // Compute the inverse of the view matrix.
  const CMatrix4 InverseViewMatrix = m_ViewMatrix.Inversed();

  // Compute the world space corners.
  for( UInt32 i = 0; i < 8; ++i )
    Points[ i ] = InverseViewMatrix.TransformPoint( CornersVS[ i ] );
}
Edited by Alundra

Share this post


Link to post
Share on other sites

I almost got it working, the shadows are rendered fine, the only problem  is that under certain angles shadows get cut off or completely disapear. Obviously something is going wrong, but i don't know what. Here is my code:

 

Getting world positions of frustum corners:

void Game::getFrustumWorldSpace(std::vector<glm::vec4>& points)
{
	glm::mat4 inverseProjectionMatrix = glm::inverse(projection * view);

	for (unsigned int x = 0; x < 2; x++)
		for (unsigned int y = 0; y < 2; y++)
			for (unsigned int z = 0; z < 2; z++)
			{
				glm::vec4 projClipSpacePosition(x*2.0f - 1.0f, y*2.0f - 1.0f, z*2.0f - 1.0f, 1.0f);
				glm::vec4 projWorldSpacePosition = inverseProjectionMatrix * projClipSpacePosition;
				points.push_back(projWorldSpacePosition / projWorldSpacePosition.w);
			}
}

Generating the lightVPMatrix:

glm::mat4 Game::getDirLightVPMatrix(std::vector<glm::vec4> points)
{
	glm::mat4 lvMatrix = glm::lookAt(glm::normalize(lightDir), glm::vec3(0.0f), glm::vec3(1.0f));

	glm::vec4 transf = lvMatrix * points[0];
	float minZ = transf.z;
	float maxZ = transf.z;
	float minX = transf.z;
	float maxX = transf.x;
	float minY = transf.y;
	float maxY = transf.y;

	for (unsigned int i = 1; i < 8; i++)
	{
		transf = lvMatrix * points[i];

		if (transf.z > maxZ) maxZ = transf.z;
		if (transf.z < minZ) minZ = transf.z;
		if (transf.x > maxX) maxX = transf.x;
		if (transf.x < minX) minX = transf.x;
		if (transf.y > maxY) maxY = transf.y;
		if (transf.y < minY) minY = transf.y;
	}

	glm::mat4 lpMatrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, minZ, maxZ);

	const float scaleX = 2.0f / (maxX - minX);
	const float scaleY = 2.0f / (maxY - minY);
	const float offsetX = -0.5f * (minX + maxX) * scaleX;
	const float offsetY = -0.5f * (minY + maxY) * scaleY;

	glm::mat4 cropMatrix(1.0f);
	cropMatrix[0][0] = scaleX;
	cropMatrix[1][1] = scaleY;
	cropMatrix[3][0] = offsetX;
	cropMatrix[3][1] = offsetY;

	return cropMatrix * lpMatrix * lvMatrix;
}

If someone could help my find the error i made that would be greatly appreciated!

Share this post


Link to post
Share on other sites

Are the shadows disappearing because they are offscreen? You're fitting the shadow projection's near plane tightly to the frustum, which will cause objects that are offscreen but should still cast shadows into the screen to go missing. Apart from that, the math all looks good :D

Share this post


Link to post
Share on other sites

its not that the shadow casters are offscreen, ill show some screenshots with the problem, as well with the shadow map generated. These seem to me kinda... off.

 

st9G7jX.jpg

Everything going allright...

 

Vp25uSH.jpg

Shadows getting cut off

 

lD00Z11.jpg

Shadows completely gone besides the character

 

The vertex shader i use to generate the shadow (no fragment because shadow maps wink.png )

The lightSpaceMatrix is the matrix generated in the prev shown getDirLightVPMatrix function

#version 330 core
layout (location = 0) in vec3 position;

uniform mat4 lightSpaceMatrix;
uniform mat4 model;

void main()
{
    gl_Position = lightSpaceMatrix * model * vec4(position, 1.0f);
} 

Share this post


Link to post
Share on other sites

I don't see clearly the shadow map but the problem is maybe because your object is not completely rendered in the shadow map which cause to have shadow cutted.

If it's the case, that means you don't have the good size of ortho projection when generating the shadow map.

Here the full code to generate the shadow matrix using cascaded shadow map :

// Set the shadow view matrix.
CMatrix4 ShadowViewMatrix;
ShadowViewMatrix.LookAt( -DirLight, CVector3( 0.0f, 0.0f, 0.0f ) );

// Get the camera projection matrix.
const CMatrix4 ProjectionMatrix = CameraComponent->GetProjectionMatrix();

// Compute the camera view to light view matrix.
const CMatrix4 CameraViewToLight = CameraComponent->GetViewMatrix().Inversed() * ShadowViewMatrix;

// For each cascade find the transformation from shadow to cascade space.
for( UInt32 i = 0; i < 4; ++i )
{
  // Compute the projection inverse scale factor.
  const float ScaleXInv = 1.0f / ProjectionMatrix( 0, 0 );
  const float ScaleYInv = 1.0f / ProjectionMatrix( 1, 1 );

  // Compute corners in view space.
  CVector3 CornersVS[ 8 ];
  const float NearX = ScaleXInv * m_CascadeSplitArray[ i ];
  const float NearY = ScaleYInv * m_CascadeSplitArray[ i ];
  CornersVS[ 0 ] = CVector3( -NearX, +NearY, m_CascadeSplitArray[ i ] );
  CornersVS[ 1 ] = CVector3( +NearX, +NearY, m_CascadeSplitArray[ i ] );
  CornersVS[ 2 ] = CVector3( +NearX, -NearY, m_CascadeSplitArray[ i ] );
  CornersVS[ 3 ] = CVector3( -NearX, -NearY, m_CascadeSplitArray[ i ] );
  const float FarX = ScaleXInv * m_CascadeSplitArray[ i + 1 ];
  const float FarY = ScaleYInv * m_CascadeSplitArray[ i + 1 ];
  CornersVS[ 4 ] = CVector3( -FarX, +FarY, m_CascadeSplitArray[ i + 1 ] );
  CornersVS[ 5 ] = CVector3( +FarX, +FarY, m_CascadeSplitArray[ i + 1 ] );
  CornersVS[ 6 ] = CVector3( +FarX, -FarY, m_CascadeSplitArray[ i + 1 ] );
  CornersVS[ 7 ] = CVector3( -FarX, -FarY, m_CascadeSplitArray[ i + 1 ] );

  // Compute the AABB.
  CAxisAlignedBoundingBox AABB;
  for( UInt32 k = 0; k < 8; ++k )
    AABB.Encapsulate( CameraViewToLight.Transform( CornersVS[ k ] ) );

  // Set the cascade projection matrix.
  CMatrix4 CascadeProjectionMatrix;
  CascadeProjectionMatrix.OrthoOffCenter( AABB.m_Min.x, AABB.m_Max.x, AABB.m_Min.y, AABB.m_Max.y, AABB.m_Min.z, AABB.m_Max.z );

  // Compute the shadow view-proj matrix.
  m_CascadeViewProjArray[ i ] = ShadowViewMatrix * CascadeProjectionMatrix;
}

You can remove the for loop to only have one cascade which is what you actually have.

Edited by Alundra

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!