Terrain(지형) 렌더링
1. 높이맵 (HeightMap)
지형 렌더링은 높이맵(heightmap)에 의존한다.
높이맵이란, 지형의 높이를 나타내는 y값을 이미지 파일의 명암값(0~255)으로 표현하는 것을 말한다.
높이맵은 위에서 보았을 때 x와 z축의 길이가 동일해서 높이에 대한 정보만으로 지형을 표현할수 있다.
흑백 이미지로 표현하기에 데이터의 크기가 작고, 비교적 쉽게 만들수 있다. 또한, 인덱스 버퍼를 사용할 수 있어서 메모리를 절약할수 있다.
(다른 방법들은 인덱스 버퍼를 사용하지 않는 것인가?)
반면, 단점으로는 지형의 기울기에 따라, 폴리곤의 증가, 감소가 불가능하다는 것이 있다.
이런 단점을 보안하기 위해서 쿼드 트리(옥트리)와 ROAM, BSP등이 있다.
문제점을 없애기보다는 컬링이나 공간 관리도 보완한다.
우선 높이맵을 표현할 구조체를 만든다.
struct TERRAINVERTEX
{
enum _FVF { FVF=(D3DFVF_XYZ | D3DFVF_NORMAL| D3DFVF_TEX1) };
D3DXVECTOR3 p;
D3DXVECTOR3 n;
D3DXVECTOR2 t;
};
p에는 높이맵에서 읽어온 좌표값, n은 p를 노말라이즈시킨 벡터, t는 텍스쳐좌표를 뜻한다.
(여기서 정점 포멧에 대한 정의가 한 번 헷갈렸다. 정점 포멧이란, 필요한 정점의 성분의 형식을 지정하는 것을 말한다.)
(정점 포멧에는 Diffuse, normal, pspecular, xyz, tex 등의 성분을 사용할수 있는데, 이 많은 성분중에 사용할 성분만을 지정하여 정점을 정의하는 것이다.)
높이맵 구조체을 정의했다면, BuildHeightMap 함수에서 높이맵을 불러와서, 각 정점의 y좌표를 저장시킨다.
이렇게 높이맵을 만들면 지형의 첫 시작점이 월드 좌표의 원점에 잡히게 된다.
(그림판으로 그렸는데, 생각만큼 안그려진다;;)
이 경우에 프로그래밍적(렌더링)으로 문제점은 없지만, 후에 컬링과 카메라와 연계했을 시에 논리적으로 불편하게 된다.
(불편할 것같지 않다는 생각이 많이들지만, 그렇다고하니까 일단 말한다. 맵툴만들때에 원점을 옮기지 않고 만들어보자)
이런 식으로 지형을 월드 좌표계의 원점에 위치하게 수정해야한다.
x = (x - width) / 2
z = (z - depth) / 2
2. 텍스처링
지형에 텍스처를 입히는 방법은 두 가지가 있다.
첫 번째는 미리 만들어둔 텍스처 파일을 읽어서 입히는 방법이다. 기존의 방식대로 텍스처를 입히는 방식이다.
두 번째는 절차적으로 코드로 '텍스처를 파일을 만들고 입히는 방법'이다.
첫 번째 방법은 IDirect3DTexture9를 이용하는 방법이기에 딱히 쓰지 않겠다.
두 번째 방식으로 텍스처를 입히기 위해서는 우선 빈 텍스처를 생성해야한다.
그후에, 높이맵에서 읽어온 값에 따라서 빈 텍스처의 텍셀값을 지정해준다.
CreateTexture()
{
D3DXCreateTexture( _device, width, height, D3DX_DEFAULT, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, tex);
// D3DXCreatrTexture 참고 : http://telnet.or.kr/directx/graphics/reference/d3dx/functions/texture/d3dxcreatetexture.htm 참조
//etc...
for(int i = 0; i < texHeight; i++)
{
for(int j = 0; j < texWidth; j++)
{
//getHeightmapEntry는 매개변수로 받아온 x,z좌표의 높이(y)를 받아오는 함수.
float height = (float)getHeightmapEntry(i, j) / _heightScale;
if( (height) < 42.5f ) c = d3d::BEACH_SAND;
else if( (height) < 85.0f ) c = d3d::LIGHT_YELLOW_GREEN;
else if( (height) < 127.5f ) c = d3d::PUREGREEN;
else if( (height) < 170.0f ) c = d3d::DARK_YELLOW_GREEN;
else if( (height) < 212.5f ) c = d3d::DARKBROWN;
else c = d3d::WHITE;
}
}
}
3. 난반사광 (diffuse lighting)
용책에서는 수학적 연습을 목적으로 조명을 직접 계산한다.
하지만, 필자는 그럴 필요가 없다고 생각하기에 깊이 파고 들지는 않고 diffuse를 잠깐 살펴보겠다.
난반사는 특정한 방향으로만 진행한다. 이때, 이것은 물체면에 부딪혀서 여러 방향으로 확산된다.
그래픽스에서의 난반사는 매우 거친 면으로 반사되는 완벽 확산체를 사용한다. 그렇기에 모든 방향으로 동일한 세기의 빛이 반사되므로 확산광의 세기는 시점의 위치와 무관하다.
그렇다면 확산광의 세기는 무엇으로 표현하느냐면, 바로 물체면 방향과 빛의 방향에 따른다.
광원으로부터의 빛과 물체변이 수직할 때(빛벡터와 물체면의 내적이 0일때), 확산광이 가장 세다.
내적한 값이 0보다 크거나 작으면, 빛의 세기가 감소한다. 이때의 빛과 면의 내적한 값을 더 작아지기에 확산광의 세기는 더 약해진다.
왜냐면 내적을 구하는 공식이 ( |v| x |v2| x cos(θ) )이다. θ가 90이라면 최대값(1)이 되고, 그보다 작으면 값이 점점 작아진다.
즉, 두 벡터가 이루는 각도의 값이 90일때에 힘이 가장 세다.
참고 문서1 : http://3dapi.com/bs24_height1/bs24_height1.pdf
참고 문서2 : 3D 게임 프로그래밍 (해골책)
정점 포맷 참고 문서 : http://telnet.or.kr/directx/graphics/programmingguide/gettingstarted/vertexformats/vformats.htm
나 설명 진짜 못하네...
LOD (Level Of Detail) (0) | 2014.08.04 |
---|---|
쿼드트리 컬링 (0) | 2014.08.04 |
Quadtree (0) | 2014.07.25 |
절두체 컬링 (0) | 2014.07.24 |
Directx9를 이용한 3D GAME 프로그래밍 입문 – part1 수학적 준비 (벡터) (0) | 2014.02.28 |
댓글,
Lowpoly
게임 서버 프로그래머 지망생