Post Lists

2018년 12월 18일 화요일

Point Light Shadow 구현기 in Deferred Shading

일단 다시 Point Light Shadow, 그러니까 Omnidirectional shadow를 구현하면서 궁금했던 점을 먼저 정리해야 겠다. 그래야, 일단 구현을하고, 최적화를 하고, 내 엔진에 적용을 하니까.

- Questions & Answers
1. 왜  lookAt함수의 Up Vector가 다르게 설정되었을까?
이 질문은 내가 오늘에서야 해결하게 되었다. LearnOpenGL 사이트의 댓글에도 남겼다.

반복하기 귀찮으니, 그 댓글을 복사한다

For those who want to know why the LookAt function has those kind of Up vectors.

https://www.nvidia.com/obje...
Go to the link above, and go down to the section "Mapping Texture Coordinates to Cube Map Faces"

After reading the section slowly, you will know the texture coordinate will be calculated in the table of the section.
you have to look at the column of tc. If so, You will get to know why the LooktAt function has those kinds of Up vectors!

I hope this will help you.

2. Directional Light Shadow와 다르게, 왜 Depth Map Shader에서 FragDepth를 far_plane으로 나누어서 직접 계산을 해주는가?

- Vertex Shader

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

uniform mat4 model;

void main()
{
 gl_Position = model * vec4(aPos, 1.0);
}

- Geometry Shader
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices=18) out;

uniform mat4 shadowMatrices[6];

out vec4 FragPos; // FragPos from GS (output per emitvertex)

void main()
{
 for(int face = 0; face < 6; ++face)
 {
  gl_Layer = face; // built-in variable that specifies to which face we render.
  for(int i = 0; i < 3; ++i)
  {
   FragPos = gl_in[i].gl_Position;
   gl_Position = shadowMatrices[face] * FragPos;
   EmitVertex();
  }
  EndPrimitive();
 }
}

- Fragment Shader
#version 330 core

in vec4 FragPos;

uniform vec3 lightPos;
uniform float far_plane;

void main()
{
 // get distance between fragment and light source
 float lightDistance = length(FragPos.xyz - lightPos);

 // map to [0, 1] range by dividing by far_plane
 lightDistance = lightDistance / far_plane;

 // write this as modified depth
 gl_FragDepth = lightDistance;
}

일단 쉐이더를 봐보자.

Vertex Shader에서 한 오브젝트의 World Space 좌표가 gl_Position에 들어가게 된다.

Geometry Shader에서 Fragment Shader로 갈 FragPos는 vertex shader로 부터 온 그 object world position이 그대로 들어가게 된다. gl_Position에는 light space로 변환되어 된다. 이것은 Geometry Shaer가 끝난 후 Perspective division을 하게 되어, fragment rasterization을 위한 준비를 하게 할 것이다.




Fragment Shader가 중요한데 한 줄마다 보겠다.

           float lightDistance = length(FragPos.xyz - lightPos);

먼저 FragPos는 Geometry Shader로부터 Vertex의 World Space Position이고, lightPos또한 light의 World Position이 된다. 이 때 lightDistance는, World space 상의 길이가 된다.

그리고 나서,

           // map to [0, 1] range by dividing by far_plane
           lightDistance = lightDistance / far_plane;

다음과 같은 것을 해주는데, 우선 lightDistance를 해당 light의 far_plane으로 나누어주면 [0,1]로 매핑되는 이유는, 현재 fragment shader에 도착한 fragPos들은 geometry shader가 끝난 후, vertex post-processing이 되어 light의 frustum 안에있지 않은 것들은 다 clip되어져서 없다. 따라서, 모든 것들은 far_plane 범위 안에 있기 때문에, lightDistance를 far_plane으로 나누는 것은 그 거리를 [0, 1]로 매핑시킨다.

 gl_FragDepth = lightDitance;

그리고 나서 그 FragDepth를 직접 설정해주는데, 이것은 linearized depth를 넣는 것이다.
원래 depth값은 가까이서 depth를 잘 구분하기 위해 가까운값 그러니까 depth value가 작은 곳에서의 비교가 잘 되게 precision을 구성하도록 non-linear depth value를쓴다. 그러나, 그러지 않고 멀리 있는 것에서도 구분을 잘하도록 직접 linearized depth를 써넣어 준다.

https://chanhaeng.blogspot.com/2018/12/the-linearization-and-non-linearization.html

여기에 그 내용을 잘 설명했다.

3. How to render debug map of Omnidirectional light?
omnidirectional light shadow map을 어떻게 디버깅 용도로 보일 수 있을까 고민했는데, cubemap에서 사용했듯이, 그냥 정육면체에 cubemap texture를 붙여서 볼 수 있게 했다.

https://twitter.com/Chan_Lee3211/status/1076124845769773057

내 트위터에 해당 사진을 올렸다.

4. diskRadius 값
튜토리얼에 보면 diskRadius를 viewer의 위치값에 따라 조정하는데

float diskRadius = (1.0 + viewDistance / pointFarPlane[index]) / 25.0;

여기에서 왜 25.0으로 나누는지 모르겠다. 그래서 일단 댓글에 질문을 남겼지만 답변을 해줄지 모르겠다.

댓글 없음:

댓글 쓰기