2015. 3. 8.

[DirectX9]지형 생성

 정점 버퍼인덱스 버퍼를 활용하여 지형을 생성해보자. 먼저 가장 기본적인 평평한 지형을 만들고, 그 다음 그 지형을 활용하여 굴곡이 있는 지형을 생성해보도록 하겠다. 이번 포스트에는 DirectX9에 대한 새로운 지식이 필요하다기 보다는 정점 버퍼와 인덱스 버퍼를 맵핑하는 알고리즘이 추가된 정도이다. 이 알고리즘 부분만 이해한다면 나머지 부분은 이미 여러 번 코드이므로 쉬울 것이다.


전체적인 과정
  1. 평면을 생성하는 정점을 선언하고, 정점 버퍼에 저장한다.
  2. 정점 버퍼에 저장된 정점을 활용하는 인덱스 버퍼를 만들어 평면을 생성하는 정보를 저장한다.
  3. 생성된 평면에 텍스처를 입힌다.
 크게 이 세 과정을 거쳐서 간단한 지형을 생성하고자 한다. 정점 버퍼, 인덱스 버퍼, 텍스처에 대한 간단한 지식이 한 번에 사용되는 예제가 될 것이다.


사용될 정점을 저장하는 정점 버퍼

가로 길이가 100이고, 세로 길이가 100인 평면 지형을 만들기 위해서는 몇 개의 정점이 있으면 되겠는가? 최소로 생각하면 4개만 있어도 충분하다. 평면 지형은 하나의 사각형으로 표현할 수도 있기 때문이다. 하지만 우리는 나중에 평면 지형을 활용하여 지형의 높이 굴곡도 표현하고자 하기 때문에 지금은 길이 1당 정점이 하나씩 필요하다고 생각하고 설명을 진행하도록 하겠다. 길이 1당 정점이 하나씩 필요하다면 100 * 100개의 정점이 필요하다.

 먼저 사용자 정의 정점을 선언하자. 
 사용자 정의 정점 구조체를 기반으로 100 * 100개의 정점을 위한 정점 버퍼를 먼저 생성해보자.
 가로 길이와 세로 길이 만큼 정점 버퍼의 크기를 설정하여 정점 버퍼를 생성하였다. 그리고 Lock() 함수를 통해 정점 정보를 저장할 주소값을 얻어왔다. 포인터 변수 pV에 그 주소값을 저장하였다. 정점의 위치, 수직 벡터, 텍스처 좌표를 설정한 후, 이 값을 정점 버퍼에 하나씩 저장하고 있다. 저장을 완료한 후에 Unlock() 함수를 호출하였다.

 우리는 현재 총 100 * 100개의 정점을 정점 버퍼에 저장하였다. 저장된 정점들과 인덱스 버퍼를 활용하여 지형을 생성하여야 한다. 그러기 위해서는 먼저 지형을 만들 수 있는 인덱스 정보들을 인덱스 버퍼에 넣어야 한다.


지형을 형성하는 인덱스 정보를 저장하는 인덱스 버퍼

 인덱스 버퍼의 장점에 대해서는 이전 포스트에서 언급한 적이 있다. 그리고자 하는 물체가 복잡하여 사용되는 정점의 수가 많을수록 정점 인덱스를 사용하는 효과는 더욱 커진다. 만약 인덱스 버퍼를 사용하지 않고, 가로 100, 세로 100의 평면을 삼각형으로 표현하기 위해서는 몇 개의 정점이 필요할까? 간단하게 계산할 수 있다. 총 100 * 100개의 작은 사각형이 그려져야하고, 이 사각형을 그리기 위한 2개의 작은 삼각형을 그려야 한다. 결국 100 * 100 * 2개의 삼각형을 그려야 한다. 정점 인덱스를 사용하지 않으면 좌표값이 동일한 정점의 중복을 허용해야 한다. 즉 하나의 삼각형을 그리는데 3개의 정점이 고정적으로 사용된다. 그래서 총 필요한 정점의 수는 100 * 100 * 2 * 3개가 된다. 이에 반해 인덱스 버퍼를 사용하면 필요한 정점의 수는 100 * 100으로 고정된다. 이 경우 인덱스 버퍼를 사용하면 정점 버퍼에 소비하는 메모리의 크기는 1/6로 줄일 수 있다. 여튼 좋으니깐 사용하도록 하자.

 인덱스 버퍼를 생성하는 코드를 보도록 하자.
 방금 전 평면 지형을 생성하기 위한 작은 사각형의 갯수에 대해 설명하였다. 그리고 그 사각형의 갯수의 두 배만큼의 삼각형이 필요하다. 그러므로 주석에서 설명하고 있는 것처럼 인덱스 버퍼의 크기는 코드에서처럼 설정된다.

 다음은 인덱스 버퍼에 작은 삼각형들을 생성하는 알고리즘이다. 코드를 보기 전에 정점 버퍼에 저장된 정점의 인덱스를 살펴보자. 아래 그림을 보자.


 우리가 정점 버퍼를 생성하면서 저장한 정점은 위의 그림처럼 저장되어 있다. 위의 숫자는 바로 정점의 인덱스를 의미한다. 위 그림은 가로 4, 세로 4인 경우 정점 버퍼에 저장된는 정점과 인덱스를 보여주고 있다. 가로 100, 세로 100의 경우는 아래와 같을 것이다.(화살표는 무시하도록 하자)


 첫 번째 행는 0 ~ 99, 두 번째 행은 100 ~ 199, 100번째 행은 99900 ~ 99999 인덱스가 부여될 것이다. 이 인덱스를 기반으로 평면을 형성하는 인덱스 버퍼를 만들어 보자.
 알고리즘에 따르면 첫 번째 삼각형은 [0, 1, 100], 두 번째 삼각형은 [100, 1, 101]로 인덱스 버퍼에 저장된다. 그리고 이 두 개의 삼각형으로 평면을 구성하는 작은 사각형을 표현할 수 있다. 이런 삼각형을 표현하는 MYINDEX 구조체가 100 * 100 * 2개만큼 인덱스 버퍼에 저장된다. 그리고 주목할만한 사실은 MYINDEX에 저장되는 인덱스가 반시계 방향(CCW)인지 시계 방향(CW)인지 살펴보아야 한다. 우리가 알고리즘을 통해 생성한 MYINDEX는 시계 방향으로 구성된다. SetRenderState() 함수를 통해 컬링 모드를 시계 방향으로 설정했다면 아무것도 그려지지 않게된다. 이 점을 유의하도록 해서 인덱스 버퍼와 컬링 모드를 설정하도록 하자.


생성한 지형에 텍스처를 입히자

 다음 이미지는 텍스처로 사용할 파일이다. 다운받아서 사용하도록 하자.

 텍스처를 초기화하였다.
 그리고 이 텍스처를 0번으로 설정하고, 확대 필터, 텍스처 좌표계를 설정하였다.


전체 코드

실행 결과


정리하면

  • 정점 버퍼, 인덱스 버퍼, 텍스처를 활용하여 지형을 생성할 수 있다.
  • 인덱스 버퍼를 사용하면 정점 버퍼에 저장하는 정점의 개수를 줄일 수 있다.

다음 주제: [DirectX9]높이맵

댓글 없음:

댓글 쓰기