12
20

<본 포스팅은 강좌가 아닙니다.>

조명을 계산할때 각 정점의 법선(노말) 정보를 가져와서 조명효과를 결정했다.

 

박스면의 법선을 생각해보자면 한 면은 아래와 같은 모습일 것이다.

 

 

만약 벽돌처럼 울퉁불퉁한 면이라면 법선이 이렇게 되지 않을 것이다.

 

 

이렇게 각 픽셀에 사용할 법선 정보를 담은 텍스처를 노말맵, 법선맵이라고 한다.

 

텍스처에 법선 정보는 3개 성분 xyz를 갖는다. 텍스쳐는 RGB가 있으므로

 

xyz를 각 각 RGB에 대입하면 된다.

 

왼손좌표계로 z가 forward(높여주는)방향이여서 울퉁불퉁한 면을 표현하게 한다.

 

그래서 노말맵이 대부분 푸른빛을 띄고 있는 것이다. + 구간에서 존재하기 때문이다.

 

z값이 최소한 0.5~1이다. 왜 0이 아닌지는 아래 공식을 보면 된다.

 

Julian Herzog &amp;amp;nbsp;( Website )

 

저장할때 정규화한 벡터를 쓰는데 이 범위는 -1~1이다.

 

 -1을 0으로 하고 0를 0.5로 +1를 +1로 변환을 해야한다.

 

왜냐하면 텍스처의 각 채널에서 가질 수 있는 값의 범위는 0~1이기 때문이다.

 

법선벡터->법선맵 법선맵->법선벡터
법선맵 RGB = 법선벡터 XYZ * 0.5 + 0.5 법선벡터 XYZ = 법선맵 RGB * 2 - 1

 

접선 공간 행렬 : Z는 정점의 법선(Normal),

X는 UV중 하나의 값 접선(Tangent),

Y는 두 벡터의 외적 결과인 종법선(Binnormal)

이렇게 접선 공간 변환에 사용하는 행렬를 만들 수 있다.

행 기준 행렬

 

이제 렌더몽키를 켜서 제작해보자.

 

vs input으로 받을 법선, 접선, 종법선을 추가한다. (Stream Mapping)

 

VS 버텍스셰이더

버텍스 셰이더 구조체, 변수이다.

행렬은 월드행렬과 월드뷰투영행렬으로 구성했다.

 

기존엔 정점에서 법선정보를 가져왔지만

노말맵 즉, 텍스쳐에서 법선정보를 가져오므로

LightDir인 방향벡터를 전달해 픽셀셰이더에서 조명연산을 한다.

 

mT mB mN는 접선, 종법선, 법선 정보이다.

이 벡터가 필요한것은 접선공간을 변환하기 위해서다.

월드공간에서 계산하기위해 월드 공간으로 변환하는 행렬 만들 수 있다.

 

 

입사광 방향 벡터와 카메라 벡터 구해야 한다.

빛 방향 벡터 =월드 위치 - 라이트 위치 -> 정규화

뷰 벡터 = 월드 위치 - 카메라 위치 -> 정규화

 

물체 공간에 있는 법,접 종법 선 정보를 월드 공간으로 바꿔서

Output를 리턴해 픽셀셰이더로 넘겨준다.

 

PS 픽셀셰이더

픽셸셰이더 구조체와 변수

 

법선맵(Normal map)를 tex2D함수로 읽어와 변수에 저장한다. 

읽어오면 텍스쳐값이므로 0~1의 범위를 갖는다.

그리고 -1~+1인 법선벡터 범위를 올바른 범위로 확장시켜줌.

tangentNormal * 2 - 1  // 0.5를 넣으면 0이된다.

 

받아온 월드 공간의 법접종법선 정보로 TBN 행렬을 만든다.

transpose() 전치행렬로 만든다. 월드 공간으로 바꾸기 위해

 

tangentNoraml에 곱하여 월드 공간에서의 법선을 구할 수 있다.

float3 worldNormal = mul (TßN , tangentNormal);

 

그런 후에 빛, 카메라 방향 벡터로 난반사광의 양과

정반사광의 양을 구해 각 맵에 곱한다.

 

결과 

 

더보기
더보기
더보기
더보기
//VS
float4x4 gWorldMatrix;
float4x4 gWorldViewProjMatrix;

float4 gWorldLightPos;
float4 gWorldCamPos;

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

struct VS_OUTPUT 
{
   float4 mPosition : POSITION;
   float2 mUV       : TEXCOORD0;
   //float3 mDiffuse  : TEXCOORD1; 정점법선이 아닌 텍셀법선
   float3 mLightDir : TEXCOORD1;
   float3 mViewDir  : TEXCOORD2;
   float3 mT        : TEXCOORD3;
   float3 mB        : TEXCOORD4;
   float3 mN        : TEXCOORD5;
};

VS_OUTPUT vs_main( VS_INPUT Input )
{
   VS_OUTPUT Output;
 
   Output.mPosition = mul( Input.mPosition, gWorldViewProjMatrix );
   Output.mUV = Input.mUV;
   
   float4 worldPos = mul(Input.mPosition, gWorldMatrix);

   float3 lightDir = worldPos.xyz - gWorldLightPos.xyz;
   Output.mLightDir = normalize(lightDir);
   
   float3 viewDir = normalize(worldPos.xyz-gWorldCamPos.xyz);
   Output.mViewDir = viewDir;
   
   float3 worldNormal = mul(Input.mNormal, (float3x3)gWorldMatrix);
   Output.mN=normalize(worldNormal);
   
   float3 worldTangent = mul(Input.mTangent, (float3x3)gWorldMatrix);
   Output.mT=normalize(worldTangent);
   
   float3 worldBinormal = mul(Input.mBinormal, (float3x3)gWorldMatrix);
   Output.mB=normalize(worldBinormal);
  
   return  Output;
}

//PS
struct PS_INPUT
{
   float2 mUV : TEXCOORD0;
   float3 mLightDir : TEXCOORD1;
   float3 mViewDir : TEXCOORD2;
   float3 mT : TEXCOORD3;
   float3 mB : TEXCOORD4;
   float3 mN : TEXCOORD5;
};

sampler2D DiffuseSampler;
sampler2D SpecularSampler;
sampler2D NormalSampler;
float3 gLightColor;

float4 ps_main( PS_INPUT Input) : COLOR
{  
   float3 tangentNormal = tex2D(NormalSampler, Input.mUV).xyz;
   tangentNormal = normalize(tangentNormal*2-1);
   
   float3x3 TBN = float3x3(normalize(Input.mT), normalize(Input.mB),normalize(Input.mN));
   TBN = transpose(TBN);
   float3 worldNormal = mul(TBN, tangentNormal); 
   
   float4 albedo = tex2D(DiffuseSampler, Input.mUV);
   float3 lightDir = normalize(Input.mLightDir);
   float3 diffuse = saturate(dot(worldNormal, -lightDir));
   diffuse = gLightColor * albedo.rgb*diffuse;
   
   float3 specular = 0;

   if(diffuse.x > 0.0f)
   {
      float3 reflection = reflect(lightDir, worldNormal);
      float3 viewDir = normalize(Input.mViewDir);
      
      specular = saturate(dot(reflection, -viewDir));
      specular = pow(specular,20.0f);
      
      float4 specularInten=tex2D(SpecularSampler, Input.mUV);
      specular*=specularInten.rgb*gLightColor;
   }
   
   float3 ambient = float3(0.1f, 0.1f, 0.1f)*albedo;
   
   return float4(ambient+diffuse+specular,1);
}
COMMENT