반응형
현재 라이팅 문제점 1
- 기존엔 정점 셰이더(VS)에서 Diffuse를 계산하고, 픽셀 셰이더(PS)에서 Specular를 계산하였다.
- 그런데 한 가지 문제점이 존재한다.
- 큐브를 렌더링 한다고 할 때 인덱스 버퍼를 이용한다면, 다음과 같이 윗면에 대한 정점 4개, 밑면에 대한 정점 4개로 모든 면의 구성이 가능하다.
CubeVertex Cube::m_Vertices[] =
{
// Bottom
// {vertex, color, normal}
{ XMFLOAT3(0.0f, 0.0f, 0.0f), XMFLOAT3(0.3f, 0.5f, 0.6f), XMFLOAT3(0.f, -1.f, 0.f) },
{ XMFLOAT3(0.f, 0.f, 20.0f), XMFLOAT3(0.3f, 0.5f, 0.6f), XMFLOAT3(0.f, -1.f, 0.f) },
{ XMFLOAT3(30.f, 0.f, 20.0f), XMFLOAT3(0.3f, 0.5f, 0.6f), XMFLOAT3(0.f, -1.f, 0.f) },
{ XMFLOAT3(30.f, 0.f, 0.0f), XMFLOAT3(0.3f, 0.5f, 0.6f), XMFLOAT3(0.f, -1.f, 0.f) },
// Top
{ XMFLOAT3(0.0f, 30.0f, 0.0f), XMFLOAT3(0.7f, 0.f, 0.f), XMFLOAT3(0.f, 1.f, 0.f) },
{ XMFLOAT3(0.f, 30.f, 20.0f), XMFLOAT3(0.7f, 0.f, 0.f), XMFLOAT3(0.f, 1.f, 0.f) },
{ XMFLOAT3(30.f, 30.f, 20.0f), XMFLOAT3(0.7f, 0.f, 0.f), XMFLOAT3(0.f, 1.f, 0.f) },
{ XMFLOAT3(30.f, 30.f, 0.0f), XMFLOAT3(0.7f, 0.f, 0.f), XMFLOAT3(0.f, 1.f, 0.f) },
};
- 하지만 밑면과 윗면을 제외한 면들의 노멀은 올바른 방향을 가지지 않는다.
- 때문에 VS에서 조명을 계산할 때 올바르지 않은 노멀을 가지고 계산하는 정점들이 생기므로 이상한 결과가 나올 수 있다.
- 이는 각 면마다 정점을 따로 지정하여 총 24개의 정점을 이용하여 해결할 수 있다. 하지만 더 많은 메모리가 사용된다.
- 만약 지오메트리 셰이더(GS)를 이용한다면 8개의 정점만으로도 이 문제를 해결할 수 있다.
- GS는 삼각형 단위로 입력을 받기 때문에 3개의 정점을 받게 된다.
- 3개의 정점으로 시작점이 같은 2개의 벡터를 만들 수 있고, 두 벡터는 하나의 평면을 이룬다.
- 이 두 벡터를 외적하면 해당 평면에 수직인 법선 벡터를 구할 수 있다.
- 그러면 현재 삼각형이 속한 평면에 맞는 노멀을 얻을 수 있게 되고, 이를 조명 계산에 사용하면 된다.
- GS는 하나의 프리미티브당 한 번 수행되므로, VS에서 수행되었던 연산 중 GS에서도 가능한 연산은 최대한 GS에서 하도록 하자. (함수 호출 수를 최소화 하기 위해)
메인 함수만 작성해보면 대략 다음과 같은 로직이 된다.
[maxvertexcount(3)]
void main(triangle GS_INPUT input[3], inout TriangleStream<GS_OUTPUT> triStream)
{
// Make two vectors which have same starting point
float3 v1 = input[1].Pos.xyz - input[0].Pos.xyz; // v1v2
float3 v2 = input[2].Pos.xyz - input[0].Pos.xyz; // v1v3
// Get a normal using cross product
float3 faceNormal = cross(v1, v2);
faceNormal = mul(faceNormal, (float3x3) World);
faceNormal = normalize(faceNormal);
// light position in world space
float3 worldLightPos = mul((float3) LightPosition, (float3x3) World);
// processing for each vertex
for (int i = 0; i < 3; ++i)
{
GS_OUTPUT output = (GS_OUTPUT) 0;
GS_INPUT v = input[i];
// Get a light vector
float3 worldPos = mul(v.Pos.xyz, (float3x3) World);
float3 lightVector = normalize(worldLightPos - worldPos);
// Get Diffuse
output.Diffuse = max(dot(faceNormal, lightVector), 0);
// Get Camera Vector
float3 cameraVector = mul(CameraPosition, (float3x3) World) - worldPos;
cameraVector = normalize(cameraVector);
// Get Reflection Vector
float3 reflectionVector = reflect(-lightVector, faceNormal);
reflectionVector = normalize(reflectionVector);
output.Normal = faceNormal;
output.Pos = mul(v.Pos, WorldViewProjection);
output.TexCoord = v.TexCoord;
output.Color = v.Color;
output.CameraVector = cameraVector;
output.ReflectionVector = reflectionVector;
triStream.Append(output);
}
triStream.RestartStrip();
}
하지만 여전히 조명 효과가 무언가 이상하다..
----------------------------------
관련된 자료를 찾아보다 우연히 보게 된 것인데, 지오메트리 셰이더에서 이러한 연산을 매번 하는 것은 좋지 않다고 한다... CPU를 사용하는 것이랑 다를 게 없다고 한다. 그냥 정점을 24개로 구성해야 하는 것인가..?
관련글
https://www.reddit.com/r/opengl/comments/5azptl/calculate_triangle_normals_using_geometry_shader/
https://gamedev.stackexchange.com/questions/75313/calculating-per-vertex-normal-in-geometry-shader
'게임 공부 > 게임 개발 일지' 카테고리의 다른 글
Constant Buffer 관련 실수한 것 한 가지 | 메모리 정렬(alignment) (0) | 2021.08.07 |
---|---|
세 종류의 라이팅 구현 (1) - 소개 | Directional Light, Point Light, Spot Light (0) | 2021.08.05 |
비동기 소켓과 멀티 스레딩을 활용한 소켓 함수 사용의 차이 (0) | 2021.05.30 |
[DX] 카메라 클래스 설계 및 구현 (0) | 2021.05.04 |
assimp를 이용하여 3D 메쉬 가져와서 렌더링 해보기 (0) | 2021.04.08 |
댓글