
URP에서는 노말맵을 가져올때 타입을 노말으로 바꿔줘야 합니다. PBR에서는 노말맵 말고도 Roughness, Metallic, AO 등의 데이터 텍스쳐가 많은데 왜 노말맵만 특별 취급을 받는 것일까?
항상 궁금 했었기 때문에 이번 포스트에서는 노말맵의 원리에 대해 파헤쳐보겠습니다.

(URP 뿐 아니라 노말맵이 필요한 메테리얼을 쓸 경우 인스펙터 창에서도 바꿔줘야한다)
디폴트에서 노말맵으로 바꾸면 데이터 텍스쳐 중에서 무언가 다른 연산이 적용되기 때문에 이렇게 따로 Texture Type을 나눈다고 추측을 했었습니다.

일단 Type이 바뀌면 노말맵 특유의 푸르딩딩한 색이 파랑 원색으로 바뀌는 것을 눈으로 확인 할 수 있습니다. Vector3 (0.5,0.5,1)이 Vector3 (0,0,1)으로 바뀝니다. 이것은 어떤 차이일까요?

이전 포스트 01 렌더링 파이프라인에서 다룬 내용 대로 폴리곤으로 이루어진 오브젝트에는 여러가지 좌표계가 존재하는데 오브젝트의 중심을 로컬 좌표계라고 부르는 것 처럼 폴리곤의 면 중심 하나하나에도 고유의 좌표계가 있습니다. 이것을 탄젠트(Tangent) 좌표계라고 합니다.

(탄젠트 좌표계)
탄젠트 좌표계는 면의 중심을 원점으로 두는 좌표계로 항상 Z 축이 ‘폴리곤이 바라보는 방향’ 입니다.

탄젠트 좌표계의 벡터와 광원 벡터를 내적 연산(Dot product)한 값을 폴리곤의 명암 값으로 사용합니다.
달리 말하면 탄젠트 좌표계의 벡터를 변경하면 빛 연산을 직접 조작할 수 있다는 뜻입니다.

(파랑색 화살표가 Z축)
즉 위 그림과 같이 노말맵이 탄젠트 좌표계의 XYZ축을 RGB 벡터3로 대응해서 조정하는 것입니다. 픽셀단위로 X,Y,Z 벡터를 수정하여 표면이 다르게 보이도록 해서 마치 폴리곤 표면이 튀어나오거나 들어간 것 처럼 눈속임 하는 것입니다.


노말맵은 벡터의 기울기를 숫자로 표시합니다. 노말맵의 색은 곧 숫자입니다. 벡터의 기울기가 없으면 0, 검은색이며 기울기의 방향은 1에서 -1 사이의 값입니다. R과 G채널의 검은색은 0 미만의 음수도 포함하고 있지만 음수의 색을 모니터로 표현할 수는 없으니 저렇게 0과 같은 색으로 나타내는 것입니다.

하지만 일반적인 텍스처 이미지가 가질 수 있는 정보량은 0부터 1까지 밖에 없기 때문에 -1~1 데이터를 0~1까지 절반으로 압축해야만 합니다. 그렇게 압축된 결과물이 우리가 쉽게 접하던 (0.5,0.5,1)의 연한 파랑색의 노말맵입니다.
그리고 B 채널에도 값이 들어있는 걸 볼 수 있는데 B채널의 값은 탄젠트 좌표계의 Z축 벡터의 크기 값을 1로 맞춰줘서 벡터 연산을 쉽게 만드는 정규화, 노말라이즈(Normalize)를 위한 크기 값입니다.
사실은 B채널은 텅텅 비워도 상관없습니다. X 벡터와 Y 벡터 값이 있으면 삼각함수를 통해 Z 벡터를 연산 할 수 있기 때문입니다.

피타고라스의 정리에 의해 a^2 + b^2 = c^2 와 같습니다. 벡터 R의 제곱에 벡터 G의 제곱을 더한 뒤 제곱근을 연산하면 벡터 B의 크기가 나온다는 이야기입니다.
모든 노말 벡터는 단위벡터여야 합니다. 즉 벡터 B의 크기는 항상 1이고 이것은 3차원 벡터(1,1,1)에도 똑같이 적용됩니다. 즉 z축이 추가되더라도 x^2 + y^2 + z^2 = 1이고 이 식의 z를 좌변으로 넘기면
z^2 = 1 - x^2 - y^2
z = sqrt(1-x^2-y^2)이 됩니다!


URP에서 텍스처 타입을 노말맵으로 변경하는 것의 의미가 여기에서 밝혀집니다. *2 -1 연산을 통해 0~1 범위 텍스처를 -1~1 범위로 확장시킵니다. 그래서 노드에서 타입을 바꾸면 노말맵이 파랑 원색으로 바뀌는 것이었습니다.

z = sqrt(1-x^2-y^2) 공식을 그대로 노드로 재현하여 B채널을 만들었습니다. 초록색 네모 친 부분의 값이 완전히 같음을 알 수 있습니다.


여기에 텍스처 타입을 노말맵으로 바꿔서 DXT5NM압축을 하게 되면 RGBA 채널을 24비트 포맷으로 압축합니다.
24비트는 각 채널당 5/6/5/8 비트만큼의 데이터가 들어갑니다. 즉 노말맵이 XYZ에 대응되는 RGB를 그대로 쓰게 되면 R채널은 5, G채널은 6 만큼밖에 들어가지 않기 때문에 상당히 큰 데이터 손실이 일어납니다.
그래서 채널을 스위즐링 해서 A 채널과 G 채널에 X와 Y 데이터를 넣어주는 과정이 바로 인스펙터의 Normal map 설정입니다.
궁금증 해결!
Comentários