slide1
Download
Skip this Video
Download Presentation
제 13 장 기본적인 지형 렌더링

Loading in 2 Seconds...

play fullscreen
1 / 75

제 13 장 기본적인 지형 렌더링 - PowerPoint PPT Presentation


  • 327 Views
  • Uploaded on

제 13 장 기본적인 지형 렌더링. 기본적인 지형 렌더링. 지형 렌더링 지형 메쉬 : 삼각형 격자 격자 내 각 버텍스에 높이 부여하여 지형 표현 텍스처 추가하여 해변 , 언덕 , 눈 덮힌 산 등 표현 이 장의 내용 Terrain 클래스 구현 과정을 보여줌 전체 지형의 버텍스 / 인덱스 데이터를 보관하고 한 번에 렌더링 작은 지형의 경우에는 적용할 수 있으나 큰 지형에는 세부레벨조정 (Level of Detail) 이나 추려내기 기능 필요. 목표

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about '제 13 장 기본적인 지형 렌더링' - aysha


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide2
기본적인 지형 렌더링
  • 지형 렌더링
    • 지형 메쉬: 삼각형 격자
    • 격자 내 각 버텍스에 높이 부여하여 지형 표현
    • 텍스처 추가하여 해변, 언덕, 눈 덮힌 산 등 표현
  • 이 장의 내용
    • Terrain 클래스 구현 과정을 보여줌
      • 전체 지형의 버텍스/인덱스 데이터를 보관하고 한 번에 렌더링
      • 작은 지형의 경우에는 적용할 수 있으나 큰 지형에는 세부레벨조정(Level of Detail) 이나 추려내기 기능 필요

컴퓨터게임(DirectX)

slide3
목표
    • 산이나 계곡과 같은 자연스러운 지형을 표현하기 위한 지형의 높이 정보 생성 방법
    • 지형을 위한 버텍스와 삼각형 데이터 생성 방법
    • 지형에 조명과 텍스처 적용을 위한 테크닉
    • 지형 표면에 카메라를 이식, 지형 위를 걷거나 뛰는 효과 구현

컴퓨터게임(DirectX)

slide4
높 이 맵
  • 지형의 언덕이나 계곡을 표현하는 데 이용되는 것이 높이맵(heightmap)
  • 각각의 항목이 지형 격자 내의 특정 버텍스의 높이와 대응
  • 각 요소가 지형 격자 내의 각 버텍스와 일대일 대응관계를 가지는 행렬로 생각
  • 높이맵을 디스크에 보관할 때
    • 각 높이에 한 바이트의 메모리를 할당
    • 0 부터 255까지의 높이를 가짐
  • 높이맵의 그래픽 표현 중 하나로
    • 낮은 고도를 어두운 값으로, 높은 고도를 밝은 값으로 표현하는 그레이스케일 맵이 있다.

컴퓨터게임(DirectX)

slide5
높이맵 만들기
  • 높이맵은 절차적인 방법을 이용하거나 포토샵과 같은 이미지 편집기를 이용해 만들 수 있다.
  • 이미지 편집기로 높이맵에 이용할 이미지를 만들 때
    • 먼저 이미지 포맷으로 그레이 스케일을 지정
  • 높이맵 작성을 마친 뒤, 8비트 RAW파일로 저장
    • 경우에 따라서는 다른 파일 포맷으로 저장 가능

컴퓨터게임(DirectX)

slide8
RAW 파일 로딩하기
  • RAW파일은 일련의 연속된 바이트 블록으로 다음의 메서드를 이용해 손쉽게 블록을 읽기 가능

std::vector _heightmap;

bool Terrain::readRawFile(std::string fileName)

{

// 각 벡터를 위한 높이

std::vector in( _numVertices);

std::ifstream inFile(fileName.c_str(), std::ios_base::binary);

if(inFile == 0)

return false;

컴퓨터게임(DirectX)

slide9
inFile.read(

(char*) &in[0]; // 버퍼

in.size()); // 버퍼로 읽어들일 바이트 수

inFile.close();

// byte 벡터를 int 벡터로 복사

_heightmap.resize( _numVertices);

for(int i = 0; i < in.size(); i++) _heightmap[i] = in[i];

return true;

}

컴퓨터게임(DirectX)

slide10
코드에서 바이트 벡터를 정수 벡터로 복사하고 있다.
    • 이것은 이후에 높이값을 [0,255] 범위 밖으로 배율을 변경 할 수 있도록 하기 위한 것이다.
  • 이 메서드의 유일한 제한
    • RAW파일에 포함된 바이트 수만큼 지형 내의 버텍스가 준비되어 있어야 함

컴퓨터게임(DirectX)

slide11
Terrain 클래스는 높이맵 내의 항목에 접근하고 수정하기 위한 다음의 두 메서드를 제공

int Terrain::getHeightmapEntry(int row, int col)

{

return _heightmap[row*_numVertsPerRow+col];

}

void Terrain::setHeightmapEntry(int row, int col, int value)

{

_heightmap[row*_numVertsPerRow+col] = value;

}

컴퓨터게임(DirectX)

slide12
지형 기하정보 생성하기

Z

시작=(-w/2,d/2)

셀 간격

X

셀/ 사각형

d=깊이

끝=(w/2, -d/2)

W=너비

컴퓨터게임(DirectX)

slide13
지형의 크기를 정의하는 요소
    • 행당 버텍스 수
    • 열당 버텍스 수
    • 셀 간격
    • 지형과 연결될 장치
      • 높이맵 데이터를 포함하는 파일명 문자열
      • 높이맵 요소들의 배율을 조정하는데 이용될 높이 스케일 값
  • 예제에서 이 값들을 Terrain 클래스의 생성자에 전달

컴퓨터게임(DirectX)

slide14
class Terrain

{

public:

Terrain(

IDirect3DDevice9* device,

std::string heightmapFileName,

int numVertsPerRow,

int numVertsPerCol,

int cellSpacing, // 셀 간의 간격

float heightScale); // 높이 배율을 조정하는 값

….. // 메서드 생략

private:

… //장치/버텍스 버퍼 생략

컴퓨터게임(DirectX)

slide15
int _numVertsPerRow;

int _numVertsPerCol;

int _cellSpacing;

int _numCellsPerRow;

int _numCellsPerCol;

int _width;

int _depth;

int _numVertices;

int _numTriangles;

float _heightScale;

};

전체 코드는 예제 참고

컴퓨터게임(DirectX)

slide16
생성자에 전달된 값들을 이용해 다음과 같은 방법으로 다른 변수들의 값을 계산함.

_numCellsPerRow = _numVertsPerRow – 1;

_numCellsPerCol = _numVertsPerCol – 1;

_width = _numCellsPerCol * _cellSpacing;

_depth = _numCellsPerCol * _cellSpacing;

_numVertices = _numvertsPerRow * _numvertsPerCol;

_numTriangles = _numCellsPerRow * _numCellsPerCol * 2;

컴퓨터게임(DirectX)

slide17
우리가 이용할 지형의 버텍스 구조체

struct TerrainVertex{

TerrainVertex(){}

TerrainVertex(float x, float y, float z, float u, float v){

_x = x; _y = y; _z = z; _u = u; _v = v;

}

float _x, _y, _z;

float _u, _v;

static const DWORD FVF;

};

const DWORD Terrain::TerrainVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX1;

컴퓨터게임(DirectX)

slide18
TerrainVertex 는 Terrain 클래스 내부의 중첩된 클래스임
    • Terrain 클래스 외부에서는 이 클래스가 필요하지 않기 때문이다.

컴퓨터게임(DirectX)

slide19
버텍스 계산하기
  • 삼각형 격자의 버텍스들을 계산하기 위해서는
    • 시작점의 버텍스를 생성하기 시작,
    • 끝점에 이를 때까지 셀 간격만큼 버텍스의 간격을 비우고 버텍스들을 만들면 된다.
    • Y좌표는 읽어들인 높이맵 데이터 구조체 내의 대응되는 항목에서 간단하게 얻을 수 있다.

컴퓨터게임(DirectX)

slide20
지형 버텍스와 텍스춰 버텍스간의 대응
  • 텍스처 좌표 계산방법은 아래 그림을 참고
  • 이 그림은 버텍스(i,j)에 해당하는 텍스처 좌표(u,v)를 얻는 간단한 시나리오를 보여줌

(1,0)

(0,0)

+U

(0,1)

(1,1)

+V

컴퓨터게임(DirectX)

slide21
u = j · uCoordIncreamentSize
  • v = i · vCoordIncreamentSize

여기에서

  • uCoordIncreamentSize =
  • vCoordIncreamentSize =

1

numCellCols

1

numCellRows

컴퓨터게임(DirectX)

slide22
최종적으로 버텍스를 생성하는 코드

bool Terrain::computeVertices(){

HRESULT hr = 0;

hr = _device->CreateVertexBuffer(

_numVertices * sizeof(TerrainVertex),

D3DUSAGE_WRITEONLY,

TerrainVertex::FVF,

D3DPOOL_MANAGED,

&_vb, 0);

if(FAILED(hr))

return false;

int startX = -_width / 2; // 버텍스 생성을 시작할 좌표

int startZ = _depth / 2;

컴퓨터게임(DirectX)

slide23
int endX = _width / 2; // 버텍스 생성을 마칠 좌표

int endZ = -_depth / 2;

// 하나의 버텍스에서 다음 버텍스로 증가할

// 텍스처 좌표의 크기를 계산

float uCoordIncrementSize = 1.0f / (float)_numCellsPerRow;

float vCoordIncrementSize = 1.0f / (float)_numCellsPerCol;

TerrainVertex* v = 0;

_vb->Lock(0, 0, (void**)&v, 0);

int i = 0;

컴퓨터게임(DirectX)

slide24
for(int z = startZ; z >= endZ; z -= _cellSpacing)

{

int j = 0;

for(int x = startX; x <= endX; x += _cellSpacing)

{

// 중첩된 루프 내의 위치에 따라

// 버텍스 버퍼와 높이맵으로의 올바른

// 인덱스를 계산한다.

int index = i * _numVertsPerRow + j;

컴퓨터게임(DirectX)

slide25
v[index] = TerrainVertex( (float)x,

(float)_heightmap[index], (float)z,

(float)j * uCoordIncrementSize,

(float)i * vCoordIncrementSize);

j++; // next column

}

i++; // next row

}

_vb->Unlock();

return true;

}

컴퓨터게임(DirectX)

slide26
인덱스 계산 –삼각형 정의하기
  • 삼각형 격자의 인덱스를 계산하기 위해서는
    • 그림의 좌측 상단에서 시작
    • 우측 하단으로 각각의 사각형을 구성하는 두 개의 삼각형들을 차례대로 계산

버텍스열 j

(0,0)

버텍스열 j + 1

버텍스행 i

A B

C D

ij번째 셀

버텍스행 i+1

(m,n)

컴퓨터게임(DirectX)

slide27
사각형(i,j)를 찾기 위한 범용 공식

∆ ABC = {i·numVertsPerRow+j i·numVertsPerRow+j+1 (i+1) ·numVertsPerRow+J}

∆ CBD = {(i+1)·numVertsPerRow+j i·numVertsPerRow+j+1 (i+1) ·numVertsPerRow+j+1}

컴퓨터게임(DirectX)

slide28
인덱스를 계산하는 코드

Bool Terrain::computeIndices()

{

HRESULT hr = 0;

hr = _device->CreateIndexBuffer(

_numTriangles * 3 * sizeof(WORD), //삼각형당 3개의 인덱스

D3DUSAGE_WRITEONLY,

D3DFMT_INDEX16,

D3DPOOLMANAGED,

&_ib,

0);

컴퓨터게임(DirectX)

slide29
If(FAILED(hr))

return false;

WORD* indices = 0;

_ib->Lock(0, 0, (void**)&indices, 0);

// 하나의 사각형을 구성하는 두 개의 삼각형을 나타내기 위한

// 6개의 인덱스 그룹의 시작점

int baseIndex = 0;

// 각 사각형의 삼각형을 계산하는 루프

for(int i = 0; i < _numCellsPerCol; i++)

{

컴퓨터게임(DirectX)

slide30
for(int j = 0; j < _numCellsPerRow; j++)

{

indices[baseIndex] = i * _numVertsPerRow + j;

indices[baseIndex + 1] = i * _numVertsPerRow + j + 1;

indices[baseIndex + 2] = (i+1) * _numVertsPerRow + j;

indices[baseIndex + 3] = (i+1) * _numVertsPerRow + j;

indices[baseIndex + 4] = i * _numVertsPerRow + j + 1;

indices[baseIndex + 5] = (i+1) * _numVertsPerRow + j + 1;

baseIndex += 6;

}

}

_ib->Unlock();

return true;

}

컴퓨터게임(DirectX)

slide31
텍 스 처 링
  • Terrain 클래스는 지형에 텍스처를 입히는 두 가지 방법을 제공

1) 미리 만들어둔 텍스처 파일을 읽어들이고 이를 이용하는 방법

      • IDirect3DTexture9 인터페이스로의 포인터인 _tex데이터 멤버로 텍스처 파일을 읽어들임
      • bool loadTexture(std::string fileName);

컴퓨터게임(DirectX)

slide32
bool loadTexture(std::string fileName);

bool Terrain::loadTexture(std::string fileName)

{

HRESULT hr = 0;

hr = D3DXCreateTextureFromFile(

_device,

fileName.c_str(),

&_tex);

if(FAILED(hr))

return false;

return true;

}

컴퓨터게임(DirectX)

slide33
2) 절차적 방식
  • 지형에 텍스처를 입히는 두 번째 방법
    • 절차적으로 텍스처를 계산해 내는 것
  • 먼저 “빈” 텍스처를 만들고 미리 정의된 인자를 바탕으로 코드에서 각 텍셀의 컬러를 계산
    • 예제에서는 지형의 높이를 인자로 이용

컴퓨터게임(DirectX)

slide34
Terrain:genTexture 메서드를 이용한 절차적 텍스처
    • D3DXCreateTexture를 이용해 빈 텍스처를 만들고
    • 최상의 레벨(밉맵 레벨)을 잠근 다음, 각각의 텍셀을 대상으로 반복하여 컬러를 입힘
    • 대응되는 사각형의 높이에 따라 텍셀의 컬러를 결정하여 각 텍셀을 대상으로 반복하여 컬러를 입힘
    • 각각의 텍셀에 색상을 입힌 뒤에는 텍셀을 비추는 태양 빛의 각도에 따라 텍셀의 밝기를 조정
      • Terrain::lightTerrain 메서드에서 이루어짐
    • 하위 밉맵 레벨의 텍셀을 계산하는 것으로 끝을 맺음
      • D3DXFilterTexture 함수가 담당

컴퓨터게임(DirectX)

slide35
텍스처를 만들어내는 코드

bool Terrain::genTexture(D3DXVECTOR3* directionToLight)

{

HRESULT hr = 0;

// 각각의 사각형 셀을 위한 텍셀

int texWidth = _numCellsPerRow;

int texHeight = _numCellsPerCol;

// 빈 텍스처를 만든다.

hr = D3DXCreateTexture(

_device,

texWidth, texHeight,

0, // create a complete mipmap chain

0, // usage

D3DFMT_X8R8G8B8,// 32 bit XRGB format

D3DPOOL_MANAGED, &_tex);

컴퓨터게임(DirectX)

slide36
if(FAILED(hr))

return false;

D3DSURFACE_DESC textureDesc;

_tex->GetLevelDesc(0 /*level*/, &textureDesc);

//직접 32비트 픽셀을 이용해 텍스처를 채우므로

//올바른 포맷인지를 먼저 확인

if( textureDesc.Format != D3DFMT_X8R8G8B8 )

return false;

D3DLOCKED_RECT lockedRect;

_tex->LockRect(0/*lock top surface*/, &lockedRect,

0 /* lock entire tex*/, 0/*flags*/);

// 텍스처를 채운다.

DWORD* imageData = (DWORD*)lockedRect.pBits;

컴퓨터게임(DirectX)

slide37
for(int i = 0; i < texHeight; i++) {

for(int j = 0; j < texWidth; j++) {

D3DXCOLOR c;

// 사각형의 우측 상단 높이를 구한다.

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;

컴퓨터게임(DirectX)

slide38
imageData[i * lockedRect.Pitch / 4 + j] = (D3DCOLOR)c;

}

}

_tex->UnlockRect(0);

// 지형을 밝힌다.

if(!lightTerrain(directionToLight)) {

::MessageBox(0, "lightTerrain() - FAILED", 0, 0);

return false;

}

hr = D3DXFilterTexture(

_tex, // 밉맵 레벨을 채울 텍스처

0, // 디폴트 팔레트

0, // 하위 레벨의 원본으로 최상위 레벨을 이용

D3DX_DEFAULT); // 디폴트 필터

컴퓨터게임(DirectX)

slide39
if(FAILED(hr)) {

::MessageBox(0, "D3DXFilterTexture() - FAILED", 0, 0);

return false;

}

return true;

}

컴퓨터게임(DirectX)

slide40
조 명
  • Terrain::genTexture 메서드는 지형에 조명을 추가, 사실감을 더하는 Terrain::lightTerrain 메서드 호출
  • Direct3D에게 지형의 음영만 계산하는 것을 맡기지 않고 지형의 조명을 계산하는 이유
    • 버텍스 법선을 저장하지 않아도 되므로 메모리 절약
    • 지형은 정적이며 조명을 움직이지도 않을 것이므로 조명을 미리 계산하여 지형에 실시간으로 조명을 적용하는데 따르는 처리 부담을 덜 수 있다.
    • 약간의 수학 연습을 할 수 있으며 기본적인 조명의 개념과 Direct3D 함수들에 익숙해짐

컴퓨터게임(DirectX)

slide41
조명의 개요
  • 우리가 지형의 음영을 계산하는데 이용하는 조명 테크닉은 가장 기본적인 것
    • 난반사광(Diffuse lighting)
  • 빛으로의 방향을 지정하여 하나의 평행한 광원을 정의
    • 이 방향은 평행 광원에서의 빛이 발산되는 방향과는 반대의 방향
      • 예를 들어 lightRaysDirection=(0, -1, 0) 방향으로 하늘에서 아래로 빛이 발산되기를 원한다면, 평행 광원으로의 방향은 반대방향인 directionToLight=(0, 1, 0) 이 된다.

컴퓨터게임(DirectX)

slide42
다음은 지형 내의 각각의 사각형에 대해 빛 벡터 L과 사각형 표면 법선 N간의 각도 계산
    • 각도가 커지면 사각형 면이 점차 광원에서 멀어져 빛을 덜 받음
    • 작아지면 사각형 면이 점차 광원으로 향하며 더욱 많은 빛을 받음
    • 90도가 넘으면 표면은 더 이상 빛을 못 받음

L

N

L

N

컴퓨터게임(DirectX)

slide43
빛 벡터와 표면 법선과의 각도 관계를 이용해 표면이 받는 빛의 양을 결정하는 [0, 1] 범위의 음영 스칼라를 만들 수 있다.
    • 큰 각도는 0에 가까운 스칼라로 표현되며 음영 스칼라와 컬러를 곱하면 0에 가까운 값으로 어두운 컬러가 된다.
    • 작은 각도는 1에 가까운 스칼라로 표현되며 컬러와 곱하면 1에 가까운 밝은 값이 만들어진다.

컴퓨터게임(DirectX)

slide44
사각형의 음영 계산하기
  • 광원으로의 방향은 우리가 L 이라고 부르는 정규화된 벡터로 얻을 수 있다.
  • L 과 사각형의 법선 벡터 N 간의 각도를 계산하기 위해서는 먼저 N을 찾아야 함
  • 먼저 사각형과 공면인 0이 아니면서 평행이 아닌 두개의 벡터를 찾아야 함

컴퓨터게임(DirectX)

slide45
다음은 이 두 개의 벡터 u와 v를 보여줌

a

Y

u = b - a

b

v = c - a

Z

c

X

컴퓨터게임(DirectX)

slide46
u = (cellSpacing, by– ay, 0)
    • v = (0, cy– ay, -cellSpacing)
  • u와 v가 있다면 사각형의 법선 N은 n = u x v로 얻을 수 있다.
    • N은 정규화 되어야 한다.
    • N =
  • L과 N간의 각도를 찾기 위해서는 3-공간 내 두 단위 벡터의 내적이 두 벡터 간 각도의 코사인 이다.

N

|N|

컴퓨터게임(DirectX)

slide47
L · N = s
  • 스칼라 s는 [-1, 1] 범위 내에 위치한다.
  • [-1, 0) 내의 s값은 [그림 13.7]에서 빛을 받지 못하는 90도 이상의 L과 N간의 각도에 대응하므로, [-1,0) 내일 경우 s를 0으로 고정

float cosine = D3DXVec3Dot(&n, directionToLight);

If(cosine < 0.0f) cosine = 0.0f;

컴퓨터게임(DirectX)

slide48
이제 90도 이상의 각도에 대해 s를 고정하면 s는 [0,1] 범위의 스칼라가 된다.
  • 0도에서 90도까지의 각도가 1에서 0값에 대응됨
  • 각각의 사각형을 위한 음영 인수는 Terrrain::computeShade 에서 계산한다.
    • 이 메서드는 사각형을 지정하는 열과 행, 그리고 평행 광원의 방향을 인자로 받는다.

컴퓨터게임(DirectX)

slide49
float Terrain::computeShade(int cellRow, int cellCol, D3DXVECTOR3* directionToLight)

{

// 사각형의 새 버텍스 높이를 얻는다.

float heightA = getHeightmapEntry(cellRow, cellCol);

float heightB = getHeightmapEntry(cellRow, cellCol+1);

float heightC = getHeightmapEntry(cellRow+1, cellCol);

// 사각형의 두 벡터 외적을 이용해

// 법선을 찾아낸다.

D3DXVECTOR3 u(_cellSpacing, heightB - heightA, 0.0f);

D3DXVECTOR3 v(0.0f, heightC - heightA, -_cellSpacing);

컴퓨터게임(DirectX)

slide50
D3DXVECTOR3 n;

D3DXVec3Cross(&n, &u, &v);

D3DXVec3Normalize(&n, &n);

float cosine = D3DXVec3Dot(&n, directionToLight);

if(cosine < 0.0f)

cosine = 0.0f;

return cosine;

}

컴퓨터게임(DirectX)

slide51
지형에 음영 입히기
  • 특정한 사각형의 음영을 계산할 수 있게 되었다면 지형의 모든 사각형을 계산하는 일은 매우 간단
    • 각각 사각형을 대상으로 반복, 사각형의 음영값을 계산
    • 사각형에 대응하는 텍셀 컬러에 이 음영을 적용
    • 이 작업을 통해 빛을 적게 받는 사각형이 좀더 어두어짐

컴퓨터게임(DirectX)

slide52
Terrain::lightTerrain 메서드의 가장 중요한 부분

DWORD* imageData = (DWORD*)lockedRect.pBits;

for(int i = 0; i < textureDesc.Height; i++){

for(int j = 0; j < textureDesc.Width; j++){

int index = i * lockedRect.Pitch / 4 + j;

// 셀의 현재 컬러를 얻는다.

D3DXCOLOR c( imageData[index] );

// 셀에 음영을 적용한다.

c *= computeShade(i, j, directionToLight);;

// 음영을 입힌 컬러를 저장한다.

imageData[index] = (D3DCOLOR)c;

}

}

컴퓨터게임(DirectX)

slide53
지형 위를 “걷기”
  • 지형을 구성한 다음에는 마치 지형 위를 걷는 것과 같은 카메라 움직임을 구현할 필요가 있다.
  • 이를 위해서는 현재 서있는 지점의 높이에 따라 카메라의 높이를 조정해야 한다.
  • 가장 먼저 해야 할 일은 카메라 위치의 x와 z좌표를 이용해 현재의 셀을 찾아내는 것
    • 이 작업은 Terrain::getHeight 함수가 담당
      • 이 함수는 카메라의 x와 z좌표를 인자로 받고 카메라의 높이로 지정할 수 있는 높이를 리턴

컴퓨터게임(DirectX)

slide54
float Terrain::getHeight(float x, float z){

// 지형의 시작점을 원점으로 이동하는 변환, xz 평면상에서 이동

x = ((float)_width / 2.0f) + x;

z = ((float)_depth / 2.0f) - z;

//셀 간격을 1로 만드는 변환, 변환의 배율을 낮춘다.

x /= (float)_cellSpacing;

z /= (float)_cellSpacing;

컴퓨터게임(DirectX)

slide55
일단 지형의 시작점을 원점으로 이동하는 변환을 계산하고 수행
  • 다음은 셀 간격 변수의 반전으로 배율을 변경
    • 이 과정을 거치면 셀 간격은 1이 된다.
  • 이어 양의 z축이 “아래”를 가리키도록 새로운 틀을 전환
    • +z 가 아래를 향하는 것으로 인식

컴퓨터게임(DirectX)

slide56
P

P

  • 다음은 이 과정을 그림으로 보여주고 있다.

Z

시작

0 1 2 3

X

0 1 2 3

X

셀간격

1.0

Z

컴퓨터게임(DirectX)

slide57
변경된 좌표 시스템은 행렬의 순서와 동일
    • 즉 좌측 상단이 원점, 열의 수가 오른쪽으로 증가
    • 행의 수는 아래쪽으로 증가.
  • 다음과 같은 코드로 간단한 셀의 행과 열을 알아냄
    • float col = ::floorf(x);
    • float row = ::floorf(z);

X 이하의 가장 큰 정수

컴퓨터게임(DirectX)

slide58
이젠 현재 위치의 셀을 찾을 수 있으므로 셀을 구성하는 네 버텍스의 높이를 구성할 수 있다.

float A = getHeightmapEntry(row, col);

float B = getHeightmapEntry(row, col+1);

float C = getHeightmapEntry(row+1, col);

float D = getHeightmapEntry(row+1, col+1);

  • 이젠 셀 내의 카메라가 위치한 특정 x, z좌표의 높이(y-좌표)를 얻는 과정이 남아있다.

컴퓨터게임(DirectX)

slide59
Y

v1

y

v2

Z

(x,z)

v0

v3

X

컴퓨터게임(DirectX)

slide60
높이를 구하기 위해서는 우리가 셀의 어떤 삼각형에 있는지를 먼저 알아야 한다.
  • 우리가 있는 삼각형을 찾기 위해서는
    • 왼쪽 상단의 버텍스가 원점에 오도록 현재의 셀을 이동
  • col과 row는 현재 우리가 있는 셀의 왼쪽 상단 버텍스 위치를 나타냄
    • x축의 -col이 되고 z축의 -row가 되도록 이동해야 함

컴퓨터게임(DirectX)

slide61
x와 z좌표를 이동하면 다음과 같은 결과가 나옴
    • float dx = x - col;
    • float dz = z - row;
  • 다음은 이동한 뒤의 셀 상태를 보여줌

0 1 2 3

(0,0)

0 1 2 3

우리가 있는 셀

P

P

(1,1)

버텍스(2,1)

1.0

컴퓨터게임(DirectX)

slide62
이제 dz<1.0 - dx이면
    • 우리는 “위쪽”삼각형 v0v1v2에 있는것
    • 아니라면 “아래쪽” 삼각형 v0v2v3에 있는 것

컴퓨터게임(DirectX)

slide63
“위쪽” 삼각형에서의 높이 계산 방법
  • 위쪽 삼각형에서의 높이를 구하기 위해서는
    • 먼저 삼각형의 옆면과 벡터 q = (qx, A, qz)의 종단점에서 시작하는 두 개의 벡터
    • u = (cellSpacing, B - A, 0)와
    • v = (0, C - A, -cellSpacing)을 구성해야 함
  • 이어 dx로 u를 따라 선형적으로 보간하고 dz로 v를 선형적으로 보간한다.

컴퓨터게임(DirectX)

13 12
q

q

u

u

v

v

보간 과정 (그림13.12)

A) 삼각형의 인접하고 상반된 면의 두 벡터를 계산

B) u를 dx로 선형적 보간하고 v를 dz로 선형적으로 보간하면 높이가 구해짐 => (q + dx u + dz v) 의 y 좌표

Y

Y

Z

dxu

Z

dzv

y

(x,z)

(x,z)

X

X

컴퓨터게임(DirectX)

terrain getheight
Terrain::getHeight를 완결하는 코드

if(dz < 1.0f - dx) // 위쪽 삼각형 ABC

{

float uy = B - A; // A->B

float vy = C - A; // A->C

height = A + d3d::Lerp(0.0f, uy, dx) + d3d::Lerp(0.0f, vy, dz);

}

else // 아래쪽 삼각형 DCB

{

float uy = C - D; // D->C

float vy = B - D; // D->B

height = D + d3d::Lerp(0.0f, uy, 1.0f - dx) + d3d::Lerp(0.0f, vy, 1.0f - dz);

}

return height;

}

컴퓨터게임(DirectX)

slide67
Lerp 함수는 1D 선을 따르는 기본 선형 보간이며 다음과 같이 구현됨

float d3d::Lerp(float a, float b, float t)

{

return a – (a * t ) + ( b * t);

}

컴퓨터게임(DirectX)

slide68
예제 애플리케이션 : 지형

컴퓨터게임(DirectX)

slide69
높이맵 데이터를 포함하는 RAW 파일을 이용해 지형을 생성하고 텍스처를 입히며 조명을 추가
  • 화살표 키를 이용해 지형 위를 걷는 동작
  • 먼저 지형과 카메라 초당 프레임 수를 기록하기 위한 전역 변수 추가.

Terrain* TheTerrain = 0;

Camera TheCamera(Camera::LANDOBJECT);

FPSCounter* FPS = 0;

컴퓨터게임(DirectX)

slide70
bool Setup()

{

D3DXVECTOR3 lightDirection(0.0f, 1.0f, 0.0f);

TheTerrain = new Terrain(Device, "coastMountain64.raw", 64, 64, 10, 0.5f);

TheTerrain->genTexture(&lightDirection);

FPS = new FPSCounter(Device);

Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

컴퓨터게임(DirectX)

slide71
D3DXMATRIX proj;

D3DXMatrixPerspectiveFovLH(

&proj,

D3DX_PI * 0.25f, // 45 - degree

(float)Width / (float)Height,

1.0f,

1000.0f);

Device->SetTransform(D3DTS_PROJECTION, &proj);

return true;

}

void Cleanup()

{

d3d::Delete(TheTerrain);

d3d::Delete(FPS);

}

컴퓨터게임(DirectX)

slide72
bool Display(float timeDelta)

{

if( Device )

{

if( ::GetAsyncKeyState(VK_UP) & 0x8000f )

TheCamera.walk(100.0f * timeDelta);

if( ::GetAsyncKeyState(VK_DOWN) & 0x8000f )

TheCamera.walk(-100.0f * timeDelta);

if( ::GetAsyncKeyState(VK_LEFT) & 0x8000f )

TheCamera.yaw(-1.0f * timeDelta);

if( ::GetAsyncKeyState(VK_RIGHT) & 0x8000f )

TheCamera.yaw(1.0f * timeDelta);

컴퓨터게임(DirectX)

slide73
if( ::GetAsyncKeyState('N') & 0x8000f )

TheCamera.strafe(-100.0f * timeDelta);

if( ::GetAsyncKeyState('M') & 0x8000f )

TheCamera.strafe(100.0f * timeDelta);

if( ::GetAsyncKeyState('W') & 0x8000f )

TheCamera.pitch(1.0f * timeDelta);

if( ::GetAsyncKeyState('S') & 0x8000f )

TheCamera.pitch(-1.0f * timeDelta);

컴퓨터게임(DirectX)

slide74
D3DXVECTOR3 pos;

TheCamera.getPosition(&pos);

float height = TheTerrain->getHeight( pos.x, pos.z );

pos.y = height + 5.0f; TheCamera.setPosition(&pos);

D3DXMATRIX V;

TheCamera.getViewMatrix(&V);

Device->SetTransform(D3DTS_VIEW, &V);

Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff000000, 1.0f, 0);

Device->BeginScene();

컴퓨터게임(DirectX)

slide75
D3DXMATRIX I;

D3DXMatrixIdentity(&I);

if( TheTerrain )

TheTerrain->draw(&I, false);

if( FPS )

FPS->render(0xffffffff, timeDelta);

Device->EndScene();

Device->Present(0, 0, 0, 0);

}

return true;

}

컴퓨터게임(DirectX)

ad