• Home
  • About
    • 게임 개발자 유정룡 photo

      게임 개발자 유정룡

      포트폴리오

    • Learn More
    • Email
    • Github
    • Bitbucket
  • Projects
    • All Projects
    • All Tags

Direct3D11 공부 37일차(NormalMap)

28 Jul 2021

Reading time ~2 minutes

NormalMap

물체를 표현할 때 음영에 따라 폴리곤을 만들면 그만큼 많은 자원이 들어가 퍼포먼스가 떨어진다.

그래서 NomalMap을 사용해서 음영을 나타낸다.

이 이미지가 NormalMap이다.

이런 식으로 빛이 왔을때 내적각을 이용해 밝기를 표현한다.

일단 현재 받은 데이터는 2D 데이터이기 때문에 이 데이터를 3D 데이터로 변환해야 한다.

평면상의 노멀벡터는 Y축 모델 공간에서는 Z축이기 때문에 반대로 생각해야 한다.

일단, 받은 데이터는 UV로 받았다.

X : U - Tangent

Y : V - BiNormal or BiTangent (normal, tangent 외적값)

Z : Normal

이 데이터로는 Z축을 표현할 수 없기 때문에 계산을 해줘야 한다.

각 픽셀의 탄젠트 공간을 구한 후 그 공간의 수직인 방향(Normal)을 구한다.

이제 이 NormalVector와 현재 라이트를 내적하여 밝기를 구해준다.

위에 NormalMap을 보면 파란색이 많은 것을 볼 수 있는데 이는 굴곡을 만들 때 밝은 방향(Z축, B의 값)이 많기 때문이다.

Shader

void NormalMapping(float2 uv, float3 normal, float3 tangent, SamplerState samp)
{
    float4 map = NormalMap.Sample(samp, uv);

    [flatten]
    if (any(map.rgb) == false)
    { return; }

    float3 coord = map.rgb * 2.0f - 1.0f; // -1 ~ +1
    
    // 탄젠트 공간
    float3 N = normalize(normal); // Z
    float3 T = normalize(tangent - dot(tangent, N) * N); // X 정규 직규화
    float3 B = cross(N, T); // Y
    
    float3x3 TBN = float3x3(T, B, N);

    coord = mul(coord, TBN);
    
    Material.Diffuse *= saturate(dot(-GlobalLight.Direction, coord));
}

위에 말한 개념을 바탕으로 NormalMapping하는 함수를 작성한다.

받은 Noraml값을 정규화 해준뒤, 그 값을 이용해 정규 직규화를 해준다.

그리고 외적을 통해 BiNormal을 구해준다.


float4 PS(MeshOutput input) : SV_Target
{    
    NormalMapping(input.Uv, input.Normal, input.Tangent);

    Texture(Material.Diffuse, DiffuseMap, input.Uv);
    Texture(Material.Specular, SpecularMap, input.Uv);
    
    MaterialDesc output = MakeMaterial();
    MaterialDesc result = MakeMaterial();
    
    ComputeLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    ComputePointLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    ComputeSpotLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    return float4(MaterialToColor(result), 1.0f);
}

적용을 시켜준다.

Mesh

Mesh 에 있던 MeshVertex를

typedef VertexTextureNormal MeshVertex

Tangent를 사용해야 하기 때문에

typedef VertexTextureNormalTangent MeshVertex;

으로 변경해준다.

그런 뒤, 이에 맞게 Tangent값들은 cube, cylinder, grid, sphere에 바꿔준다.

이 중에 Sphere만 다뤄보자

Sphere

Vector3 n;
D3DXVec3Normalize(&n, &p);

Vector3 t = Vector3
(
	-(radius * sinf(phi) * sinf(theta)),
	0.0f,
	(radius * sinf(phi) * cosf(theta))
);

Vector2 uv = Vector2(theta / (Math::PI * 2), phi / Math::PI);
			
v.push_back(MeshVertex(p.x, p.y, p.z, uv.x, uv.y, n.x, n.y, n.z, t.x, t.y, t.z));

현재 방향과 반대방향으로 uv를 만들어 준다.

이 상태로 NormalMap을 적용해주면

음영이 만들어지는데 차이를 못느낄것 같아 각각 적용한 모습을 따로 뽑아봤다.

아무것도 적용 안한 상태

텍스처만 적용한 상태

Normal Map만 적용한 상태

전체를 적용한 상태

확실히 음영이 잘 나타난다.



DirectX Share Tweet +1