개발 기간 : 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 |