2015. 3. 5.

[DirectX9]멀티 텍스처(Mulit Texture)와 라이트 맵핑(Light Mapping)

 이 내용을 이해하기 위해서는 DirectX9에서의 텍스처를 어떻게 사용하는지에 대해서 알고 있어야 한다. 멀티 텍스처는 전과는 다르게 텍스처 이미지를 하나가 아닌 둘 이상 사용하는 경우를 말한다. 여러 개의 텍스처를 사용하는 것이 뭐가 중요하냐고 생각할 수도 있지만, DirectX9에서는 한 번에 결합할 수 있는 텍스처 스테이지의 갯수를 제한하기 때문에 주의해야 한다. 자신의 컴퓨터에서 이런 사항들을 확인할 수 있다. 윈도우 시작창에 "directx caps viewer"라고 입력해보자. 그러면 뷰어가 실행될 것이다. 아래의 스크린샷을 참고하여 해당 항목을 찾아보자.


  • MaxTextureBlendStages: 텍스처 스테이지 갯수이다.
  • MaxSimultaneousTextures: 출력순간에 결합할 수 있는 텍스처의 갯수이다. 한 번에 8장의 텍스처를 중첩해서 사용할 수 있다는 의미이다. 
 MatTextureBlendStages가 8이고, MaxSimultaneousTextures가 2인 경우를 생각해보자. 2개의 텍스처를 블랜딩(Blend) 처리하고 싶다면 아래 이미지처럼 수행해야 할 것이다.


  arg1, arg2 두 개의 인자로 전달받은 텍스처를 블랜딩하여 다음 스테이지로 그 결과물을 넘기게 된다. 우리는 2개의 텍스처를 블랜딩하기 때문에 더이상의 스테이지로 진행되지 않을 것이다. 만약 3개의 텍스처를 블랜딩한다면 2개의 텍스처를 블랜딩한 결과와 나머지 하나의 텍스처를 블랜딩하기 위해서 하나의 스테이지를 더 통과해야 할 것이다. 내 컴퓨터의 경우 8개의 텍스처를 동시에 입력받아 처리할 수 있으므로 1개의 스테이지에서 3개의 텍스처를 동시에 블랜딩할 수 있을 것이다.

 서론이 길어졌다. 이제 본격적으로 멀티 텍스처와 라이트 맵핑에 대해서 알아보자.


라이트 맵핑(Light Mapping)


 위의 그림은 가장 기본적인 라이트 맵핑을 보여주는 예이다. 벽 텍스처와 빛 텍스처를 합성하여 오른쪽과 같은 결과를 만들어 낼 수 있다. 보통 오른쪽과 같은 결과를 만들어 내는 가장 단순한 방법은 벽 텍스처와 빛 텍스처를 D3DTOP_MODULATE 연산을 통해 합성하면 된다. 이는 코드를 살펴볼 때 좀더 자세하게 알아보겠다.

 벽 텍스처는 벽을 나타내기 위한 텍스처로 이해하면 끝이다. 하지만 빛 텍스처는 흔히 Light Map이라고 불린다. Light Map은 광원이 비추는 영역을 실시간으로 연산하는 것이 아니라 미리 계산하여 2D 이미지로 저장한 결과이다. 위의 Light Map의 경우 가운데 지점을 중심으로 퍼져나가는 전구가 발하는 빛을 표현했다고 생각하자. 벽에 광원을 설치하여 이를 하나하나 연산한 결과로 표현해도 되지만, 만약 이런 빛이 100개가 있고, 이 빛들이 모두 위의 경우처럼 벽에 비춰지고 있다면 어떻게 할 것인가? DirectX9에서 제공하는 광원의 수는 제한적이다. 제한적이지 않다고 해도 100개의 광원을 연산하는 것은 성능의 큰 부담을 줄 수 있다. 이런 문제를 해결하기 위해 고안된 방법이 바로 라이트 맵핑(Light Mapping)이다.

 전구와 같은 점 광원(Point Light)는 위치만 정해지면, 그 위치에서 일정한 범위에 빛을 발할 것이다. 그 영역은 2D 이미지로 미리 표현할 수 있을 것이다. 위 그림의 Light Map이 바로 그 이지미이다. 우리는 이 이미지를 프로그램이 실행될 때 한 번 로딩하여 벽 텍스처와 합성하여 사용하면, 광원을 배치하는 것에 비해 성능 향상을 이룰 수 있다. 그리고 Light Map을 사용하면 DirectX9에서 제공하지 않는 광원의 형태로 활용할 수 있을 것이다. 


라이트 맵핑을 구현해보자

 우리의 목표는 다음 벽 텍스처와 Light Map을 합성하여 라이트 맵핑을 완성하는 것이다.

 
 두 텍스처를 합성한 결과는 쉽게 상상할 수 있을 것이다. 그럼 두 텍스처를 합성하기 위해 필요한 요소들을 생각해보자.
 앞에서 설명한 내용을 정리한다고 생각하고 다시 소개하였다.

 이제 벽을 만들어야 한다. 벽은 간단하게 사각형으로 만들자. 사각형은 삼각형 폴리곤 2개로 처리하도록 하자.
 정점 버퍼에 담은 정점을 DrawPrimitive() 함수를 통해 사각형으로 표현한다. 6개의 정점으로 2개의 삼각형을 만들고자 한다. 여기서는 단순히 DrawPrimitive() 함수의 인자로 D3DPT_TRIANGLELIST를 전달한다. 인덱스 버퍼를 사용하고, D3DPT_TRIANGLESTRIP을 사용하면 4개의 정점으로 벽을 표현할 수 있지만 이번은 단순하게 처리하였다.

 벽을 만들었으니 이번에는 벽에 벽 텍스처를 입혀보도록 하자.
 다음은 이번 포스트에 가장 중요한 사항이 라이트 맵핑 코드를 보자.
 Render() 함수가 길어졌다. 라이트 맵핑 기능 때문이다. 길어진 만큼 내용이 복잡해진 것은 아니다. 걱정하지 말고 차근차근 살펴보자. SetTextureStageState() 함수를 이해하는 것이 가장 중요하다.
 SetTextureStageState() 함수의 첫 번째 인자는 텍스처 스테이지를 의미한다. 두 번째 인자 D3DTSS_COLOROP는 색상에 대한 처리를 하겠다는 의미이고, 세 번째 인자 D3DTOP_SELECTARG1는 이 스테이지의 input 값을 다른 처리를 하지 않고 바로 output으로 전달하겠다는 의미이다. 결국 스테이지 0의 텍스처, 즉 벽 텍스처의 컬러값이 output으로 전달되었다. D3DTSS_ALPHAOP의 의미는 알파값을 의미한다. 컬러값과 마찬가지로 세 번째 인자를 D3DTOP_SELECTARG1로 전달하였으므로 벽 텍스처의 알파값이 그대로 output으로 전달될 것이다. 결과적으로 0번 텍스처 스테이지에서는 벽 텍스처의 컬러값과 알파값이 output으로 전달되었고, 이 값은 1번 텍스처 스테이지로 전달된다.
 다음은 1번 텍스처 스테이지 단계에 대한 처리이다. 먼저 1번 스테이지에서 처리될 텍스처에 대해서 살펴보자. 1번 텍스처 스테이지에는 라이트 맵핑할 빛의 텍스처가 이미 존재한다. 그리고 0번 텍스처에서 output으로 1번 스트에지로 전달된 컬러값과 알파값이 있다. 그래서 1번 텍스처 스테이지에서 D3DTSS_COLOROP는 0번 스테이지와 달리 D3DTOP_MODULATE로 처리한다. 알파값에 대한 연산도 마찬가지이다. 두 값을 모두 D3DTOP_MODULATE로 처리한다고 설정하였으므로, 어떤 값을 D3DTOP_MODULATE 처리할 것인지 정해야 한다. D3DTSS_COLORARG1, D3DTSS_COLORARG2를 통해 인자를 정할 수 있다. D3DTA_TEXTURE는 해당 스테이지의 텍스처를 의미하고, D3DTA_CURRENT는 이전 텍스처 스테이지에서 전달된 값을 의미한다. 결과적으로 1번 인자는 1번 스테이지의 텍스처의 컬러값, 2번 인자는 0번 스테이지에서 전달된 텍스처의 컬러값을 의미한다. 알파값에 대한 처리는 컬러값와 완전히 동일하다.
 1번 텍스처 스테이지의 결과가 2번 텍스처 스테이지로 전달되었다. 하지만 우리는 더이상의 텍스처 처리가 필요하지 않다. 그러므로 남은 텍스처 스테이지는 더이상 텍스처 처리를 하지 않겠다고 설정해주어야 한다.

 이제 라이트 맵핑을 구현한 전체 코드를 보도록 하자.
 실행 결과는 다음과 같다.

정리하면

  • 라이트 맵핑을 활용하여 광원을 대신하는 표현을 나타낼 수 있다. 광원을 배치하는 것에 비해 성능 향상을 기대할 수 있다.
  • 텍스처 스테이지 최대 갯수는 정해져있다. 그리고 한 번에 합성할 수 있는 텍스처의 수도 정해져있다.
  • SetTextureStageState() 함수의 두 번째 인자와 세 번째 인자의 조합으로 라이트 맵핑을 구현할 수 있다.

댓글 없음:

댓글 쓰기