8 – 03 Normal Map (노멀맵) (Unity Shader)
NormalMap이란, 실제 디테일이 없는 부분을 디테일이 있는 것처럼 보이게 만들기 위한 눈속임 맵입니다.
일반적으로 푸른색을 띄고 있으며, 빛을 속이기 위한 백터 데이터들로 이루어진 텍스쳐 파일입니다.
NormalMap을 이용해서 빛을 속이게 되면 마치 매우 많은 폴리곤으로 이루어진 오브젝트처럼 디테일이 표현되지만 실제로 폴리곤이 늘어나는 것은 아닙니다.
struct SurfaceOutputStandard 구조체 확인
https://docs.unity3d.com/Manual/SL-SurfaceShaders.html
normalmap을 직접 확인해보기 위해서 아래 에셋을 사용했습니다. (무료)
https://assetstore.unity.com/packages/3d/characters/humanoids/barbarian-warrior-75519 <- TestAsset
Shader "Custom/TestShader" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _BumpMap("Normal Map", 2D) = "white" {} // NormalMap // NormalMap과 _BumpMap의 관계성이 없지만 // 유니티의 다른 내장 쉐이더들이 저 명칭을 사용하고 있기 때문에 // 다른 쉐이더와의 호환성을 위해서 사용합니다. } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard #pragma target 3.0 sampler2D _MainTex; sampler2D _BumpMap; // NormalMap struct Input { float2 uv_MainTex; float2 uv_BumpMap; // NormalMap }; void surf (Input IN, inout SurfaceOutputStandard o) { float4 c = tex2D (_MainTex, IN.uv_MainTex); o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)); // NormalMap o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
위의 노말맵 텍스쳐를 데이터로 사용
확인을 위해 바디와 눈만 활성화
위의 빨간 박스는 노말맵이 없을 경우 노말맵이 없는 쉐이더를 쓰는 것을 추천하는 문구입니다.
노말맵 적용 전(좌) / 노말맵 적용 후(우)
위와 같이 빛의 음영을 속여서 디테일이 늘어난 것처럼 만드는 속임수 입니다.
NormalMap은 일반적인 텍스쳐가 아닙니다.
일반적인 게임용 포맷인 DXT1 혹은 DXT5가 아니라 DXTnm이라는 파일 포맷입니다.
이 파일 포맷은 일반적인 텍스쳐의 압축에 의한 NormalMap의 품질 저하를 막기 위하여 만든 AG파일 포맷입니다.
이 포맷은 NormalMap의 R과 G의 퀄리티를 최대한 보전하여 A와 G에 넣어 저장합니다.
이렇게 보전된 R과 G는 NormalMap의 X와 Y로 계산되며 Z는 삼각함수를 이용하여 수학적으로 추출됩니다.
그러므로 이 텍스쳐를 이용하여 NormalMap으로 온전하게 생성해내려면 앞에 설명한 공식이 적용되어 있는 함수를 이용합니다.
아래와 같이 해주면 NormalMap의 강도를 조절할 수 있습니다.
Shader "Custom/TestShader" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _BumpMap("Normal Map", 2D) = "white" {} // NormalMap _Test("TestFloat",Range(0.5, 3)) = 1 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard #pragma target 3.0 sampler2D _MainTex; sampler2D _BumpMap; // NormalMap float _Test; struct Input { float2 uv_MainTex; float2 uv_BumpMap; // NormalMap }; void surf (Input IN, inout SurfaceOutputStandard o) { float4 c = tex2D(_MainTex, IN.uv_MainTex); float3 d = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)); o.Normal = float3(d.x * _Test, d.y * _Test, d.z); // _Test에 따라서 수치 변화 2 2배 0.5 0.5배 o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
https://darkcatgame.tistory.com/84 <- 추가적인 내용