개요
DirectX는 6개짜리 텍스트 배열을 입방체 맵(cube map)이라는 특별한 형태로 해석하여 사용 할 수 있다. 각 텍스트는 좌표축에 정렬된 큐브의 각 면을 나타내며 0~5번째 텍스처는 각각 +-X 면 +- Y면 +-Z면에 해당한다. 이는 6개 짜리 텍스트 배열로 자원을 생성하고 차원 멤버를 cube map로 설정한 desc로 srv를 만들면 큐브 맵에 대한 뷰가 생성된다. 이후 그 srv를 루트 매개변수에 달고 hlsl에서 TextrueCube라는 타입으로 받으면 큐브 맵이 쉐이더에 전달된다.
큐브 맵 에서는 3차원 좌표로 표본 추출을 할 수 있는데 원점에서 해당 좌표에 해당하는 벡터로 쏜 반직선이 맞는 입방체의 한 점이 추출되는 점이 된다. 3차원 좌표가 표현하는 벡터는 조회 벡터로 불리며 방향만 같다면 길이에 상관없이 같은 표본을 추출하게 된다.
환경 매핑
입방체 맵의 대표적인 사용예시는 환경 매핑이다. 환경 매핑은 환경 맵을 활용하는 기법인데 환경 맵은 어느 한 시점에서 수직수평90도의 각도로 6방면으로 바라본(렌더링 된) 장면을 큐브의 각 면으로 가진 입방체 맵이다. 이는 미리 그려진 정적 환경맵이 될 수도 있으며, 매 프레임 6방면을 렌더링하여 만드는 동적 환경맵이 될 수도 있다.
배경 구
환경 맵의 대표적인 사용예는 배경을 나타내는 구를 구현할 때 사용된다. 일단 현재 카메라 위치를 원점으로 하는 구를 만들어 렌더링 한다. 이때 정점 셰이더에서 구의 각 점의 로컬 포지션을 출력한다. 픽셀 셰이더에는 보간된 구의 한 점의 로컬 포지션을 조회 벡터로 사용하여 배경 육방면을 그린 입방체 맵으로 부터 표본을 추출하여 그 색을 그대로 출력한다. 그러면 그러한 배경이 렌더링에 적용된다. 이때 구의 후면선별은 꺼야한다.
또한 배경 구를 그릴때는 정점 셰이더에서 동차 절단 공간 좌표의 z값을 w로 하여 항상 먼 평면의 위치에 정점들이 위치하도록 해야한다. 그리고 깊이 판정을 작거나 같은 경우 성공하는것으로 해주면 된다.
마지막으로 배경 구는 모든 물체들을 렌더링하고 마지막에 렌더링 하는 것이 성능에 좋다. 어차피 물체에 가려질 부분을 굳이 렌더링하고 덮어 씌어지는 것 보다 물체들의 깊이에 의해 덜 그려지는 것이 더 낫기 때문이다.
배경 반사 물체
환경 맵의 다른 사용 예는 배경을 반사한 물체를 렌더링하는 것이다. 일단 어떠한 물체를 기존의 텍스처, 조명 방식대로 그려 색상을 구한다. 그 후 해당 픽셀의 toEYE 벡터의 노말 벡터에 대한 반사 벡터를 조회벡터로 하여 배경 큐브 맵으로 부터 색상을 추출한다. 이후 그 색상을 기존 색상과 여러 재질 특성에 비례해 적절히 합치면 배경을 반사하는 물체가 그려진다. 여기서 배경 큐브 맵은 해당 물체의 각 픽셀에 360도 방향에서 그 픽셀에 부딛혀 반사될 배경광이라고 해석 할 수 있다. 이때 물체의 모든 픽셀에 동일하게 빛이 들어가게 되는데 이는 배경의 거리가 멀거나 물체가 구형태일 때는 크게 어색함을 유발하지는 않는다. 다만 배경의 거리가 짧거나 평면의 물체일 경우 어색할 수 있으므로 월드상의 배경 입방체 aabb와 월드 상의 반사벡터의 충돌 점을 가르키는 조회벡터를 구하는 알고리즘을 사용하여 반직선으로 표본 추출을 할 수 있도록 해야한다.
동적 입방체 맵
위의 배경 반사 방식은 미리 그려진 정적 입방체를 반사하는 물체를 그리는것만 가능하다. 실시간으로 움직이는 물체나 추가되는 물체들을 반사하지는 못한다. 이를 위해서는 매 프레임 입방체를 그리는 동적 입방체 매핑을 사용해야한다. 실시간으로 주변의 환경을 반사하는 물체를 그리려면 해당 물체의 원점에서 90/90 각도로 6방향에 대한 렌더링을 진행하여 입방체 맵의 각 면을 그리고 그 입방체 맵을 이용하여 해당 물체를 그리면 된다.
기하셰이더를 활용한 동적 입방체 매핑
동적 입방체 매핑에 기하셰이더를 활용하면 한번의 그리기 호출로 6면을 동시에 그릴 수 있다.
우선 여기에는 렌더 타겟과 깊이 버퍼 타켓으로 텍스처 배열을 사용해야한다. 저 두개에 동일한 갯수의 텍스처 배열을 달면 여러 렌더 타겟에 동시에 렌더링이 진행된다. 이렇게 하면 우선 정상적으로 정점 셰이더가 수행되지만 래스터화 단계전에 동차 철단 공간 외에 렌더 타겟 인덱스를 필수로 출력해야한다. 한 기하도형의 모든 정점이 한 렌더 타겟 인덱스를 가르키는게 정상이며 그렇게 된다면 해당 도형은 해당 동차 절단 공간으로 해당 인덱스의 렌더 타겟에 렌더링(픽셀 셰이더 진행)이 되게 된다. 우리는 이를 입방체 렌더링에 사용한다.
우선 입방체를 나타내는 텍스처 배열을 렌더 타겟으로, 그 해상도에 해당하는 깊이 버퍼 6개짜리 텍스처 배열을 깊이 버퍼로 설정한다. 그리고 입방체의 6방향에 해당하는 카메라의 시야행렬을 모두 루트 매개변수로 달아준다.(투영행렬은 동일하니 하나면 된다) 그 후 정점 셰이더에서 해당 정점을 월드 좌표로만 변환하여 기하 셰이더로 넘겨준다. 기하셰이더는 한 삼각형을 받으면 그 삼각형을 6개의 삼각형으로 만들고 각 삼각형의 정점들은 0~5번째의 렌더 타겟 인덱스를 달고 그 렌더 타겟에 해당하는 시야 행렬을 이용하여 동차 절단 공간까지 변환해준다. 그렇게 6개의 삼각형을 출력하면 각 삼각형들은 각 입방체 면에 각 동차 절단 공간으로 정삭적으로 픽셀셰이더가 진행된다.
이렇게 하면 한번의 그리기 호출로 6면을 그릴 수 있다. 다만 이 방법에는 다음과 같은 단점이 있다.
- 기하셰이더가 너무 많은 출력을 하여 자체적인 성능저하가 있을 수 있다.
- 각 면에 대해 6번의 그리기 호출을 하면 cpu에서 오브젝트 단위로 절두체 선별이 가능하지만 기하셰이더를 활용하면 이가 불가능하다.
위 단점중 두번째는 결국 6면 모두에 보이게 되는 배경 구만을 입방체에 렌더링한다면 문제가 되지않지만 1번은 여전히 문제가 된다. 1번 단점의 경우 기하셰이더가 각각 다른 렌더 타겟에 삼각형을 출력할 때 성능이 저하되지않게 하는 기술이 나오고 있지만 아직 directX에는 업데이트 되지 않았다.(참고 서적의 출판년도 기준)
참고서적
DirectX 12를 이용한 3D 게임 프로그래밍 입문
'그래픽스 > DirectX12' 카테고리의 다른 글
[Directx12][20장]그림자 매핑 (0) | 2023.03.21 |
---|---|
[Directx12][19장]법선 매핑 (0) | 2023.03.21 |
[Directx12][17장]3차원 물체의 선택 (0) | 2023.03.21 |
[Directx12][16장][2]절두체 선별 (0) | 2023.03.21 |
[Directx12][16장][1]인스턴싱 (0) | 2023.03.21 |