2015. 3. 9.

[DirectX9]높이맵

 높이맵은 외부 지형을 처리하는 기법 중 하나이다. 전에 정점 버퍼, 인덱스 버퍼, 텍스처를 활용하여 가장 기본적인 지형을 생성하였다. 이 지형은 아무런 굴곡이 없는 편평한 지형이었다. 실제 외부 지형은 산과 같은 높이 굴곡이 있다. 이런 높이를 표현하기 위해서는 정점의 y 좌표값을 굴곡을 형성할 수 있게 설정해주어야 한다. 높이맵은 이런 y 값을 설정하는 방법 중 하나이다.


높이맵

 높이맵은 지형의 높이값을 0 ~ 255 사이의 명암값으로 나타낸 텍스처 파일이다.



 위 이미지가 바로 높이맵으로 사용되는 텍스처 파일이다. 아래는 지면을 입힐 텍스처이다. 0 ~ 255 사이의 명암값을 나타내는 파일이며, 255에 가까울수록 지형은 높아진다. 반대로 0에 가까울수록 지형은 낮다. 우리는 이 텍스처 파일로부터 지형의 높이 정보를 읽어와서 편평한 지형에 굴곡을 나타내려고 한다. 즉 정점의 x, z 값에 맵핑되는 텍스처의 명암값을 읽어서 정점의 y 값으로 설정해야 한다. 2차원 이미지의 밝기값이 3차원 공간에서 높이값으로 사용된다. 높이맵은 지형의 높이값을 저장하는 자료구조와도 같다.



 위 이미지는 높이맵과 이를 지형에 적용한 결과를 보여준다. 높이맵에 저장된 명암값을 읽어와서 지형의 높이값으로 변환하면 복잡한 지형을 간단하게 나타낼 수 있다. 그러면 이제 높이맵을 사용하기 위해서 어떤 과정을 거쳐야 하는지 알아보자.

  1. 높이맵으로 사용할 텍스처를 생성한다.
  2. 정점 버퍼에 정점을 저장할 때, 정점의 y 값을 높이맵의 명암값으로부터 읽어와서 저장한다.
 위 세 과정을 코드와 함께 살펴보도록 하자.


높이맵으로 사용할 텍스처를 생성하자

 먼저 높이맵으로 사용할 텍스처를 선언하고, 텍스처를 생성하자.
 2개의 텍스처를 생성하고 있다. 하나는 지면에 입힐 텍스처 파일이고, 하나는 높이맵으로 사용할 텍스처 파일이다. 그런데 높이맵을 생성하는 함수는 D3DXCreateTextureFromFileEx() 함수이다. 이 함수로 텍스처 파일을 생성하면 D3DFORMAT, D3DPOOL을 설정할 수 있다.


정점의 y 값을 높이맵의 명암값으로부터 읽어서 설정하자

 높이맵 텍스처로부터 명암값을 읽어오기 위해서는 2D 텍스처로부터 데이터를 읽어와야 한다. 이 때 사용되는 구조체가 바로 D3DSURFACE_DESC와 D3DLOCKED_RECT이다.
 D3DSURFACE_DESC 구조체로부터 텍스처의 가로, 세로 크기를 얻어 올 수 있다. 우리는 텍스처의 크기에 맞게 지형을 생성하고자 한다. 1픽셀 당 하나의 정점을 맵핑하고, 그 정점의 y 값을 텍스처의 명암값으로부터 읽어온다. 먼저 텍스처의 크기로부터 정점의 갯수가 정해지기 때문에 D3DSURFACE_DESC 구조체에서 가로, 세로 크기를 얻어와야 한다. 그리고 나서 가로 * 세로 갯수만큼 정점 버퍼를 생성해야 한다. 다음 코드는 이를 구현한 내용이다.
 정점 버퍼를 생성했다면 다음은 정점 버퍼에 정점을 저장할 차례이다. 이 때 정점을 저장하면서 높이맵 텍스처로부터 읽은 명암값으로부터 값을 읽어 정점의 y 값으로 설정한다. 높이맵 텍스처의 명암값을 읽어오기 위해서는 정점 버퍼에서 Lock() 함수를 호출하듯이, 텍스처에서 LockRect() 함수를 호출하여 메모리 주소값을 얻어와야 한다. 이 때 주소값을 저장하는 구조체가 바로 D3DLOCKED_RECT이다. 주소값만 저장할 용도라면 포인터로 선언하면 되는데 굳이 구조체로 만든 이유가 있다. 그래픽 하드웨어나 파일에 따라서 한 행의 크기가 다를 수 있기 때문에 Pitch 값을 통해 정확하게 읽어오기 위해서라고 한다.
 이 구조체에 주소값과 Pitch값을 저장한다.
 그 다음은 정점의 좌표값을 설정해야 한다. 특히 y 값은 텍스처의 명암값으로부터 읽어와야 한다. 텍스처의 명암값을 읽어오기 위해서는 LockRect() 함수로 텍스처의 색상값에 접근할 수 있는 주소값을 얻어와서 D3DLOCKED_RECT의 pBits 변수에 저장된다. 그리고 텍스처의 한 행의 바이트 값은 Pithch에 저장된다. 이 Pitch 값을 4로 나누면 한 행에 저장된 픽셀의 갯수를 알 수 있다. 결국 열의 값을 나타내는 오프셋(Offset)이 된다. 결국 pBits의 주소값을 기본으로 포인터 연산을 수행하여 한 픽셀 단위로 명암값을 읽어올 수 있다.
 이제 정점 버퍼에 좌표값도 모두 설정하였다. 이제 정점 버퍼에 보관된 정점을 읽어서 물체를 그리면 완료이다.


전체코드


실행결과


정리하면

  • 정점의 y 값을 텍스처의 명암값 형태로 저장한 것이 바로 높이맵 텍스처이다.
  • D3DSURFACE_DESC와 D3DLOCKED_RECT를 통해 텍스처의 크기 정보와 텍스처 픽셀의 명암값을 저장하여, 이를 정점 버퍼를 생성하고 정점의 값을 설정하는데 사용한다.

댓글 없음:

댓글 쓰기