[DirectX 11] Normal Mapping

2021. 12. 18. 03:42·Graphics

Normal Mapping

 

Normal Map을 이용한 접선 공간에서의 수직 축인 Normal을 추출하여 Light 연산을 하는 방식이다.

기존 World 기준 Normal값을 이용하여 Tangent를 추출해야하는데, 삼각형을 이루는 점 3개를 통해 Tangent를 유도한다.

Binormal의 경우 Normal값과 Tangent값만 있다면 외적을 통해 구할 수 있으므로 공간 절약을 위해 Shader 측에서 하기로 한다.

 

World Space Normal / Tangent Space Normal

위 이미지에서 오브젝트 또는 World Space Normal Mapping(왼쪽), Tangent Space Normal Mapping(오른쪽)을 통해

왼쪽의 World 기준 축을 잡게 되면 좀더 세밀한 Light연산이 불가능하다. 오른쪽 이미지처럼 정점 기준 표면의 축을 기준으로 Normal 값을 적용시키게 된다면 좀 더 정밀한 Light 연산을 통해 깊이 있는 이미지를 출력할 수 있게 될 것이다.

Normal, Tangent, Binormal을 축으로하는 공간


// Tanget 값 설정..
for (unsigned int i = 0; i < pMesh->m_MeshFace.size(); i++)
{
	int index0 = pMesh->m_MeshFace[i]->m_VertexIndex[0];
	int index1 = pMesh->m_MeshFace[i]->m_VertexIndex[1];
	int index2 = pMesh->m_MeshFace[i]->m_VertexIndex[2];

	Vertex* vertex0 = pMesh->m_VertexList[index0];
	Vertex* vertex1 = pMesh->m_VertexList[index1];
	Vertex* vertex2 = pMesh->m_VertexList[index2];

	DirectX::SimpleMath::Vector3 ep1 = vertex1->m_Pos - vertex0->m_Pos;
	DirectX::SimpleMath::Vector3 ep2 = vertex2->m_Pos - vertex0->m_Pos;

	DirectX::SimpleMath::Vector2 uv1 = { vertex1->m_UV.x - vertex0->m_UV.x,
										 vertex1->m_UV.y - vertex0->m_UV.y };
	DirectX::SimpleMath::Vector2 uv2 = { vertex2->m_UV.x - vertex0->m_UV.x,
										 vertex2->m_UV.y - vertex0->m_UV.y };

	float den = 1.0f / (uv1.x * uv2.y - uv2.x * uv1.y);

	// 현재 픽셀 쉐이더에서 연산을 통해 T, B, N을 얻는데
	// 픽셀 쉐이더 내의 연산은 버텍스 쉐이더의 연산에 비해 호출 횟수가 차원이 다르게 크므로 부하가 올 수 있다..
	// 법선맵의 픽셀의 색은 픽셀 쉐이더 안이 아니면 얻을수 없기 때문에 픽셀 쉐이더에서 연산을 한다고 한다..
	/// 현재 연산과정을 버텍스 쉐이더로 옮겨둠
	DirectX::SimpleMath::Vector3 tangent = (ep1 * uv2.y - ep2 * uv1.y) * den;
	tangent.Normalize();

	// 유사 정점은 값을 누적하여 쉐이더에서 평균값을 사용하도록 하자..
	vertex0->m_Tanget += tangent;
	vertex1->m_Tanget += tangent;
	vertex2->m_Tanget += tangent;
}

Face Vertex 3개를 이용하여 Tangent Normal 구하는 방식.

 

VertexOut Mesh_VS(MeshVertexIn vin)
{
    ...

	Tangent = mul((float3x3) gMeshWorld, vin.TangentL);
    N = vout.NormalW;
    T = normalize(Tangent - dot(Tangent, N) * N);
    B = cross(N, T);
    
    vout.TBNW = float3x3(T, B, N);
};

Vertex Shader에서 Normal, Tangent를 이용하여 Binormal 구한 후 TBN 행렬 저장.

 

PixelOut Deferred_PS(VertexIn pin)
{

    ...
    
    normalW = mul(2.0f * gNormalMap.Sample(gSamWrapLinear, pin.Tex).rgb - 1.0f, pin.TBNW);
	
    ...
    
};

Normal Map 표현 방식

Normal Texture는 [0 ~ 1] 사이의 값이 들어있지만 우리가 필요한 Vector 값은 [-1 ~ 1] 이므로 rgb 값을 변환 시켜준다.

Vertex Shader에서 구해둔 TBN과 연산을 하여 Normal값 추출.

 

With Normal Map / WithOut Normal Map

화면상에 Normal값을 출력한 화면.

 

With Normal Map Light / WithOut Normal Map Light

Normal Map 적용 비교 출력 화면.

 

'Graphics' 카테고리의 다른 글

[DirectX 11] Animation  (0) 2021.12.18
[DirectX 11] Skinning Mesh  (0) 2021.12.18
[DirectX 11] Gamma Correction  (0) 2021.12.18
[DirectX 11] Deferred Rendering GBuffer  (0) 2021.12.14
[DirectX 11] Shadow Map  (0) 2021.12.14
'Graphics' 카테고리의 다른 글
  • [DirectX 11] Animation
  • [DirectX 11] Skinning Mesh
  • [DirectX 11] Gamma Correction
  • [DirectX 11] Deferred Rendering GBuffer
KyuHwang
KyuHwang
  • KyuHwang
    DirectX Engine
    KyuHwang
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • C++ (4)
      • CS (0)
      • Graphics (32)
      • DLL (2)
      • Shader (7)
      • Project (4)
      • ETC (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • YouTube
  • 공지사항

  • 인기 글

  • 태그

    texture block compression
    shader
    Win API
    C ++
    DLL Interface
    hlsl
    nvtt
    Explicit Linking
    DirectX 2D
    Define Function
    Alpha Blending
    bc format
    animation retargeting
    Project
    Define Macro
    Shader Reflection
    RunTime Compile Shader
    Order Independent Transparency
    std::is_base_of
    Implicit Linking
    rigging chain
    dll
    Directx11
    animation constraint
    Return Type Operator
    Shader Macro
    Shader Resource 관리
    mobile format
    std::enable_if
    Hash Code
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
KyuHwang
[DirectX 11] Normal Mapping
상단으로

티스토리툴바