Clipmaps
원문 : Clipmaps
원문을 보시길 적극 추천.
요약
클립맵은 지형에 매우 높은 해상도의 텍스쳐를 매핑할수 있게 하는 SGI 워크스테이션에서 처음 구현된 기능이다.
원본 SGI 구현은 매우 전문적인 맞춤형 하드웨어가 필요했다.
NVIDIA GeForce 8800의 고급기능은 현재 소비자의 하드웨어를 사용하여 같은 알고리즘을 허용한다.
비록 현재 API들과 GeForce 8800가 8192 크기의 텍스쳐를 직접적으로 지원할 지라도,
이 크기가 비행 시뮬레이션같은 넓은 경치를 말할때는 충분하지 않을지도 모른다.
전체 경치에 하나의 텍스쳐를 사용하는 이 아이디어는 전체 경관 텍스쳐를 한번에 디자일 할 수 있을 뿐 아니라
파라미터화 시키는 것도 간단하다.
큰 텍스쳐는 몇개의 텍스쳐를 사용하여 블렌딩 하는 기존 방식에 비해 큰 이점을 가진다.
이는 텍스쳐를 원하는 만큼 복잡하게 디자인 할 수 있다는 것이다.
디자이너가 하나의 전체 맵을 만들었으므로 그걸 그대로 사용할 수 있다.
클립맵은 원근투영이기 때문에 텍스쳐 밉맵 피라미드 내의 상대적으로 작은 영역만
모든 프레임에서 엑세스 한다는 사실을 이용한다.
따라서 우리는 뷰어를 여기저기 이동할 때 이러한 "hot"영역을 관리하고
비디오 메모리에서 업데이트 해야 한다.
DX10 해결책은 이러한 영역들을 텍스쳐 배열에 저장하는 것이다.
픽셀셰이더에서 인덱싱 할 수 있다면 DX10에서 클립맵 알고리즘을 간단히 구현할 수 있다.
클립맵은 작동 방식
클립맵은 모든 단일 프레임에서 텍스쳐링에 필요한 모든 정보를 가지고 있는
밉맵피라미드의 부분 표현이라고 정의할 수 있다.
소스텍스쳐로부터 어떤 데이터가 사용될것인지 어떻게 결정할까?
답은 밉맵 샘플 선택 전략에 있다.
텍스쳐링을 하는 동안 픽셀의 영역에 텍셀을 1대1 매핑하여 사용할 수 있게 하는 것이 가장 이상적인 방법이다.
이것이 현재 화면 해상도를 기반으로 밉맵레벨에서 클립 사이즈를 정의하는 방법이다.
밉맵 피라미드의 가장 낮은 레벨은 항상 비디오 메모리에 적재되고 고정적으로 사용된다.
다른 모든 밉레벨은 모든 프레임에서 실제 데이터를 저장하기 위해 동적으로 업데이트 되는 클립맵 스택을 형성한다.
가장 일반적인 경우 스택의 내용은 스택의 크기와 뷰어의 위치에 따라 정의할 수 있다.
그림설명 :
위 그림은 클립맵을 그림으로 표시한 것이다.
파란색 밉 레벨들은 전체 월드에 매핑되는 데이터를 표시한다.
녹색 지역은 동적으로 로드되는 하위(sub)지역이다.
기본 아이디어는 하나의 2D 텍스쳐 배열에 클립맵 스택을 저장하는 것이다.
텍스쳐 배열은 DX10에서 추가된 기능이다.
밉맵 피라미드의 나머지 부분은 밉과 함께 기존방식의 2D텍스쳐로 구현된다.
하위 리소스 메소드 복사/업데이트를 사용하여 동적으로 스택을 갱신할 수 있다.
때로는 시스템 메모리에서 필요한 모든 데이터를 가지고 있을수 없는 경우도 있다.
그러므로 디스크에서 필요한 모든 데이터를 효율적으로 스트리밍 하기 위한 추가 메커니즘이 필요하다.
데이터 표현
클립맵 스택은 2D텍스쳐 배열에 저장된다.
이 배열은 클립맵의 동적인 부분을 이루고, 각 프레임에서 모든 밉레벨의 실제데이터를 포함해야 한다.
원본 밉 레벨마다 별도의 레이어가 있으므로, 밉이 없는 텍스쳐를 만들어야 한다.
나머지 이미지 부분은 기존의 일반적인 2D 텍스쳐로 저장할 수 있다.
DX10 API를 사용하여, 아래와 같이 리소스를 만든다.
(Clipmap 스택 텍스쳐의 경우 ArraySize 요소를 사용해 레이어 수를 지정해줘야 한다.)
D3D10_TEXTURE2D_DESC texDesc; ZeroMemory( &texDesc, sizeof(texDesc) ); texDesc.ArraySize = 1; texDesc.Usage = D3D10_USAGE_DEFAULT; texDesc.BindFlags = D3D10_BIND_SHADER_RESOURCE; texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; texDesc.Width = g_PyramidTextureWidth; texDesc.Height = g_PyramidTextureHeight; texDesc.MipLevels = g_SourceImageMipsNum - g_StackDepth; texDesc.SampleDesc.Count = 1; pd3dDevice->CreateTexture2D(&texDesc, NULL, &g_pPyramidTexture); texDesc.ArraySize = g_StackDepth; texDesc.Width = g_ClipmapStackSize; texDesc.Height = g_ClipmapStackSize; texDesc.MipLevels = 1; pd3dDevice->CreateTexture2D(&texDesc, NULL, &g_pStackTexture); |
각주 : 피라미드용 텍스쳐하나와 스택용 텍스쳐 하나필요.
피라미드용 텍스쳐에는 원본 이미지의 밉레벨에서 동적으로 사용될 스택을 뺀 만큼의 밉이 필요.
정보 갱신 전략
지형을 이리저리 이동하면서, 새로운 클립 중심 위치에 맞게 스택의 정보를 갱신해줄 필요가 있다.
대부분의 경우에는 클립맵 스택(동적으로 바뀌는 부분)의 각 레이어에 있는 비교적 작은 부분의 데이터를 바꿔줘야 한다.
스택안에서 큰 데이터 교체가 일어나는 것을 방지하기 위해, toroidal addressing라고 알려진 특정 기술을 사용한다.
Toroidal addressing은 이미지의 위쪽 새 데이터가 아래에 로드되고, 오른쪾의 새 데이터가 왼쪽에 로드된다.
이 개념의 접근방식은 중첩된 영역에 대한 변경을 필요로 하지 않기 때문에 아래의 경우 무척 효율적이다.
그림 2. 스택의 단일레이어에 대한 두가지 업데이트 단계
대부분의 어플리케이션에서는 가로와 세로 부분을 개별적으로 업데이트 하여
L자 모양 대신 간단한 직사각형 영역을 만들 수 있으므로 이 프로세스가 더 간단해 질 수 있다.
클립맵 텍스쳐 주소
모든 작업은 픽셀 셰이더에서 처리한다.
우선 가져올(fetch) 밉레벨을 결정할 필요가 있다.
이를 위해 ddx와 ddy명령어를 사용하여 화면공간에서의 쿼드 크기를 찾는다.
float2 dx = ddx(input.texCoord * textureSize.x); float2 dy = ddy(input.texCoord * textureSize.y); float d = max(sqrt(dot(dx.x, dx.x) + dot(dx.y, dx.y)), sqrt(dot(dy.x, dy.x) + dot(dy.y, dy.y)); |
이제 쉽게 접합한 밉레벨을 계산할 수 있다.
float mipLevel = log2(d); |
밉레벨을 부동소수점(float)으로 계산하고 소수부분을 사용하여 삼선형(trilinear) 필터링을 수행한다.
클립맵 텍스쳐 어드레싱은 간단하다.
필요한건 밉레벨을 기반으로 입력 텍스쳐 좌표를 스케일하는 것이다.
원본 이미지 크기를 클립맵 스택 크기로 나눠서 배율 인수를 계산한다.
float2 clipTexCoord = (input.texCoord) / pow(2, iMipLevel); clipTexCoord.x *= scaleFactor.x + 0.5f; clipTexCoord.y *= scaleFactor.y + 0.5f; float4 color = StackTexture.Sample(stackSampler, float3(clipTexCoord, iMipLevel)); |
스택 샘플러의 경우 toroidal 어드레싱을 구현하기 위해 어드레싱모드를 "wrap"모드로 지정해야한다.
Reference Link
'Study > Graphics ' 카테고리의 다른 글
밉맵 텍스쳐링 (0) | 2020.01.05 |
---|---|
Specular Showdown in the Wild West (0) | 2019.08.16 |
early z (0) | 2018.08.29 |
hlod (0) | 2018.07.20 |
What is Texture (0) | 2018.03.10 |