폴리곤 수를 늘리지 않고도

디테일을 추가할 수 있는 법선 맵핑

 

-이론-

각 픽셀의 법선 정보를 담고있는 텍스쳐를 법선맵(normal map)이라고 한다.

 

법선

법선백터의 경우 -1~0~1 의 값을 가진다 하지만 텍스쳐는 0~1의 값을 갖는다  그때문에 법선벡터의 값을 수식으로 0~1을 가진것 처럼 만들어 준다. 그렇게 해주면 법선 벡터의 0이 텍스쳐에서는 0.5가 되게 만들어 준다.

법선맵 RGB = XYZ x 0.5 + 0.5 

 

법선맵을 법선백터로 바꾸는 공식

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

 

법선공간

법선벡터는 각 표면을 기준으로 벡터의 방향이 결정된다

법선백터를 접선공간, 혹은 표면공간이라고 한다.

 

시멘틱

uv의 u나 v의 축을 접선이라고 부른다.

외적 계산으로 또 하나의 축을 구한다.이것을 종 법선이라고 한다.

법선맵,접선,종법선의 정보를 이미 모델이나 텍스쳐가 자지고있기 때문에 시멘틱을 사용하는 것만으로 이점보들을 구할 수 있다.

 

 

-HLSL함수-

TANGENT : 정점에서 접선정보를 불어올때 사용하는 시멘틱.

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

transpos() : 전치 행렬을 구하는 HLSL함수.

 

 

 

 

-실습-

 

정점 셰이더

 

struct VS_INPUT{
   float4 mPosition : POSITION;
   float3 mNormal : NORMAL;
   float2 mUV : TEXCOORD0;
   float3 mTangent: TANGENT;
   float3 mBinormal:BINORMAL;
};

//탄젠트와 바이노멀 시멘틱 구조체 선언

 

 

 

struct VS_OUTPUT{
   float4 mPosition : POSITION;
   float2 mUV: TEXCOORD0;
   float3 mLightDir : TEXCOORD1;
   float3 mViewDir: TEXCOORD2;
   float3 T : TEXCOORD3;
   float3 B : TEXCOORD4;
   float3 N : TEXCOORD5;
};

 

 

float4x4 gWorldMatrix;
float4x4 gWorldViewProjectionMatrix;

float4 gWorldLightPosition;
float4 gWorldCameraPosition;


VS_OUTPUT vs_main (VS_INPUT Input){
   VS_OUTPUT Output;
  
   Output.mPosition = mul(Input.mPosition, gWorldMatrix);
   Output.mUV = Input.mUV;
  
   float4 worldPosition = mul(Input.mPosition, gWorldViewProjectionMatrix);
   float3 lightDir = worldPosition.xyz - gWorldLightPosition.xyz;
   Output.mLightDir = normalize(lightDir);
  
  
  
   float3 viewDir = normalize(Output.mPosition.xyz - gWorldCameraPosition.xyz);
   Output.mViewDir = viewDir;
  
   
   
   float3 worldNormal = mul(Input.mNormal,(float3x3)gWorldMatrix);
   Output.N = normalize(worldNormal);
   //노멀 눨드 구하기


   float3 worldTangent = mul(Input.mNormal, (float3x3)gWorldMatrix);
   Output.T = normalize(worldTangent);
    //탄젠트 월드 구하기  


   float3 worldBinormal = mul(Input.mBinormal, (float3x3)gWorldMatrix);
   Output.B = normalize(worldBinormal);
   //바이노멀 월드 구하기


   return Output;
}

 

 

 

픽셀 셰이더

 

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);
//탄젠트노멀을 0~1까지로 바꿔주는 수식  


   float3x3 TBN = float3x3(normalize(Input.T), normalize(Input.B),normalize(Input.N));
   TBN = transpose(TBN);
//전치 행열  


   float3 worldNormal = mul(TBN, tangentNormal);
  
   float3 lightDir = normalize(Input.mLightDIr);
   float3 diffuse = saturate(dot(worldNormal, -lightDir));
  
   float4 albedo = tex2D(DiffuseSampler, Input.mUV);
   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 specularIntendity = tex2D(SpecularSampler, Input.mUV);
         specular *= specularIntendity.rgb * gLightColor;
      }
  
   float3 ambient = float3(0.1f,0.1f,0.1f) * albedo;
  
   return float4(ambient + diffuse * specular,1);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Posted by 프리랜서 디자이너