2015-11-03 6 views
1

В настоящее время я пытаюсь узнать, как работают каскадные теневые карты, поэтому я пытался получить одну карту теней, чтобы она соответствовала виду усечения без мерцания. Я использую около/далека плоскости от 1 до 10000 для моей проекции камеры, и это, как я вычислить ортогональную матрицу для света:Каскадная карта теней мерцающая

GLfloat far = -INFINITY; 
GLfloat near = INFINITY; 

//Multiply all the world space frustum corners with the view matrix of the light 
Frustum cameraFrustum = CameraMan.getActiveCamera()->mFrustum; 
lightViewMatrix = glm::lookAt((cameraFrustum.frustumCenter - glm::vec3(-0.447213620f, -0.89442790f, 0.0f)), cameraFrustum.frustumCenter, glm::vec3(0.0f, 0.0f, 1.0f)); 

glm::vec3 arr[8]; 
for (unsigned int i = 0; i < 8; ++i) 
    arr[i] = glm::vec3(lightViewMatrix * glm::vec4(cameraFrustum.frustumCorners[i], 1.0f)); 

glm::vec3 minO = glm::vec3(INFINITY, INFINITY, INFINITY); 
glm::vec3 maxO = glm::vec3(-INFINITY, -INFINITY, -INFINITY); 

for (auto& vec : arr) 
{ 
    minO = glm::min(minO, vec); 
    maxO = glm::max(maxO, vec); 
} 

far = maxO.z; 
near = minO.z; 

//Get the longest diagonal of the frustum, this along with texel sized increments is used to keep the shadows from shimmering 
//far top right - near bottom left 
glm::vec3 longestDiagonal = cameraFrustum.frustumCorners[0] - cameraFrustum.frustumCorners[6]; 
GLfloat lengthOfDiagonal = glm::length(longestDiagonal); 
longestDiagonal = glm::vec3(lengthOfDiagonal); 

glm::vec3 borderOffset = (longestDiagonal - (maxO - minO)) * glm::vec3(0.5f, 0.5f, 0.5f); 

borderOffset *= glm::vec3(1.0f, 1.0f, 0.0f); 

maxO += borderOffset; 
minO -= borderOffset; 

GLfloat worldUnitsPerTexel = lengthOfDiagonal/1024.0f; 
glm::vec3 vWorldUnitsPerTexel = glm::vec3(worldUnitsPerTexel, worldUnitsPerTexel, 0.0f); 
minO /= vWorldUnitsPerTexel; 
minO = glm::floor(minO); 
minO *= vWorldUnitsPerTexel; 

maxO /= vWorldUnitsPerTexel; 
maxO = glm::floor(maxO); 
maxO *= vWorldUnitsPerTexel; 

lightOrthoMatrix = glm::ortho(minO.x, maxO.x, minO.y, maxO.y, near, far); 

Использование самой длинной диагонали, чтобы компенсировать усеченные, кажется, работать, поскольку теневая карта, похоже, не уменьшается при масштабировании при просмотре, однако использование приращений размера текста, описанных https://msdn.microsoft.com/en-us/library/windows/desktop/ee416324(v=vs.85).aspx, не имеет никакого эффекта. Я использую довольно большую сцену для тестирования, что приводит к низкому разрешению на моих теневых картах, но я хотел получить стабилизированную тень, которая подходит для усечения, прежде чем я перейду к разделению усечения. Трудно сказать, из образов, но мерцающий эффект не сводится к решению, что Microsoft, представленный: enter image description here

+0

Что вы подразумеваете под «мерцанием»? –

+1

https://www.youtube.com/watch?v=PxbGUOC_UeA это видео объясняет это, моя сцена выглядит как первая часть видео, в то время как решение, предлагаемое моим микрософт, должно стабилизировать его. – Johan

ответ

1

Законченное использовании этого решения:

//Calculate the viewMatrix from the frustum center and light direction 
Frustum cameraFrustum = CameraMan.getActiveCamera()->mFrustum; 
glm::vec3 lightDirection = glm::normalize(glm::vec3(-0.447213620f, -0.89442790f, 0.0f)); 
lightViewMatrix = glm::lookAt((cameraFrustum.frustumCenter - lightDirection), cameraFrustum.frustumCenter, glm::vec3(0.0f, 1.0f, 0.0f)); 

//Get the longest radius in world space 
GLfloat radius = glm::length(cameraFrustum.frustumCenter - cameraFrustum.frustumCorners[6]); 
for (unsigned int i = 0; i < 8; ++i) 
{ 
    GLfloat distance = glm::length(cameraFrustum.frustumCorners[i] - cameraFrustum.frustumCenter); 
    radius = glm::max(radius, distance); 

} 
radius = std::ceil(radius); 

//Create the AABB from the radius 
glm::vec3 maxOrtho = cameraFrustum.frustumCenter + glm::vec3(radius); 
glm::vec3 minOrtho = cameraFrustum.frustumCenter - glm::vec3(radius); 

//Get the AABB in light view space 
maxOrtho = glm::vec3(lightViewMatrix*glm::vec4(maxOrtho, 1.0f)); 
minOrtho = glm::vec3(lightViewMatrix*glm::vec4(minOrtho, 1.0f)); 

//Just checking when debugging to make sure the AABB is the same size 
GLfloat lengthofTemp = glm::length(maxOrtho - minOrtho); 

//Store the far and near planes 
far = maxOrtho.z; 
near = minOrtho.z; 

lightOrthoMatrix = glm::ortho(minOrtho.x, maxOrtho.x, minOrtho.y, maxOrtho.y, near, far); 

//For more accurate near and far planes, clip the scenes AABB with the orthographic frustum 
//calculateNearAndFar(); 

// Create the rounding matrix, by projecting the world-space origin and determining 
// the fractional offset in texel space 
glm::mat4 shadowMatrix = lightOrthoMatrix * lightViewMatrix; 
glm::vec4 shadowOrigin = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); 
shadowOrigin = shadowMatrix * shadowOrigin; 
GLfloat storedW = shadowOrigin.w; 
shadowOrigin = shadowOrigin * 4096.0f/2.0f; 

glm::vec4 roundedOrigin = glm::round(shadowOrigin); 
glm::vec4 roundOffset = roundedOrigin - shadowOrigin; 
roundOffset = roundOffset * 2.0f/4096.0f; 
roundOffset.z = 0.0f; 
roundOffset.w = 0.0f; 

glm::mat4 shadowProj = lightOrthoMatrix; 
shadowProj[3] += roundOffset; 
lightOrthoMatrix = shadowProj; 

который я нашел более в http://www.gamedev.net/topic/650743-improving-cascade-shadow/ я в основном переключается на используя вместо этого ограничительную сферу, а затем построим матрицу округления, как в этом примере. Работает как шарм

+0

Спасибо, что поделились своим решением! Почему вы переключились на ограничительную сферу? Чтобы избежать преобразования 8 каскадных углов? Это верно? – Avithohol