[GpGiki 대문으로]

Terrain Texturing


원문: http://www.delphi3d.net/articles/printarticle.php?article=terraintex.htm

제 맘대로 번역했습니다. 이상한곳 지적바랍니다. - 강성진


Terrain texturing

요즘은 모든 사람들이 그들의 할머니와 함께 지형엔진을 만듭니다. 연속적인LOD 알고리즘과 빠른 지형렌더링 테크닉 같은 정보를 찾기는 어렵지 않죠. 그러나 거기에는 더 많은 일이 있습니다. -- 적당히 최적화되고 LOD된 지형메쉬를 가졌다 하더라도, 또한 우리에게는 텍스쳐를 입히는게 필요합니다. 그러나 이 문제에 어떻게 덤벼들지를 가르쳐주는 문서는 매우 적죠.

Many terrain engines simply use a single large texture, e.g. 1024x1024, and stretch that over the terrain. This approach is ridiculously simple to implement, but it is severely limited by the amount of texture memory at our disposal. For a decent-sized terrain, a 1024x1024 texture will never be able to capture sufficient detail. Increasing the texture resolution is not very feasible. A 1024x1024 RGB texture takes up 3MB -- the next valid size is 2048x2048, which would take up 12MB. Without mipmaps.

많은 지형 엔진은 단순하게 하나의 커다란 텍스쳐(지형전체에 잡아늘여진 1024x1024 크기의)를 사용합니다. 이 방법을 구현하기는 바보처럼 간단하지만, 사용할 수 있는 텍스쳐 메모리 용량에 엄격히 제한됩니다. 왠만한 크기의 지형에서, 1024x1024 텍스쳐로 충분한 디테일을 얻기는 결코 불가능합니다. 텍스쳐 해상도를 늘리는 것도 가능하진 않죠. 1024x1024 RGB 텍스쳐는 3MB 를 잡아먹습니다. -- 다음으로 가능한 크기인 2048x2048 은 12MB를 차지하죠. 밉맵없이.

디테일 텍스쳐

텍스쳐 디테일을 증가시키기 위해, 많은 엔진들은 디테일 텍스쳐에 의지하기도 합니다. 디테일 텍스쳐란 그레이스케일 텍스쳐로 지형을 가로질러 여러 번 반복되며 기본 텍스쳐의 색상을 변화시킵니다. 디테일 텍스쳐는 지형에 작은 틈과 굴곡처럼 보이는 착각을 만듭니다. -- 기본 텍스쳐에서 얻기는 불가능한 세세함이죠.

유감스럽게도, 주의깊은 관찰자는 디테일 텍스쳐가 반복된다는 것을 즉시 알아챌겁니다. 관찰자가 풀이 많은 지역에서 눈덮인 곳까지 걸어 간다해도, 항상 같은 틈과 굴곡을 보게 될것입니다. 추가하고 싶은 표면타입과 선택한 디테일 텍스쳐가 다소 양립할수 있어야 하기 때문에, 지형에 놓을수 있는 변화무쌍함을 제한받습니다. 이점을 오른쪽의 그림이 아프도록 명백하게 보여줍니다.

텍스쳐 혼합

이상적으로 말해, 우리는 지형 어디에나 충분한 디테일을 가지고 싶습니다. 그리고 표면이 변화하게끔 하고싶고요. 모든 표면타입(모래, 풀, 바위...)에 다른 텍스쳐를 가지고, 지형을 깔아버리면 가능합니다. 그러나 한 표면에서 다른 표면으로 변화시키는 방법이 필요합니다. 이 삼각형은 풀이고, 다음은 돌이다라고는 할수없죠. -- 한 텍스쳐에서 다른 텍스쳐로의 변화가 자연스럽게 보이도록 하는게 필요합니다. 이렇게 하기위해, 텍스쳐끼리 블렌딩할수 있는 멀티텍스쳐 하드웨어를 사용할수 있습니다.

우선 첫째로 필요한 것은 어떤 텍스쳐가 어디로 갈지 계산하는 것입니다. 이건 대체로 경사도와 높이를 가지고 수행하게 됩니다. 가파른 절벽들은 바위 텍스쳐, 평평한 지역들은 풀이 되겠고, 어쩌면 높은 고도에서는 약간의 눈을 원할수도 있겠죠. 어떤곳을 가리키는지 알았을 때, 이제 3D카드가 이해하게끔 이 정보들을 설명하는게 필요합니다.

각 정점마다, 모든 표면타입에 대한 “적용율(Coverage Factor)”을 할당합니다. 이 적용율은 모든 입력 텍스쳐들의 가중평균을 잡는데 사용될겁니다. 예를들어, 절벽하단에 대한 정점이라면 50% 풀과 50%의 바위일수 있겠죠. 이렇게 해서 한 텍스쳐에서 다른 텍스쳐로 부드러운 변화를 가능하게 합니다.

T1, T2, T3(예를들면 모래, 풀 그리고 바위)의 3개의 텍스쳐를 가지고 있다면, 모든 픽셀마다 아래의 식을 계산하기 원합니다.

Color = T1*c1 + T2*c2 + T3*c3 (where c1+c2+c3 = 1)

이 식에서 c1, c2, c3는 적용율이고, 결과는 3개의 입력 텍스쳐들의 가중평균입니다. 모든 입력 텍스쳐를 넣을수 있을만큼 충분한 텍스쳐 유닛을 가졌다고 가정한다면, 필요한것은 어떻게든 하드웨어에 적용율을 넘겨줄 방법입니다. 그리고 하드웨어에서 가중평균을 계산하기 위한 방법도요.

근데, 내 비디오 카드가 그것을 할 수 있을까요?

적용율을 넘기는 방법은 간단합니다: RGB=(c1,c2,c3) 로 정점컬러 안에 적용율을 인코딩할수 있습니다. 픽셀셰이더로 위 계산식을 사용해 텍스쳐를 블렌딩할수 있습니다. 물론, 이 방법의 결점은 픽셀셰이더는 오직 하이엔드 3D카드서만 지원된다는 거죠. 또 OpenGL의 추가적인 결점은 픽셀셰이더가 벤더에 따라 틀려지는 확장을 사용해야 한다는 겁니다. 본질적으로 코드를 두번 써내야 한다는거죠(NVidia 한번, ATI 한번). 우리는 게으른 놈팽이들이기 때문에, 그렇게 하진 않을겁니다.

운좋게도, 더 나은 해결책이 있습니다. 만약 텍스쳐들을 함께 블렌딩하기 원한다면 GL_ARB_texture_env_combine 을 살펴보는 것이 필요합니다. 이 확장은 GL_INTERPOLATE 모드를 제공하는데, 2개의 입력(textures or the primary color) 들을 세번째 입력인수를 보간율로 사용해 선형보간해 줍니다 :

LERP(A, B, C) = A*B + (1-A)*C

이것은 B와 C의 가중평균으로 생각될수 있습니다. 그래서 2개의 입력 텍스쳐를 블렌딩하기위해 사용할수 있죠. 그런데, 원래 계산식은 3개의 텍스쳐를 가집니다. 괜찮습니다, 왜냐하면 각 텍스쳐 유닛마다 보간할수 있으니까요. 3개의 입력 텍스쳐를 가지고 이렇게 계산할수 있습니다 :

LERP(A2, LERP(A1, T1, T2), T3)

이건 이렇게 평가되겠죠:

T1*A1 + T2*(1-A1))*A2 + T3*(1-A2)

원래 계산식처럼 보이지 않죠. 그러나 수학을 할줄 안다면, A1, A2를 c1, c2, c3의 함수로 계산할수 있다는 것을 찾을수 있을거고, 두 계산식이 정확히 같다는 것을 알게 될겁니다. 그렇지만 고민을 덜어주도록 하죠.

A1 = c1/(1 - c3)

A2 = 1 - c3

3개의 적용율을 단지 2개로 줄였고, RGBA(A1,A1,A1,A2)로 정점색상을 인코딩할수 있습니다. 이제 텍스쳐환경을 세팅할수 있습니다. 다음처럼 적용된 3개의 텍스쳐 유닛이 필요합니다.

UnitTextureOutput
0T1T1
1T2LERP(Col0.RGB, T1, T2)
2T3LERP(Col0.A, Unit1, T3)

이제 어째서 3개의 텍스쳐 유닛으로 제한되었는지 알아보죠. 최근의 모든 카드들은 적어도 4개의 텍스처 유닛을 가지고 있습니다. 그러나 ARB확장만을 사용하는 텍스쳐 블렌딩 시스템은 오직 텍스쳐 3개만 사용해야 구현할수 있습니다. 사실, 우리가 사용한 특징은 OpenGL 1.3 스펙내의 한 부분입니다! 이것이 의미하는 점은 우리는 코드를 한번만 쓰면되고, 3개의 텍스쳐유닛을 가진 대부분의 어떤카드에서도 돌아가리라 기대할수 있다는 겁니다. Geforce3 또는 그 이상, 라데온 또는 그 이상, 그리고 Kyro2 모두 이 동작을 할수있을겁니다. 서로 다른 카드들에 대해 융통성있게 쓰여진 특별한 상황의 코드는 없습니다.

풀, 모래 그리고 바위? 눈은 어떤가요!?

The downside to all this is that we are, as mentioned in the previous paragraph, limited to only three textures. The reason for this is simply that we can only pass two interpolants in the primary color -- one in RGB and one in alpha. It's possible to add more interpolants by putting them in textures, but then we need at least two more texture units. If we had six texture units, for example, we could use the following setup to blend up to five textures:

c! 이부분이 어색하고 이상하고 틀린거 같습니다. 제대로 손봐주세요.

앞서 언급했듯, 오직 3개의 텍스쳐로 제한된다는게 불리한 점입니다. 이 같은 이유는 단순히 primary color에는 오직 2개의 보간인수만 넘길수 있다는 겁니다(하나는 RGB, 하나는 alpha). 텍스쳐에 보간인수를 저장해서 더 많은 인수를 추가하는 것이 가능하지만, 그땐 적어도 2개의 텍스쳐 유닛이 더 필요합니다. 만약 6개의 텍스쳐 유닛이 있다면, 5개의 텍스쳐를 블렌딩하기 위해 다음 세팅을 사용할수 있습니다.

UnitTextureOutput
0II
1T1T1
2T2LERP(Col0.RGB, T1, T2)
3T3LERP(Col0.A, Unit2, T3)
4T4LERP(Unit0.RGB, Unit3, T4)
5T5LERP(Unit0.A, Unit4, T5)

이 셋업에서, I는 2개의 보간인수를 가진RGBA 텍스쳐로 텍스쳐유닛 4, 5에서 사용됩니다. 이걸 가능하게 하려면 GL_ARB_texture_env_crossbar 혹은 GL_NV_texture_env_combine4 지원이 필요합니다. 왜냐하면 텍스쳐유닛 4,5 가 텍스쳐유닛0 을 참조하는게 필요하기 때문이죠. 현재 존재하거나 다가올 카드들은 6개 또는 그 이상의 텍스쳐 유닛을 가지고 있을거고, 이 확장중에 하나를 지원할것이기 때문에 그다지 큰일은 아닐겁니다.

결론

최종결과는 어느정도 이처럼 보일겁니다.

이 지형을 위한 텍스쳐는 풀, 갈색 진흙, 그리고 바위입니다. 진흙이 경사면에 나타나기 때문에 계곡내에선 풀이 가장 두드러지는걸 볼수 있습니다. 고도가 높아질수록 진흙은 바위로 바뀌죠. 이 스크린샷은 문서와 함께 공개된 데모에서 가져왔습니다. 보다시피, 지형에 라이트맵을 더하기 위해 제 Geforce3의 4번째 텍스쳐 유닛을 이용했습니다. 이 데모는 2개의 텍스쳐 유닛만 존재할 때, 당신이 예상하듯 2번이 아닌 3번의 렌더링 패스로 돌아갑니다.

타일화된 텍스쳐를 반복하고 서로 혼합함으로써, 커다란 기본텍스쳐와 디테일 텍스쳐를 사용할때보다 더 많은 다양성을 줄수있습니다. 게다가 보기좋은 디테일을 갖기위해 엄청난 텍스쳐메모리를 사용하는것도 필요없습니다. 4개의 텍스쳐 유닛을 가진Geforce3/4 카드들로 3개의 텍스쳐를 결합할수 있고, 라이트맵용으로 한개가 남습니다. 6개의 텍스쳐유닛을 가진 Radeon8500 카드로는 5개의 텍스쳐(혹은 라이트맵을 원한다면 4개)를 혼합할수 있습니다. 보다 중요한점은, 이 모든 것을 한 벤더에 종속된 확장을 쓰지 않고도 할수 있다는 겁니다.

물론, 이 기술에 가장 큰 결점은 유용한 결과를 얻기 위해서는 적어도 3개의 텍스쳐 유닛이 필요하다는 점입니다. 그것보다 적다면, 여러 번의 패스를 가져야 겠죠.

끝으로, 여기에 제 데모를 링크합니다. 물론 소스포함입니다. 그리고 경사도와 높이에 따른(전 참조테이블을 이용했죠.) 적용율의 생성 같은, 문서에서 언급못했던 몇가지 세세한 부분에 도움을 받을수 있을겁니다. Happy texturing!


제일 위로
최종 수정 일시: 06월 20일(2007년) 07:36 PM 편집 | 정보 | 차이 | 비슷한 페이지 DebugInfo
유용한 페이지들: 분류 분류 | 자유로운 연습장 SandBox | 무작위 페이지들 RandomPages | 인기있는 페이지들 MostPopular