오다기리 박의 알고리즘 노트

[셰이더 프로그래밍 입문] 7. 법선매핑 본문

컴퓨터 그래픽스/쉐이더

[셰이더 프로그래밍 입문] 7. 법선매핑

오다기리 박 2018. 7. 1. 23:36

7. 법선매핑

  • TANGENT : 정점에서 접선정보를 불러올 때 사용하는 시맨틱

  • BINORMAL : 정점에서 종법선정보를 불러올 때 사용하는 시맨틱

  • transpose() : 전치행렬을 구하는 함수

  • 접선공간 : 법선맵 안에 저장된 법선이 존재하는 공간

평면 위에서 울퉁불퉁한 표면의 법선을 사용한 결과


  • 표면 위에서 법선이 어떻게 정의되어 있는냐에 따라서 조명효과를 결정하기 위해 각 텍셀에 법선을 저장한다



법선맵

  • 법선맵(normal map) : 각 픽셀에 사용할 법선정보를 담고 있는 텍스쳐(접선공간으로 저장)

  • 법선매핑(normal mapping) : 법선맵을 이용해서 조명을 계산하는 기법

  • 법선맵 RGB = 법선벡터 XYZ x 0.5 + 0.5

  • 법선벡터 XYZ = 법선맵 RGB x 2 -1


접선공간(tangent space)


  • 표면의 바깥쪽 방향을 법선의 +z로 둔다. 라는 명제를 사용하여 법선맵을 만든다.

  • 정점셰이더의 입력데이터에서 NORMAL, TANGENT, BINORMAL 시맨틱으로 법선, 종법선, 접선 정보를 구해온다.

  • 접선 T=(Tx,Ty,Tz), 종법선 B=(Bx,By,Bz), 법선 N=(Nx,Ny,Nz)일 때 접선공간변환에 사용하는 -열기준 변환행렬 =   -행기준 변환행렬 =

  • 열기준 행렬 : mul(벡터,행렬)  / 행기준 행렬 : mul(행렬,벡터)



▶렌더몽키 코드 

- Texture State



- 버텍스 셰이더
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
float4x4 gWorldViewProjectionMatrix;
float4x4 gWorldMatrix;
 
float4 gWorldLightPosition;
float4 gWorldCameraPosition;
 
struct VS_INPUT
{
   float4 mPosition:POSITION;
   float3 mNormal:NORMAL;
   float3 mBinormal:BINORMAL;
   float3 mTangent:TANGENT;
   float2 mUV:TEXCOORD0;
};
 
struct VS_OUTPUT
{
   float4 mPosition:POSITION;
   float2 mUV:TEXCOORD0;
   float3 mLightDir:TEXCOORD1;
   float3 mViewDir:TEXCOORD2;
   float3 T:TEXCOORD3;
   float3 B:TEXCOORD4;
   float3 N:TEXCOORD5;
};
 
 
VS_OUTPUT vs_main(VS_INPUT Input)
{
   VS_OUTPUT Output;
   
   Output.mPosition=mul(Input.mPosition,gWorldViewProjectionMatrix);
   Output.mUV=Input.mUV;
   
   float4 worldPosition=mul(Input.mPosition,gWorldMatrix);
   float3 lightDir=worldPosition.xyz-gWorldLightPosition.xyz;
   Output.mLightDir=normalize(lightDir);
   
   float3 viewDir=normalize(worldPosition.xyz-gWorldCameraPosition.xyz);
   Output.mViewDir=viewDir;
   
   float3 worldNormal=mul(Input.mNormal,(float3x3)gWorldMatrix);
   Output.N=normalize(worldNormal);
   
   float3 worldTangent=mul(Input.mTangent,(float3x3)gWorldMatrix);
   Output.T=normalize(worldTangent);
   
   float3 worldBinormal=mul(Input.mBinormal,(float3x3)gWorldMatrix);
   Output.B=normalize(worldBinormal);
   
   return Output;
}
cs

- 픽셀 셰이더
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
sampler2D DiffuseSampler;
sampler2D SpecularSampler;
sampler2D NormalSampler;
 
float3 gLightColor;
 
struct PS_INPUT
{
   float2 mUV:TEXCOORD0;
   float3 mLightDir:TEXCOORD1;
   float3 mViewDir:TEXCOORD2;
   float3 T:TEXCOORD3;
   float3 B:TEXCOORD4;
   float3 N:TEXCOORD5;
};
 
float4 ps_main(PS_INPUT Input):COLOR
{
   float3 tangentNormal=tex2D(NormalSampler,Input.mUV).xyz;
   tangentNormal=normalize(tangentNormal*2-1);
   
   float3x3 TBN=float3x3(normalize(Input.T),normalize(Input.B),normalize(Input.N));
   TBN=transpose(TBN);
   float3 worldNormal=mul(TBN,tangentNormal);
   
   float4 albedo=tex2D(DiffuseSampler,Input.mUV);
   float3 lightDir=normalize(Input.mLightDir);
   float3 diffuse=saturate(dot(-lightDir,worldNormal));
   diffuse=gLightColor*albedo.rgb*diffuse;
   
   float3 specular=0;
   if(diffuse.x>0)
   {
      float3 reflection=reflect(lightDir,worldNormal);
      float3 viewDir=normalize(Input.mViewDir);
      
      specular=saturate(dot(reflection,-viewDir));
      specular=pow(specular,20.0f);
      
      float4 specularIntensity=tex2D(SpecularSampler,Input.mUV);
      specular*=specularIntensity.rgb*gLightColor;
   }
   
   float3 ambient=float3(0.1f,0.1f,0.1f)*albedo;
   
   return float4(ambient+diffuse+specular,1);
   
}
cs

- 결과

폴리곤 수를 늘리지 않고도 표면의 울퉁불퉁한 효과를 추가하는 법선매핑을 적용한 결과입니다. 표면 위에서 법선이 어떻게 정의되어 있는냐에 따라서 조명효과가 결정됩니다.

img