Tangent 공간 <=> Object 공간 변환
노말맵을 사용하기 위해서는 tangent 공간(or texture 공간)과 object 공간 사이의 변환이 필요하다.
노말맵에 저장된 값은 tangent 공간에 있고 Light 벡터등은 object나 world 공간에 있기 때문에 같은 공간상의 값으로 변환해야 한다.
그럼 어떤 공간에 맞출 것인가. 보통은 tangent 공간으로 맞춘다.
Light 벡터나 View 벡터등을 tangent 공간으로 변환하기 위해서는 Vertex Shader에서 한번만 변환시켜주면 되지만 노말맵의 값을 object 공간으로 변환시키기 위해서는 Pixel Shader에서 매픽셀마다 변환 연산이 수행되야 하기 때문이다.
object공간을 tangent 공간으로 변환하는 행렬은 vertex에 저장된 normal, tangent, binormal 값으로 만들 수 있다.
object-to-tangent matrix = (Tx Ty Tz)
(Bx By Bz)
(Nx Ny Nz)
* 쉐이더 코드 예
float3x3 matObjectToTangent;
matObjectToTangent[0] = input.tangent;
matObjectToTangent[1] = input.binormal; //input.tangent 와 input.normal을 외적하여 직접 계산할 수도 있다.
matObjectToTangent[2] = input.normal;
float3 vTangentSpace_Light = mul(matObjectToTangent, vObjectSpace_Light );
행렬과 벡터가 곱해지는 순서가 중요하다. 행렬곱은 교환 법칙이 성립되지 않으니까.
보통은 tangent 공간을 사용하지만 deferred 시스템과 같은 경우는 object 공간을 사용하기도 한다.
obejct 공간을 사용하기 위해서는 노말맵의 값을 tangent 공간에서 object 공간으로 변환시켜야 하고
그러기 위해서는 위의 코드에 있는 행렬의 역행렬이 필요하다.
그럼 위의 행렬의 역행렬은 어떻게 구할 것인가? ...구할 필요 없다.
저 행렬은 쉽게 생각하면 하나의 회전 행렬이다. 회전 행렬의 역행렬은? 그 행렬의 전치 행렬이다.
그럼 저 행렬의 전치 행렬을 구하면 되는 것인가? ...이것도 필요 없다.
행렬을 곱해주는 순서만 바꿔주면 된다.
* 쉐이더 코드 예
float3 vTangentSpace_Normal = normalize( tex2D(NormalMap, input.texcoord).xyz *2.0f -1.0f );
vTangentSpace_Normal .y = -vTangentSpace_Normal .y; //tangent 공간은 y축이 반대이다.
float3x3 matTangentToObject;
matTangentToObject[0] = input.tangent;
matTangentToObject[1] = input.binormal; //input.tangent 와 input.normal을 외적하여 직접 계산할 수도 있다.
matTangentToObject[2] = input.normal;
float3 vObjectSpace_Normal = mul(vTangentSpace_Normal , matTangentToObject);