제
Download
1 / 75

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


  • 324 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

13장 기본적인 지형 렌더링

컴퓨터게임(DirectX)


기본적인 지형 렌더링

  • 지형 렌더링

    • 지형 메쉬: 삼각형 격자

    • 격자 내 각 버텍스에 높이 부여하여 지형 표현

    • 텍스처 추가하여 해변, 언덕, 눈 덮힌 산 등 표현

  • 이 장의 내용

    • Terrain 클래스 구현 과정을 보여줌

      • 전체 지형의 버텍스/인덱스 데이터를 보관하고 한 번에 렌더링

      • 작은 지형의 경우에는 적용할 수 있으나 큰 지형에는 세부레벨조정(Level of Detail) 이나 추려내기 기능 필요

컴퓨터게임(DirectX)


  • 목표

    • 산이나 계곡과 같은 자연스러운 지형을 표현하기 위한 지형의 높이 정보 생성 방법

    • 지형을 위한 버텍스와 삼각형 데이터 생성 방법

    • 지형에 조명과 텍스처 적용을 위한 테크닉

    • 지형 표면에 카메라를 이식, 지형 위를 걷거나 뛰는 효과 구현

컴퓨터게임(DirectX)


높 이 맵

  • 지형의 언덕이나 계곡을 표현하는 데 이용되는 것이 높이맵(heightmap)

  • 각각의 항목이 지형 격자 내의 특정 버텍스의 높이와 대응

  • 각 요소가 지형 격자 내의 각 버텍스와 일대일 대응관계를 가지는 행렬로 생각

  • 높이맵을 디스크에 보관할 때

    • 각 높이에 한 바이트의 메모리를 할당

    • 0 부터 255까지의 높이를 가짐

  • 높이맵의 그래픽 표현 중 하나로

    • 낮은 고도를 어두운 값으로, 높은 고도를 밝은 값으로 표현하는 그레이스케일 맵이 있다.

컴퓨터게임(DirectX)


높이맵 만들기

  • 높이맵은 절차적인 방법을 이용하거나 포토샵과 같은 이미지 편집기를 이용해 만들 수 있다.

  • 이미지 편집기로 높이맵에 이용할 이미지를 만들 때

    • 먼저 이미지 포맷으로 그레이 스케일을 지정

  • 높이맵 작성을 마친 뒤, 8비트 RAW파일로 저장

    • 경우에 따라서는 다른 파일 포맷으로 저장 가능

컴퓨터게임(DirectX)


(그림13.3) 포토샵에서 만든 그레이스케일 이미지

컴퓨터게임(DirectX)



RAW 파일 로딩하기

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

    std::vector<int> _heightmap;

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

    {

    // 각 벡터를 위한 높이

    std::vector<BYTE> in( _numVertices);

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

    if(inFile == 0)

    return false;

컴퓨터게임(DirectX)


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)


  • 코드에서 바이트 벡터를 정수 벡터로 복사하고 있다.

    • 이것은 이후에 높이값을 [0,255] 범위 밖으로 배율을 변경 할 수 있도록 하기 위한 것이다.

  • 이 메서드의 유일한 제한

    • RAW파일에 포함된 바이트 수만큼 지형 내의 버텍스가 준비되어 있어야 함

컴퓨터게임(DirectX)


  • 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)


지형 기하정보 생성하기 복사하고 있다

Z

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

셀 간격

X

셀/ 사각형

d=깊이

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

W=너비

컴퓨터게임(DirectX)


  • 지형의 크기를 정의하는 요소 복사하고 있다

    • 행당 버텍스 수

    • 열당 버텍스 수

    • 셀 간격

    • 지형과 연결될 장치

      • 높이맵 데이터를 포함하는 파일명 문자열

      • 높이맵 요소들의 배율을 조정하는데 이용될 높이 스케일 값

  • 예제에서 이 값들을 Terrain 클래스의 생성자에 전달

컴퓨터게임(DirectX)


class Terrain 복사하고 있다

{

public:

Terrain(

IDirect3DDevice9* device,

std::string heightmapFileName,

int numVertsPerRow,

int numVertsPerCol,

int cellSpacing, // 셀 간의 간격

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

….. // 메서드 생략

private:

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

컴퓨터게임(DirectX)


int _numVertsPerRow; 복사하고 있다

int _numVertsPerCol;

int _cellSpacing;

int _numCellsPerRow;

int _numCellsPerCol;

int _width;

int _depth;

int _numVertices;

int _numTriangles;

float _heightScale;

};

전체 코드는 예제 참고

컴퓨터게임(DirectX)


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

    _numCellsPerRow = _numVertsPerRow – 1;

    _numCellsPerCol = _numVertsPerCol – 1;

    _width = _numCellsPerCol * _cellSpacing;

    _depth = _numCellsPerCol * _cellSpacing;

    _numVertices = _numvertsPerRow * _numvertsPerCol;

    _numTriangles = _numCellsPerRow * _numCellsPerCol * 2;

컴퓨터게임(DirectX)


  • 우리가 이용할 지형의 버텍스 구조체 방법으로 다른 변수들의 값을 계산함

    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)


  • TerrainVertex 방법으로 다른 변수들의 값을 계산함는 Terrain 클래스 내부의 중첩된 클래스임

    • Terrain 클래스 외부에서는 이 클래스가 필요하지 않기 때문이다.

컴퓨터게임(DirectX)


버텍스 계산하기 방법으로 다른 변수들의 값을 계산함

  • 삼각형 격자의 버텍스들을 계산하기 위해서는

    • 시작점의 버텍스를 생성하기 시작,

    • 끝점에 이를 때까지 셀 간격만큼 버텍스의 간격을 비우고 버텍스들을 만들면 된다.

    • Y좌표는 읽어들인 높이맵 데이터 구조체 내의 대응되는 항목에서 간단하게 얻을 수 있다.

컴퓨터게임(DirectX)


지형 버텍스와 텍스춰 버텍스간의 대응 방법으로 다른 변수들의 값을 계산함

  • 텍스처 좌표 계산방법은 아래 그림을 참고

  • 이 그림은 버텍스(i,j)에 해당하는 텍스처 좌표(u,v)를 얻는 간단한 시나리오를 보여줌

(1,0)

(0,0)

+U

(0,1)

(1,1)

+V

컴퓨터게임(DirectX)


  • u = j · uCoordIncreamentSize 방법으로 다른 변수들의 값을 계산함

  • v = i · vCoordIncreamentSize

    여기에서

  • uCoordIncreamentSize =

  • vCoordIncreamentSize =

1

numCellCols

1

numCellRows

컴퓨터게임(DirectX)


  • 최종적으로 버텍스를 생성하는 코드 방법으로 다른 변수들의 값을 계산함

    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)


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)


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)


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)


인덱스 계산 방법으로 다른 변수들의 값을 계산함–삼각형 정의하기

  • 삼각형 격자의 인덱스를 계산하기 위해서는

    • 그림의 좌측 상단에서 시작

    • 우측 하단으로 각각의 사각형을 구성하는 두 개의 삼각형들을 차례대로 계산

버텍스열 j

(0,0)

버텍스열 j + 1

버텍스행 i

A B

C D

ij번째 셀

버텍스행 i+1

(m,n)

컴퓨터게임(DirectX)


  • 사각형 방법으로 다른 변수들의 값을 계산함(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)


인덱스를 계산하는 코드 방법으로 다른 변수들의 값을 계산함

Bool Terrain::computeIndices()

{

HRESULT hr = 0;

hr = _device->CreateIndexBuffer(

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

D3DUSAGE_WRITEONLY,

D3DFMT_INDEX16,

D3DPOOLMANAGED,

&_ib,

0);

컴퓨터게임(DirectX)


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)


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)


텍 스 처 링 방법으로 다른 변수들의 값을 계산함

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

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

    • IDirect3DTexture9 인터페이스로의 포인터인 _tex데이터 멤버로 텍스처 파일을 읽어들임

    • bool loadTexture(std::string fileName);

컴퓨터게임(DirectX)


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)


2) 방법으로 다른 변수들의 값을 계산함절차적 방식

  • 지형에 텍스처를 입히는 두 번째 방법

    • 절차적으로 텍스처를 계산해 내는 것

  • 먼저 “빈” 텍스처를 만들고 미리 정의된 인자를 바탕으로 코드에서 각 텍셀의 컬러를 계산

    • 예제에서는 지형의 높이를 인자로 이용

컴퓨터게임(DirectX)


  • Terrain:genTexture 방법으로 다른 변수들의 값을 계산함메서드를 이용한 절차적 텍스처

    • D3DXCreateTexture를 이용해 빈 텍스처를 만들고

    • 최상의 레벨(밉맵 레벨)을 잠근 다음, 각각의 텍셀을 대상으로 반복하여 컬러를 입힘

    • 대응되는 사각형의 높이에 따라 텍셀의 컬러를 결정하여 각 텍셀을 대상으로 반복하여 컬러를 입힘

    • 각각의 텍셀에 색상을 입힌 뒤에는 텍셀을 비추는 태양 빛의 각도에 따라 텍셀의 밝기를 조정

      • Terrain::lightTerrain 메서드에서 이루어짐

    • 하위 밉맵 레벨의 텍셀을 계산하는 것으로 끝을 맺음

      • D3DXFilterTexture 함수가 담당

컴퓨터게임(DirectX)


텍스처를 만들어내는 코드 방법으로 다른 변수들의 값을 계산함

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)


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)


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)


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)


if(FAILED(hr)) { 방법으로 다른 변수들의 값을 계산함

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

return false;

}

return true;

}

컴퓨터게임(DirectX)


조 명 방법으로 다른 변수들의 값을 계산함

  • Terrain::genTexture 메서드는 지형에 조명을 추가, 사실감을 더하는 Terrain::lightTerrain 메서드 호출

  • Direct3D에게 지형의 음영만 계산하는 것을 맡기지 않고 지형의 조명을 계산하는 이유

    • 버텍스 법선을 저장하지 않아도 되므로 메모리 절약

    • 지형은 정적이며 조명을 움직이지도 않을 것이므로 조명을 미리 계산하여 지형에 실시간으로 조명을 적용하는데 따르는 처리 부담을 덜 수 있다.

    • 약간의 수학 연습을 할 수 있으며 기본적인 조명의 개념과 Direct3D 함수들에 익숙해짐

컴퓨터게임(DirectX)


조명의 개요 방법으로 다른 변수들의 값을 계산함

  • 우리가 지형의 음영을 계산하는데 이용하는 조명 테크닉은 가장 기본적인 것

    • 난반사광(Diffuse lighting)

  • 빛으로의 방향을 지정하여 하나의 평행한 광원을 정의

    • 이 방향은 평행 광원에서의 빛이 발산되는 방향과는 반대의 방향

      • 예를 들어 lightRaysDirection=(0, -1, 0) 방향으로 하늘에서 아래로 빛이 발산되기를 원한다면, 평행 광원으로의 방향은 반대방향인 directionToLight=(0, 1, 0) 이 된다.

컴퓨터게임(DirectX)


  • 다음은 지형 내의 각각의 사각형에 대해 빛 벡터 L과 사각형 표면 법선 N간의 각도 계산

    • 각도가 커지면 사각형 면이 점차 광원에서 멀어져 빛을 덜 받음

    • 작아지면 사각형 면이 점차 광원으로 향하며 더욱 많은 빛을 받음

    • 90도가 넘으면 표면은 더 이상 빛을 못 받음

L

N

L

N

컴퓨터게임(DirectX)


  • 빛 벡터와 표면 법선과의 각도 관계를 이용해 표면이 받는 빛의 양을 결정하는 [0, 1] 범위의 음영 스칼라를 만들 수 있다.

    • 큰 각도는 0에 가까운 스칼라로 표현되며 음영 스칼라와 컬러를 곱하면 0에 가까운 값으로 어두운 컬러가 된다.

    • 작은 각도는 1에 가까운 스칼라로 표현되며 컬러와 곱하면 1에 가까운 밝은 값이 만들어진다.

컴퓨터게임(DirectX)


사각형의 음영 계산하기 표면이 받는 빛의 양을 결정하는

  • 광원으로의 방향은 우리가 L 이라고 부르는 정규화된 벡터로 얻을 수 있다.

  • L 과 사각형의 법선 벡터 N 간의 각도를 계산하기 위해서는 먼저 N을 찾아야 함

  • 먼저 사각형과 공면인 0이 아니면서 평행이 아닌 두개의 벡터를 찾아야 함

컴퓨터게임(DirectX)


a

Y

u = b - a

b

v = c - a

Z

c

X

컴퓨터게임(DirectX)


  • u = (cellSpacing, b 표면이 받는 빛의 양을 결정하는 y– ay, 0)

  • v = (0, cy– ay, -cellSpacing)

  • u와 v가 있다면 사각형의 법선 N은 n = u x v로 얻을 수 있다.

    • N은 정규화 되어야 한다.

    • N =

  • L과 N간의 각도를 찾기 위해서는 3-공간 내 두 단위 벡터의 내적이 두 벡터 간 각도의 코사인 이다.

  • N

    |N|

    컴퓨터게임(DirectX)


    • 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)


    • 이제 표면이 받는 빛의 양을 결정하는 90도 이상의 각도에 대해 s를 고정하면 s는 [0,1] 범위의 스칼라가 된다.

    • 0도에서 90도까지의 각도가 1에서 0값에 대응됨

    • 각각의 사각형을 위한 음영 인수는 Terrrain::computeShade 에서 계산한다.

      • 이 메서드는 사각형을 지정하는 열과 행, 그리고 평행 광원의 방향을 인자로 받는다.

    컴퓨터게임(DirectX)


    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)


    D3DXVECTOR3 n; D3DXVECTOR3* directionToLight)

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

    D3DXVec3Normalize(&n, &n);

    float cosine = D3DXVec3Dot(&n, directionToLight);

    if(cosine < 0.0f)

    cosine = 0.0f;

    return cosine;

    }

    컴퓨터게임(DirectX)


    지형에 음영 입히기 D3DXVECTOR3* directionToLight)

    • 특정한 사각형의 음영을 계산할 수 있게 되었다면 지형의 모든 사각형을 계산하는 일은 매우 간단

      • 각각 사각형을 대상으로 반복, 사각형의 음영값을 계산

      • 사각형에 대응하는 텍셀 컬러에 이 음영을 적용

      • 이 작업을 통해 빛을 적게 받는 사각형이 좀더 어두어짐

    컴퓨터게임(DirectX)


    • Terrain::lightTerrain D3DXVECTOR3* directionToLight)메서드의 가장 중요한 부분

      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)


    지형 위를 D3DXVECTOR3* directionToLight)“걷기”

    • 지형을 구성한 다음에는 마치 지형 위를 걷는 것과 같은 카메라 움직임을 구현할 필요가 있다.

    • 이를 위해서는 현재 서있는 지점의 높이에 따라 카메라의 높이를 조정해야 한다.

    • 가장 먼저 해야 할 일은 카메라 위치의 x와 z좌표를 이용해 현재의 셀을 찾아내는 것

      • 이 작업은 Terrain::getHeight 함수가 담당

        • 이 함수는 카메라의 x와 z좌표를 인자로 받고 카메라의 높이로 지정할 수 있는 높이를 리턴

    컴퓨터게임(DirectX)


    float Terrain::getHeight(float x, float z){ D3DXVECTOR3* directionToLight)

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

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

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

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

    x /= (float)_cellSpacing;

    z /= (float)_cellSpacing;

    컴퓨터게임(DirectX)


    • 일단 지형의 시작점을 원점으로 이동하는 변환을 계산하고 수행

    • 다음은 셀 간격 변수의 반전으로 배율을 변경

      • 이 과정을 거치면 셀 간격은 1이 된다.

    • 이어 양의 z축이 “아래”를 가리키도록 새로운 틀을 전환

      • +z 가 아래를 향하는 것으로 인식

    컴퓨터게임(DirectX)


    P 변환을 계산하고 수행

    P

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

    Z

    시작

    0 1 2 3

    X

    0 1 2 3

    X

    셀간격

    1.0

    Z

    컴퓨터게임(DirectX)


    • 변경된 좌표 시스템은 행렬의 순서와 동일 변환을 계산하고 수행

      • 즉 좌측 상단이 원점, 열의 수가 오른쪽으로 증가

      • 행의 수는 아래쪽으로 증가.

    • 다음과 같은 코드로 간단한 셀의 행과 열을 알아냄

      • float col = ::floorf(x);

      • float row = ::floorf(z);

    X 이하의 가장 큰 정수

    컴퓨터게임(DirectX)


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

      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)


    Y 셀을 구성하는 네 버텍스의 높이를 구성할 수 있다

    v1

    y

    v2

    Z

    (x,z)

    v0

    v3

    X

    컴퓨터게임(DirectX)


    • 높이를 구하기 위해서는 우리가 셀의 어떤 삼각형에 있는지를 먼저 알아야 한다.

    • 우리가 있는 삼각형을 찾기 위해서는

      • 왼쪽 상단의 버텍스가 원점에 오도록 현재의 셀을 이동

    • col과 row는 현재 우리가 있는 셀의 왼쪽 상단 버텍스 위치를 나타냄

      • x축의 -col이 되고 z축의 -row가 되도록 이동해야 함

    컴퓨터게임(DirectX)


    • 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)


    • 이제 삼각형에 있는지를 먼저 알아야 한다dz<1.0 - dx이면

      • 우리는 “위쪽”삼각형 v0v1v2에 있는것

      • 아니라면 “아래쪽” 삼각형 v0v2v3에 있는 것

    컴퓨터게임(DirectX)


    삼각형에 있는지를 먼저 알아야 한다위쪽” 삼각형에서의 높이 계산 방법

    • 위쪽 삼각형에서의 높이를 구하기 위해서는

      • 먼저 삼각형의 옆면과 벡터 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)


    컴퓨터게임(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)


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

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

      {

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

      }

    컴퓨터게임(DirectX)


    예제 애플리케이션 뿐이므로 : 지형

    컴퓨터게임(DirectX)


    • 높이맵 데이터를 포함하는 뿐이므로 RAW 파일을 이용해 지형을 생성하고 텍스처를 입히며 조명을 추가

    • 화살표 키를 이용해 지형 위를 걷는 동작

    • 먼저 지형과 카메라 초당 프레임 수를 기록하기 위한 전역 변수 추가.

      Terrain* TheTerrain = 0;

      Camera TheCamera(Camera::LANDOBJECT);

      FPSCounter* FPS = 0;

    컴퓨터게임(DirectX)


    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)


    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<Terrain*>(TheTerrain);

    d3d::Delete<FPSCounter*>(FPS);

    }

    컴퓨터게임(DirectX)


    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)


    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)


    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)


    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)