기록공간

[DirectX 12] 조명 - 1 본문

DirectX/기초

[DirectX 12] 조명 - 1

입코딩 2020. 5. 7. 11:30
반응형

왼쪽은 빛을 비추지 않은 구이고 오른쪽은 빛을 비춘 구이다. 왼쪽 그림은 상당히 평평해 보인다. 사실 구가 아니라 그냥 2차원 원처럼 보인다. 반면 오른쪽의 구는 실제로 3차원의 구처럼 보인다. 이처럼 조명과 음영은 물체의 입체감과 부피감을 묘사하는 데 큰 도움이 된다. 실제로, 인간의 시각적인 인식은 빛과 물체 재질의 상호작용에 의존한다. 따라서 실사적인 묘사 부분의 상당 부분이 물리적으로 정확한 조명 표현이다. 

 

일반적으로 조명 표현이 정확할수록 계산 비용이 높아진다. 따라서 사실감과 처리 속도의 밸런스를 맞추는 것이 중요하다. 예를 들어 영화 같은 경우는 게임에 비해 더 사실적인 조명 표현이 가능하다. 왜냐하면 영화에서의 그래픽은 실시간 처리가 아닌 미리 렌더링을 계산하기 때문이다. 따라서 한 프레임에 몇 시간, 심지어 몇 일이 걸려도 상관 없다. 하지만 게임은 실시간 렌더링이기 때문에 적어도 30 FPS의 속도로 렌더링해야 한다.

 

빛과 재질의 상호작용

이전에는 정점들의 색상을 프로그램이 직접 지정했지만, 조명을 사용할 때에는 정점 색상을 직접 지정하지 않는다. 대신 표면의 재질들과 표면에 비출 빛들을 지정하고 조명 방정식을 이용해 정점 색상이 결정되게 한다. 조명 방정식은 빛과 재질의 상호작용에 기반해서 정점 색상을 산출한다. 결과적으로 물체의 색이 더 사실적으로 나타난다.

 

재질(material)은 빛이 물체의 표면과 상호작용하는 방식을 결정하는 속성들의 집합이라고 할 수 있다. 그러한 속성들의 예로는 표면이 반사, 흡수하는 빛의 색상, 표면 아래 재질의 굴절률, 매끄러운 정도, 투명도 등이 있다. 재질 속성들을 적절히 지정함으로써 나무, 돌, 금속, 물 같은 현실 세계의 다양한 표면을 본뜰 수 있다.

 

광원에서 나온 빛이 물체와 충돌하면, 그 빛의 일부는 흡수되고 일부는 반사된다. 반사된 빛은 새로운 경로를 따라 이동하다가 다른 물체와 부딪힐 수 있으며, 그러면 또다시 반사와 흡수가 일어난다. 그 중 광원의 일부가 관찰자의 눈에 도달해서 물체를 볼 수 있게 된다.

 

법선 벡터

면 법선(face normal)은 다각형의 면의 방향을 나타내는 단위벡터로, 다른 식으로 표현하면 다각형의 모든 점에 수직인 단위벡터이다.

 

표면 법선(surface noraml)은 표면의 항 점의 접평면에 수직인 단위벡터이다. 표면 법선은 표면의 한 점이 향하는 방향을 결정한다는 것을 주목하자.

 

왼) 면 법선   오) 표면 법선

조명 계산을 위해서는 삼각형 메시의 표면의 모든 점에서 표면 법선이 필요하다. 표면 법선이 있어야 광선이 메시 표면의 점으로 입사한 각도를 구할 수 있기 때문이다.

 

조명 계산을 픽셀마다 수행하는 것을 픽셀별 퐁 조명(Phong lighting)이라고 부른다. 조명을 정점마다 계산할 수도 있는데, 픽셀별 조명보다 덜 정확하지만 비용은 더 싸다. 정점별 조명 계산은 정점 쉐이더에서 수행한다. 래스터화 단계는 정점 쉐이더의 계산 결과를 삼각형 표면에 따라 보간해서 픽셀의 조명 값을 결정한다. 조명을 정점 쉐이더에서 계산하는 것은 품질을 희생해서 성능을 높이는 흔한 최적화 기법 중 하나이다. 품질을 희생해도 차이가 별로 없는 경우에는 최적의 방법이다.

법선 벡터의 계산

면 법선을 구할 때에는 우선 삼각형의 두 변에 놓은 두 벡터를 구한다. 예를들어 점 p0p1p2를 가지는 삼각형의 법선을 구하려고 한다면 두 벡터 u, v는 다음과 같이 계산한다.

 u = p1 - p0

 v = p2 - p0

그리고 면 법선 n을 다음과 같이 구한다

n = (u x v) / ||(u x v)||

두 벡터를 외적한 후 그 벡터의 길이를 나누어 정규화 시키면 그것이 삼각형의 법선 벡터가 된다.

 

다음 코드는 삼각형의 세 정점으로부터 삼각형 앞면의 면 법선을 구하는 함수이다.

XMVECTOR ComputeNormal(FXMVECTOR p0,
               FXMVECTOR p1,
               FXMVECTOR p2)
{
    XMVECTOR u = p1 - p0;
    XMVECTOR v = p2 - p0;
    
    return XMVector3Normalize(XMVector3Cross(u, v));
}

 

삼각형 메시의 정점 법선들을 구할 때에는 흔히 정점 법선 평균 기법을 사용한다. 이 기법에서는 메시의 임의의 정점 v의 정점 법선 n을, 그 정점을 공유하는 메시의 모든 다각형의 면 법선의 평균으로 근사한다. 

 

이 기법에서 법선들의 평균은 곧 법선들의 합을 정규화해서 얻은 단위 벡터이다. 다음은 삼각형 메시의 정점 목록과 인덱스 목록이 주어졌을 때 이러한 평균 기법을 이용해 정점 법선들을 구하는 방법을 보여주는 코드이다.

 

법선 벡터의 변환

다음 그림과 같이 정점이 변환되는 경우 법선 벡터도 변환되어야 한다.

 

x축, y축, z축의 크기 변환이 같으면 법선 벡터의 방향은 바뀌지 않지만(대신 단위벡터가 아닐 수 있으므로 정규화가 필요하다), 다르면 변환된 법선 벡터는 법선 벡터가 되지 않는다. 이럴 때는 접선 벡터(Tangent Vector)를 이용하면 된다. 접선 벡터는 법선 벡터와 항상 수직인 벡터이다. 

 

접선 벡터 u가 행렬 A로 변환되면 변환된 접선 벡터는 uA가 된다. 따라서 접선 벡터에 수직인 법선 벡터 n은 n(A^-1)^T (즉 A의 역행렬의 전치행렬, 역전치행렬)로 변환된다.  

 

다음 코드는 역전치행렬을 계산하는 보조 함수이다.

static XMMATRIX InverseTranspose(CXMMATRIX M)
{
    XMMATRIX A = M;
    A.r[3] = XMVectorSet(0.f, 0.f, 0.f, 1.f);
    
    XMVECTOR det = XMMatrixDeterminant(A);
    return XMMatrixTranspose(XMMatrixInverse(&det, A));
}

 

반응형
Comments