종제로
종제로 Devlog
종제로
전체 방문자
오늘
어제
  • 분류 전체보기 (43)
    • C, C++ (22)
      • C, C++ (10)
      • Modern C++ (4)
      • 전문가를 위한 C++ (책) (8)
    • DirectX 자체엔진 개발 (8)
    • 자료구조 알고리즘 (10)
      • 공부 (9)
      • 문제풀이 (1)
    • 자기 계발 (1)
    • 기타 (2)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 전문가를 위한 C++
  • C
  • 자료구조
  • 알고리즘
  • 모두의C언어
  • C++
  • c++ 11
  • directX
  • DirectX11
  • c++ 17

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
종제로
DirectX 자체엔진 개발

[DirectX 11] Normal Mapping

[DirectX 11] Normal Mapping
DirectX 자체엔진 개발

[DirectX 11] Normal Mapping

2022. 4. 17. 00:09

Normal Mapping

 

 

개발 기간 : 2021년 5월 17일 ~ 2021년 5월 18일

 

 

Normal Mapping은 다음과 같은 과정으로 진행됩니다.

1. Normal Map 텍스쳐를 로드합니다.

2. Tangent Space의 TBN 벡터 중 T 벡터를 계산합니다. 이 때, 메쉬의 각 정점 v마다, 메쉬에서 그 정점을 공유하는 모든 삼각형의 T 벡터의 평균을 내서 vertex별로 T 벡터를 구합니다.

3. PS에서는 픽셀 위치에서 T와 N 벡터를 알고있고, 외적을 통해 B 벡터를 구합니다. 그리고, TBN 행렬을 구축해 Normal Map의 Tangent Space Normal값을 World Space로 변환합니다.

 

 

1. Normal Map 텍스쳐를 로드합니다. 확장자에 따라 DXTK 라이브러리에서 호출하는 함수가 달라집니다.

void TextureObject::AddNormalMapSRV(wstring textureFileName)
{
	// file path가 비었는지 검사
	if (1 == textureFileName.compare(L""))
	{
		// 텍스쳐 폴더 경로
		wstring _filePath = L"../Data/Textures/";
		_filePath += textureFileName;

		ID3D11Resource* texResource = nullptr;

		ID3D11ShaderResourceView* normalMapSRV = nullptr;

		// 파일 확장자 검사용
		wstring _fileExtension = textureFileName.substr(textureFileName.size() - 4, 4);

		// dds 파일이면
		if (0 == _fileExtension.compare(L".dds"))
		{
			HR(CreateDDSTextureFromFile(m_Device,
				_filePath.c_str(), &texResource, &normalMapSRV));
		}
		// dds 이외의 파일이면
		else
		{
			HR(CreateWICTextureFromFile(m_Device, m_ImmediateContext,
				_filePath.c_str(), &texResource, &normalMapSRV));
		}

		m_NormalMapSRVVec.push_back(normalMapSRV);
		m_IsNormalMapExist = true;

		ReleaseCOM(texResource)	// view saves reference
	}
}

 

 

2. Tangent Space의 TBN 벡터 중 T 벡터를 계산합니다. 이 때, 메쉬의 각 정점 v마다, 메쉬에서 그 정점을 공유하는 모든 삼각형의 T 벡터의 평균을 내서 vertex별로 T 벡터를 구합니다.

/// 탄젠트 구하기
for (UINT i = 0; i < meshDataVec->size(); i++)
{
    // Face를 순회하면서
    for (UINT j = 0; j < meshDataVec->at(i)->m_meshface.size(); j++)
    {
        // 인덱스
        UINT v0Index = meshDataVec->at(i)->m_meshface[j]->m_vertexindex[0];
        UINT v1Index = meshDataVec->at(i)->m_meshface[j]->m_vertexindex[1];
        UINT v2Index = meshDataVec->at(i)->m_meshface[j]->m_vertexindex[2];

        // 위 인덱스의 버텍스들의 Pos
        Vector3 v0Pos = meshDataVec->at(i)->m_opt_vertex[v0Index]->m_pos;
        Vector3 v1Pos = meshDataVec->at(i)->m_opt_vertex[v1Index]->m_pos;
        Vector3 v2Pos = meshDataVec->at(i)->m_opt_vertex[v2Index]->m_pos;

        // 삼각형 면의 변 벡터
        Vector3 e1 = v1Pos - v0Pos;
        Vector3 e2 = v2Pos - v0Pos;

        // u
        float u0 = meshDataVec->at(i)->m_opt_vertex[v0Index]->u;
        float u1 = meshDataVec->at(i)->m_opt_vertex[v1Index]->u;
        float u2 = meshDataVec->at(i)->m_opt_vertex[v2Index]->u;

        // v
        float v0 = meshDataVec->at(i)->m_opt_vertex[v0Index]->v;
        float v1 = meshDataVec->at(i)->m_opt_vertex[v1Index]->v;
        float v2 = meshDataVec->at(i)->m_opt_vertex[v2Index]->v;

        // u, v 벡터
        float x1 = u1 - u0;
        float x2 = u2 - u0;
        float y1 = v1 - v0;
        float y2 = v2 - v0;

        // Tangent 구하기
        // 이용할 매트릭스들, uvMatrix의 역행렬을 바로넣었다.
        Matrix uvMatrixInverse(Vector3(y2, -x2, 0), Vector3(-y1, x1, 0), Vector3(0, 0, 0));
        Matrix e1e2Matrix(Vector3(e1.x, e2.x, 0), Vector3(e1.y, e2.y, 0), Vector3(e1.z, e2.z, 0));

        // 역행렬의 det
        float det = 1.0f / (x1 * y2 - x2 * y1);

        // tangent matrix, 탄젠트와 바이탄젠트가 들어있다. 바이탄젠트는 쉐이더에서 t와 n을 외적해구하므로 저장하지않음
        Matrix tangentMatrix = det * e1e2Matrix * uvMatrixInverse;
        Vector3 t(tangentMatrix._11, tangentMatrix._21, tangentMatrix._31);

        Vector3 n = meshDataVec->at(i)->m_meshface[j]->m_normal;
        t = t - (t.Dot(n)) * n;

        // tangent를 합산한다.
        meshDataVec->at(i)->m_opt_vertex[v0Index]->m_Tangent += t;
        meshDataVec->at(i)->m_opt_vertex[v1Index]->m_Tangent += t;
        meshDataVec->at(i)->m_opt_vertex[v2Index]->m_Tangent += t;
    }
}

 

 

3. PS에서는 픽셀 위치에서 T와 N 벡터를 알고있고, T와 N의 외적을 통해 B 벡터를 구합니다. 그리고, TBN 행렬을 구축해 Normal Map의 Tangent Space Normal값을 World Space로 변환합니다.

// 노말 맵 샘플을 World Space로 변환한다.
float3 NormalSampleToWorldSpace(float3 normalMapSample, float3 unitNormalW, float3 tangentW)
{
	// 각 성분을 [0, 1]에서 [-1, 1]로 사상한다.
	float3 normalT = 2.0f * normalMapSample - 1.0f;

	// tangent Space 기저 벡터를 만든다.
	float3 N = unitNormalW;		// 노말벡터
	float3 T = normalize(tangentW - dot(tangentW, N) * N);		// Tangent
	float3 B = cross(N, T);		// Bitangent, 노말 벡터와 탄젠트를 외적하여 구함
	
	float3x3 TBN = float3x3(T, B, N);

	// tangent Space에서 World Space로 변환한다.
	float3 bumpedNormalW = mul(normalT, TBN);

	return bumpedNormalW;
}

float4 PS(VertexOut pin)
{
	// ...
    
	float3 normalMapSample = gNormalMap.Sample(samAnisotropic, pin.Tex).rgb;
	pin.NormalW = NormalSampleToWorldSpace(normalMapSample, pin.NormalW, pin.TangentW);
    
    // ...
}

 

저작자표시 비영리 변경금지 (새창열림)

'DirectX 자체엔진 개발' 카테고리의 다른 글

[DirectX 11] Skinning Animation  (0) 2022.04.16
[DirectX 11] HDR, Tone Mapping  (0) 2022.04.15
[DirectX 11] 외곽선  (0) 2022.04.14
[DirectX 11] 파티클 시스템  (0) 2022.03.18
[DirectX 11] Emissive  (0) 2022.03.14
    'DirectX 자체엔진 개발' 카테고리의 다른 글
    • [DirectX 11] Skinning Animation
    • [DirectX 11] HDR, Tone Mapping
    • [DirectX 11] 외곽선
    • [DirectX 11] 파티클 시스템
    종제로
    종제로
    종제로 Devlog종제로 님의 블로그입니다.

    티스토리툴바

    단축키

    내 블로그

    내 블로그 - 관리자 홈 전환
    Q
    Q
    새 글 쓰기
    W
    W

    블로그 게시글

    글 수정 (권한 있는 경우)
    E
    E
    댓글 영역으로 이동
    C
    C

    모든 영역

    이 페이지의 URL 복사
    S
    S
    맨 위로 이동
    T
    T
    티스토리 홈 이동
    H
    H
    단축키 안내
    Shift + /
    ⇧ + /

    * 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.