1 / 75

제 13 장 기본적인 지형 렌더링

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

aysha
Download Presentation

제 13 장 기본적인 지형 렌더링

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. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 제13장 기본적인 지형 렌더링 컴퓨터게임(DirectX)

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

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

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

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

  6. (그림13.3) 포토샵에서 만든 그레이스케일 이미지 컴퓨터게임(DirectX)

  7. 컴퓨터게임(DirectX)

  8. 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)

  9. 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)

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

  11. 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)

  12. 지형 기하정보 생성하기 Z 시작=(-w/2,d/2) 셀 간격 X 셀/ 사각형 d=깊이 끝=(w/2, -d/2) W=너비 컴퓨터게임(DirectX)

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

  14. class Terrain { public: Terrain( IDirect3DDevice9* device, std::string heightmapFileName, int numVertsPerRow, int numVertsPerCol, int cellSpacing, // 셀 간의 간격 float heightScale); // 높이 배율을 조정하는 값 ….. // 메서드 생략 private: … //장치/버텍스 버퍼 생략 컴퓨터게임(DirectX)

  15. int _numVertsPerRow; int _numVertsPerCol; int _cellSpacing; int _numCellsPerRow; int _numCellsPerCol; int _width; int _depth; int _numVertices; int _numTriangles; float _heightScale; }; 전체 코드는 예제 참고 컴퓨터게임(DirectX)

  16. 생성자에 전달된 값들을 이용해 다음과 같은 방법으로 다른 변수들의 값을 계산함. _numCellsPerRow = _numVertsPerRow – 1; _numCellsPerCol = _numVertsPerCol – 1; _width = _numCellsPerCol * _cellSpacing; _depth = _numCellsPerCol * _cellSpacing; _numVertices = _numvertsPerRow * _numvertsPerCol; _numTriangles = _numCellsPerRow * _numCellsPerCol * 2; 컴퓨터게임(DirectX)

  17. 우리가 이용할 지형의 버텍스 구조체 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)

  18. TerrainVertex 는 Terrain 클래스 내부의 중첩된 클래스임 • Terrain 클래스 외부에서는 이 클래스가 필요하지 않기 때문이다. 컴퓨터게임(DirectX)

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

  20. 지형 버텍스와 텍스춰 버텍스간의 대응 • 텍스처 좌표 계산방법은 아래 그림을 참고 • 이 그림은 버텍스(i,j)에 해당하는 텍스처 좌표(u,v)를 얻는 간단한 시나리오를 보여줌 (1,0) (0,0) +U (0,1) (1,1) +V 컴퓨터게임(DirectX)

  21. u = j · uCoordIncreamentSize • v = i · vCoordIncreamentSize 여기에서 • uCoordIncreamentSize = • vCoordIncreamentSize = 1 numCellCols 1 numCellRows 컴퓨터게임(DirectX)

  22. 최종적으로 버텍스를 생성하는 코드 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)

  23. 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)

  24. 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)

  25. 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)

  26. 인덱스 계산 –삼각형 정의하기 • 삼각형 격자의 인덱스를 계산하기 위해서는 • 그림의 좌측 상단에서 시작 • 우측 하단으로 각각의 사각형을 구성하는 두 개의 삼각형들을 차례대로 계산 버텍스열 j (0,0) 버텍스열 j + 1 버텍스행 i A B C D ij번째 셀 버텍스행 i+1 (m,n) 컴퓨터게임(DirectX)

  27. 사각형(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)

  28. 인덱스를 계산하는 코드 Bool Terrain::computeIndices() { HRESULT hr = 0; hr = _device->CreateIndexBuffer( _numTriangles * 3 * sizeof(WORD), //삼각형당 3개의 인덱스 D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOLMANAGED, &_ib, 0); 컴퓨터게임(DirectX)

  29. 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)

  30. 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)

  31. 텍 스 처 링 • Terrain 클래스는 지형에 텍스처를 입히는 두 가지 방법을 제공 1) 미리 만들어둔 텍스처 파일을 읽어들이고 이를 이용하는 방법 • IDirect3DTexture9 인터페이스로의 포인터인 _tex데이터 멤버로 텍스처 파일을 읽어들임 • bool loadTexture(std::string fileName); 컴퓨터게임(DirectX)

  32. 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)

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

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

  35. 텍스처를 만들어내는 코드 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)

  36. 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)

  37. 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)

  38. 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)

  39. if(FAILED(hr)) { ::MessageBox(0, "D3DXFilterTexture() - FAILED", 0, 0); return false; } return true; } 컴퓨터게임(DirectX)

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

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

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

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

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

  45. 다음은 이 두 개의 벡터 u와 v를 보여줌 a Y u = b - a b v = c - a Z c X 컴퓨터게임(DirectX)

  46. 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)

  47. 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)

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

  49. 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)

  50. D3DXVECTOR3 n; D3DXVec3Cross(&n, &u, &v); D3DXVec3Normalize(&n, &n); float cosine = D3DXVec3Dot(&n, directionToLight); if(cosine < 0.0f) cosine = 0.0f; return cosine; } 컴퓨터게임(DirectX)

More Related