파일에서 Texture를 불러올 수 있다면
이제 HeightMap을 불러와서 지형의 높낮이를 만들고,
지형에 음영을 넣어보자
먼저 HeightMap의 기본적인 원리는 이러하다
HeightMap은 단색으로 이루어져있고
이런 느낌으로 밝은 부분과 어두운 부분이 섞여있는 이미지 형태로 되어있다
이 이미지에서
밝은 부분은 지형에서 높은 곳, 어두운 부분은 지형에서 낮은 곳을 의미한다
결과적으로 이 HeightMap의 색을 가져와서 높이로 사용하는 것이다
vector<Color> heights;
heightMap->ReadPixel(DXGI_FORMAT_R8G8B8A8_UNORM, &heights);
width = heightMap->GetWidth();
height = heightMap->GetHeight();
vertexCount = width * height;
vertices = new TerrainVertex[vertexCount];
for (UINT z = 0; z < height; z++)
{
for (UINT x = 0; x < height; x++)
{
UINT index = width * z + x;
//uv좌표가 좌상단이 (0,0)이라서 반대로 나오는 것을 뒤집어준다
UINT pixel = width * (height - 1 - z) + x;
vertices[index].Position.x = (float)x;
vertices[index].Position.y = heights[pixel].r * 255.0f / 10.0f; //0~1범위를 0~255로 변경
vertices[index].Position.z = (float)z;
}
}
이처럼 HeightMap의 크기만큼 반복문을 돌며
해당 위치의 rgb중 한 값을 가져와 (rgb값이 모두 같기에 아무거나 가져오면 된다) y값(높이)로 사용한다
10으로 다시 나누는 이유는 나누지 않으면 너무 높게 나오기 때문에 보기 좋게 만들기 위해 나누어주었다
RasterizerState에서 Wireframe 으로 설정해두었던 것을 끄면 이렇게 새하얀 지형이 나온다
카메라를 돌려보면 확실히 높낮이는 있지만 음영이 없어 이상하게 느껴진다
음영을 넣는 방법도 어렵지는 않다
음영은 Normal벡터를 사용하여 구한다
모든 Vertex의 Normal벡터를 먼저 구해준다
L : Light
Normal벡터가 있고, Light가 일정 각도를 가지고 Vertex에 비추어진다
그러면 우리는 그 Light와 Normal벡터가 이루는 각도에 따라서 음영을 넣어주는 방식이다
하지만 이처럼 Light가 들어오는 방향 그대로 벡터를 사용한다면
이는 우리가 원하는 Normal벡터와 Light벡터가 이루는 사이각이 나오는게 아니므로
Light를 반대쪽 방향으로 바꾸어준다
그 다음부터는 쉽다
(- Light)벡터와 Normal벡터의 내적값인 cos θ는 Normal 벡터와 똑같이 있을 때 가장 큰 값인 1,
바닥에 있을 때 0으로 나오게 된다
cos그래프는 대칭 그래프이므로
빛이 좌우 어디서 들어오는지는 빛의 세기에 영향을 끼치지 않는다
이제 NormalVector를 구해보자
for (UINT i = 0; i < indexCount / 3; i++)
{
UINT index0 = indices[i * 3 + 0];
UINT index1 = indices[i * 3 + 1];
UINT index2 = indices[i * 3 + 2];
TerrainVertex v0 = vertices[index0];
TerrainVertex v1 = vertices[index1];
TerrainVertex v2 = vertices[index2];
Vector3 a = v1.Position - v0.Position;
Vector3 b = v2.Position - v0.Position;
Vector3 normal;
//외적, 순서 주의
D3DXVec3Cross(&normal, &a, &b);
vertices[index0].Normal += normal;
vertices[index1].Normal += normal;
vertices[index2].Normal += normal;
}
for (UINT i = 0; i < vertexCount; i++)
{
D3DXVec3Normalize(&vertices[i].Normal, &vertices[i].Normal);
}
셰이더
VertexOutput VS(VertexInput input)
{
VertexOutput output;
output.Position = mul(input.Position, World);
output.Position = mul(output.Position, View);
output.Position = mul(output.Position, Projection);
output.Normal = mul(input.Normal, (float3x3)World);
return output;
}
행렬의 곱셈을 위해 World를 3x3으로 변환하여 곱해준다
World가 회전되어있으면 Normal도 회전되어야하므로 월드로 변환하는 곱셈을 한다
Vertex Shader에서 float 4의 w가 0이면
4x4 matrix에서 position을 의미하는 x, y, z가 곱해지지 않으므로 w는 방향으로 사용하게 된다
반면 w가 1이라면 위치로 사용하게 된다
이 w는 Pixel Shader에서는 화면 비율을 나타내는 동차로 사용된다
float4 PS(VertexOutput input) : SV_Target
{
float3 normal = normalize(input.Normal);
float3 light = -Direction;
return float4(1, 1, 1, 1) * dot(light, normal);
}
'> Study > DirectX' 카테고리의 다른 글
[DirectX] Texture (0) | 2024.01.23 |
---|---|
[DirectX] Grid, Camera (0) | 2024.01.04 |
[DirectX] World Matrix (2) | 2023.12.21 |
[DirectX] Triangle, Rectangle (0) | 2023.12.21 |
[DirectX] Vertex (1) | 2023.12.20 |