블로그 이미지
자신의 단점을 메꾸는 것을 단(鍛)이라 하고 자신의 강점을 갈고 닦는 것을 련(鍊)이라 하여, 두가지를 합친 것을 단련이라고 부른다. 붕대마음

카테고리

전체목록 (674)
참고사이트 (8)
Goal (4)
Travel (10)
My Life (109)
Game (35)
Game Review (7)
Game Plan (0)
Books (5)
English (1)
Optimizing (12)
Study (219)
유용한 것들_etc (45)
유용한 것들_func (20)
Unity (48)
Unreal (87)
작업장 (54)
RenderMonkey (6)
정리요망 (2)
따라잡기 시리즈 (0)
링크용 (0)
Total364,096
Today179
Yesterday188

'Study/Graphics '에 해당되는 글 43건

  1. 2020.01.05 밉맵 텍스쳐링
  2. 2019.08.16 Specular Showdown in the Wild West
  3. 2019.06.15 Clipmaps
  4. 2018.08.29 early z
  5. 2018.07.20 hlod
  6. 2018.03.10 What is Texture
  7. 2018.02.21 texture format
  8. 2018.02.13 shader에서 채널값이 모자를 때...
  9. 2018.01.31 depth-precision-visualized
  10. 2018.01.26 디더링 (Dithering)

밉맵 텍스쳐링

Study/Graphics / 2020. 1. 5. 09:40

이 글은 원문(MipMap Textureing)을 정리한 내용입니다.

원문 읽는걸 강하게! 추천합니다.


밉맵이 없다면? 

: 결점(아티펙트)가 보임/ 세밀한 부분에서 엘리어싱 발생


해결책은?

: 렌더링 하기 전에 디테일을 필터링 한다.


텍스쳐가 입혀진 오브젝트들은 뷰포트에서 다른 거리로 보여질 수 있다.

텍스쳐 이미지 에서 어느 디테일의 레벨을 사용해야 할까?

너무 높은 해상도를 사용하면 엘리어싱이 발생하고 너무 작은 해상도를 사용하면 디테일이 너무 떨어져 보인다.

그렇다면 오브젝트와 뷰포트와의 거리에 따라 디테일 정도를 다른 레벨로 사용하자!

1983년에 소개된 "Pyramidal Parametrics"라는 글에서 처음 "mipmap"이라는 단어를 사용.

mip = multum in parvo (작은 장소에 많은 것들)

LOD 문제는 텍스쳐의 피라미드를 만듦으로써 해결. (최고 해상도 텍스쳐는 피라미드 레벨0, 그 다음 레벨은 이전 레벨의 절반)

용량은 대략 1/3정도 더 필요하다.

어느밉을 쓸건지는 오브젝트가 화면에 투영된 크기를 기반으로 정한다.


밉맵 만드는 알고리즘.

1. Nearest Neighbour. (최근적 이웃 알고리즘)

   그냥 가장 가까운걸로 택한다.

2. Box Filter.

   네 텍셀의 평균값으로 픽셀값 결정

   참고 : https://mgun.tistory.com/1975  

3. Gaussian Filter

    더 자연스럽고 부드럽게 필터링 하기 위해 가우스 함수 사용.

    계산은 비싸니까 보통 필터 행렬을 만들어서 사용한다.

   참고 : https://mgun.tistory.com/1982

            https://mgun.tistory.com/1131


Texture Lookup

텍스쳐에서 색상을 가져오려 할 때 문제점.

1. 텍스쳐가 폴리곤보다 작을 때. (픽셀맵이 하나의 텍셀보다 작다. 축소)

2. 텍스쳐가 폴리곤보다 클 때. (픽셀맵이 하나의 텍셀보다 크다. 확대)

Nearest Filtering : 텍스쳐의의 중간 텍셀로 색을 결정.

Bilinear Filtering : 중복된 픽셀에 가중치를 줘서 평균화 한다.

            Bilinear은 참 좋은데 경계면(밉이 바뀌는)이 좀 구리다.

Trilinear Filtering : 구린 Bilinear을 개선하기 위한 Trilinear Filtering

            예를들면 0번레벨 *30% + 1번레벨*70% 처럼 계산한다.

OpenGL에서 보면 필터링 옵션이 여러개 있다.

GL_NEAREST

GL_LINEAR

GL_NEAREST_MIPMAP_NEAREST

GL_NEAREST_MIPMAP_LINEAR

GL_LINEAR_MIPMAP_NEAREST

GL_LINEAR_MIPMAP_LINEAR (이게 Trinear)

위의 필터링 옵션에서 보면 녹색은 텍스쳐를 샘플링할 때 쓰이는 옵션이고 주황색

밉맵을 조합할 때 쓰이는 옵션이다.


Anisotropic(이등방성) Filtering

Trinear(삼성형) 밉맵핑은 급격한 각도에서 흐리다.

Trinear과 Bilinear 필터링은 시야 관점을 고려하지 않는다.

Anisotropic 필터링은 텍스쳐에서 픽셀의 투영된 부분을 찾는다.

k anistropic 이란 텍스쳐의 k개의 샘플이 픽셀의 투영을 근사화하는데 사용됨을 의미한다.


Reference Link

- 밉맵 필터링

- 밉맵 텍스쳐링

'Study > Graphics' 카테고리의 다른 글

밉맵 텍스쳐링  (0) 2020.01.05
Specular Showdown in the Wild West  (0) 2019.08.16
Clipmaps  (0) 2019.06.15
early z  (0) 2018.08.29
hlod  (0) 2018.07.20
What is Texture  (0) 2018.03.10
Posted by 붕대마음

댓글을 달아 주세요

원문 : https://blog.selfshadow.com/2011/07/22/specular-showdown/

개인 공부를 위해 번역하였으며 원문을 읽길 적극 추천합니다.


요즘 엘리어싱 문제로 고민이 많다.

기존 사용하던 fxaa 대신  smaa로 바꿔서 사용하고 있긴 한데 그럼에도 불구하고 specular에 의한 반짝임은

도저히 고칠수가 없었다.

몇가지 아이디어를 테스트 해보기도 했다.

specular의 power, intensity를 거리 또는 pixel의 depth를 기반으로 한 감쇠정도가 그나마 괜찮았다.

그리고 구글링중 괜찮은 내용이 있어 번역했다.

번역은 적당히 했으니 원문을 추천.!!


// 이 색상은 개인적으로 추가한 내용.


말에 안장을 얹자!

이 글에서는 잘 동작하는 specular lighting을 위해 기존 방법들을 검토할 거다.

그리고 추가적으로, 현재 게임 라이팅 접근과 콘솔 메모리 제약의

간단한 변형에 대해서도 다룰 것이다.


잘 동작한다는 건 어떤 의미일까?

울퉁불퉁한 표면에 반짝이는 specular 하이라이트를 피하는 것 뿐만 아니라

먼 거리에서 올바른 외관을 얻는것에 대해 이야기 한다.

개별적인 주름과 불규칙성들은 너무 작아서 이러한 울퉁불퉁한 효과를 나타내기 어렵다.

주어진 예산으로 이러한 모든 것들을 처리 할 수 있을까?

이것들을 알아내기 위해 여행을 떠나보자!


좋음, 나쁨, 그리고 이상함.

어떤 날은 비디오게임 렌더링에 속해있는 우리들이 카우보이 무리처럼 느껴질 때가 있다.

우리는 화면에 제시간에 맞게 물체들을 올리기 위해 법칙에 맞게 빠르고 느리게 플레이 하며

우리와 아티스트가 원하는 모양을 만들기 위해 픽셀과 다툰다.


최근에 우리는 선형 라이팅(linear lighting)과 물리 기반음영모델(physically basedshading models)을 채택하여 

몇가지 잘못된 것들을 바로잡고 있었다.

이 부분은 더 발달해 있는 영화계에서조차 최근에서야 전환하고 있는 추세이다.

이러한 것들이 다 좋기는 하지만, 앞서나가거나 거친 서부시대가 끝났다고 믿기 전에, 

잘 대처해야 하는 중요한 영역이 있다 : 엘리어싱


Dan Baker가 당당히 주장했던 것 처럼, 엘리어싱은 실시간을 다루는 사람들과 오프라인을 다루는 사람들을

사이를 구별하는 중요한 요소다.

문제부분에 좀 더 많은 샘플을 할 여유는 있겠지만, 무거운 슈퍼샘플링은 우리한테는 너무 느리다.

(실제로 나는 링크4의 내용을 구현해 보지는 않았지만, 현재 콘솔에서 잘 동작하지 않을 것이라 생각한다)

MSAA 또한 외곽 엘리어싱만 처리하니 효과적이지 않고, 음영의 샘플링 내부에서 아티펙트가  생긴다.

specular 반짝임이 대표적인 예다.

후처리 AA(현재 많은 잠재적인 옵션이 있음, 링크5 참고)는 카메라나 물체가 움직일 때 

생기거나 사라지는 선명한 하이라이트를 해결하지 못하기 때문에 실제로 도움이 되지 않는다.

마지막으로, temporal AA가 해결책이 될지도 모른다고 생각하겠지만, 이는 시간적 일관성에 대한

의존도가 높아짐에 따라 프레임 전반에 걸친 저렴한 수퍼 샘플링일 뿐이다.


요약하자면, 우리가 게임에서 사용하는 기본적인 AA기술은 반짝이는 것을 막는것에, 

특히 높은 specular power일 경우에서는 그다지 쓸모있지 못하며 우리가 원하는 

거리를 기반으로 우리가 원하는 결과를 만들어 내지 못한다.

성능부분은 제외하고, 커스텀 텍스쳐 필터링과 함께 난해한 수의 샘플을 사용하지 않는다면 

수퍼샘플링조차도 부족하다고 의심되며, 그렇지 않다면 bump정보는 평균화되어 사라진다.

(전체적으로는 아니지만 나중에 자세히 설명)


다른 선택지가 있을까?

예전 컨퍼런스에서, Dan은 일반적으로 개발자들이 사용하는 두가지 해결방법에 대해 다루었는데,

거리에 따라 울퉁불퉁함(bumpiness)이나 광택(glossiness)를 줄이는 것이다.

첫번째 방법인 bump를 줄이는건 우리가 원하는 것과 반대의 개념이기 때문에 실제로 잘못되었고,

카메라에서 멀어질 때 울퉁불퉁한 표면이 흐릿해 보이게 하는 것 보다는

법선맵을 평평하게 하는 것이 보다 더 광택이 생긴다.

대신에, specular power를 (첫번째 근사치) 줄이는 것은 옳은 방법이다.

비록 경우에 따라 조정(tweak)해야 하고 울퉁불퉁지역과 평평한 지역 모두에서 normal map이

정확하게 동작하지는 않지만, 단순한 엘리어싱을 사용하거나 높은 power를 피하는 것 보다는 낮다.

텍스쳐 공간 라이팅에 대해서 이야기 하고 싶지만, 이는 자체적으로 문제가 있는 

또 다른 중대한 대안이므로 여기서는 더이상 논의하지 않겠다.


모두들, 제대로 동작하지 않는 스펙큘러와 못생긴 반짝임을 그냥 체념해 버려야 할까?

만약 마을에 보안관이 새로 온다면 아닐지도 모르지....


거리 청소

LEAN Mapping은 각계각층의(확대율에 예외가 있을수 있는데 이 부분에 대해선 나중에 좀 더 이야기 함)

specular 하이라이트를 강력하게 필터링 하기 위한 최신 접근 방식이다.


이 기술은 거리에 따라 표면의 거친정도의 거시적 효과를 잘 나타낼 뿐 아니라, 

-심지어 능선이 있는 법선맵 (ridged normal map)으로부터 이방성 하이라이트를 생성-

런타임에 동적 범프 레이어 결합을 지원한다. (부하는 좀 더 들지만 더 자연스러워진다.)


실제로 어떻게 동작하는지 자세히 알아볼 여유는 없지만, -자세히 보고자 한다면 아래 링크를 참조-

수많은 이전 기술들에 비교해 글의 말미에 있는 해결책이 좀 더 현실적이라는건 명확하다.

어쨌든 Civilization V(문명 5)에 적용되었고, 내가 경험해 보기로는 어느정도 인상적인 결과였다.


그렇긴 하지만, 바로 광범위하게 적용하기에 몇가지 문제점이있다.

- 많은 저장공간이 필요.

- 이방성, 접선공간 Beckmann 공식


우선 저장공간에 대한 요구사항은 지금당장 많은 개발자에게 제한요소가 될 것이다.

Lean매핑은 표준 normal map대신 두개의 텍스처가 더 필요하며(그리고 아마 레이어를 결합하기 위해 더 필요할지도..),

Variance Shadow Maps과 비슷한 이유인 성가신 정밀도 조건에 의해 오버헤드가 발생한다.

Firaxis는 경우에 따라 채널당 8비트 저장공간으로 압축 작업을 수행했지만, 논문에서는 아래와 같이 조언했다.

"일반적으로, 8비트 텍스쳐는 속도나 공간에 절대적으로 필요한 경우에만 의미가 있다."


당연하게도, 이 기술은 현재 콘솔에서 normal map을 위한 가장 일반적인 크로스 플랫폼 형식 중 하나 인

DXT1 또는 DXT5에서 사용할 수 없다.

이에 비해, 저장공간 비용은 최소 8배가 될 수 있다.( 핵심 LEAN 조건에서 normal이 복구되고 

다른 데이터가 그 자리에 포함 된 경우)


지연(deffered) 렌더링을 쓸 경우 더욱 거칠어지는데,

G버퍼의 공간은 일반적으로 부족하며 설상가상으로 조명 공식은 더욱 복잡해 진다

모든것을 공통 공간(예를 들자면 월드 공간같은)으로 옮길 수 있다고 가정하더라도,

여전히 추가 접선 벡터를 저장해야 한다.

또한 LEAN 매핑과 관련된 수학적인 내용이 더 있으며 이는 환경에 라이트를 드리우기 위해 

어떤 선택을 할지와 별개로 고려해야할 또 다른 중요한 사항이다.


그래서 전반적으로 보자면, 대체품이 처음 나타나는 것은 아니다.

다행스럽게도 보안관은, 더 빠른 draw가 가능한 대리자가 있다.

올해 GDC에서 Dan은 저렴한 버전인 Clean(저렴한 LEAN) Mapping을 발표했다.

낮은 저장 공간(대략 절반정도)과 약간 더 높은 퍼포먼스를 위해 이방성을 희생한다.

이는 중요한 개선이며 이방성을 잃으면 효율적으로 두번째 문제를 해결하지만,

DXT에 대한 영역은 여전히 내 취향에 맞지 않는다.


내가 정말 좋아하는 것은 예술품들을 심각하게 "재평가"할 필요 없이 자유롭게 적용하는 것이다.

(그리고 물론, 다른 부분에서 삭감하는 것을 의미한다.)

내 말을 오해하지 마라, LEAN 매핑은 실제로 흥미로운 진보라고 생각한다.

나느 현 세대의 콘솔에서 와일드 한 것을 포기하는 현 상태로 사용하는 것을 기대하지는 않는다.

그러나 문명5의 물의 경우 처럼 특정한 상황에서 적절히 사용하면 훌륭한 옵션이다.


더 저렴한 방법이 필요하다.

투자대비 더 높은 효율을 얻을 방법이 있을까?

실제로 2004년에 Michael Toksvig가 LEAN 매핑과 비슷하게 밉매핑의 이점과 하드웨어 텍스쳐 필터링을 활용하지만

이미 존재하는 normal map에서 저장되어 있는 normal 벡터의 평균 길이로부터 직접 범프 편차를 측정하는

매우 간단한 기법을 제시했다.

일반적으로 명명된 이름이 아직 없기에 그냥 Toksvig AA라고 부르겠다.


문서에서, 기존 블린 퐁 공식은 아래와 같다.



Na는 텍스쳐에서 읽은 평균법선값이며, 이로부터 Toksvig Factor라고 부를 ft값을 얻는다.

그리고나서 스펙큘러 지수(specular exponent)인 s를 조절하는데 사용하고, 이 값(s)은 분산에 따른

전체강도를 조절한다.(거친정도)


여기 위 식을 간단하게 트윅한 코드버전이 있다.

float len = length(Na);

float ft = len/lerp(s, 1, len);  // 텍스쳐의 평균값(Na)이 1에 가깝다는 말은 각 텍셀에 저장된 벡터의 방향이 비슷하다는 뜻.

float scale = (1 + ft * s) / (1 + s);

float spec = scale * pow(saturate(dot(Na,H))/len, ft*s);

// 결국 스펙큘러 지수를 평균법선값을 기반으로 조절하겠다는 뜻.


원래 버전에서는 이 모든것들이 dot(Na, H)와 dot(Na, Na)로 인덱스화된 2D LUT에 저장되는데,

이 경우 Clamp 텍스쳐 주소 모드는 saturate()를 처리해 준다.

LUT는 픽셀 셰이더 2.0 이하 버전을 쓰던 옛날에는 의미가 있었지만, 현재 우리는 셰이더에서

그냥 바로 판별 할 수 있기 때문에 스펙큘러 지수당 LUT는 필요 없고, 그럼 으로 인해 

잠재적인 캐시 와 정밀도 문제를 피할 수 있다.


방정식의 첫번째 부분인  또한 한 방향으로만 에너지 보존을 처리하기 때문에 

약간 가짜의 느낌이 나며, 1은 실제로 2가 되어야 한다고 확신한다.

내가 정말 원하는건 블린 퐁을 정규분포함수(NDF)로 취급하는 것이다.



이미 에너지 보존 블린 퐁을 사용하는 사람이라면 이 공식은 익숙하고 우아하게 보일 것이다.

스펙큘러 지수를 조정하면 모든것이 잘 동작할 것이다.


이는 명확해 보이긴 하지만 디퍼드 렌더링의 경우 초기 화면 패스에서 이 작업을 수행해야 한다.

그러므로 모든 라이팅 대신에  G버퍼에 지수를 팩킹하기 전에 한번만 스케일링 해야 한다.

이는 또한 뷰 공간에서 BFN(best fit texture인듯) 또는 두개의 구성요소 같이

법선의 영리한 G버퍼 인코딩을 자유롭게 사용할 수 있다는 것을 의미한다.


불행히도, 이는 Toksvig AA의 중대 문제점을 발생한다.

3Dc 및 (일반적으로)DXT5와 같은 두개의 입력성분을 사용하는 법선에서는 사용할 수 없다.

이 기술의 전체 기조는 지역 거칠기(분기하는 법선)는 필터링된 법선의 길이에 의해 대략적으로 포착되므로 

단위벡터를 재 구성하는 인코딩으로는 이를 수행하는 것이 좋지 않다.

또한 DXT1을 노멀맵에만 독점적으로 사용하더라도, 압축은 여전히 벡터 길이와 혼동된다.

(DXT의 압축방식이 벡터길이에 영향을 끼쳐 Toksvig AA의 결과에 영향을 끼친다는 말이듯..)

재 정규화 후 조명에 대해 허용되는 결과가 종종 나오기 때문에 이 사실을

잊어버리기는 쉽지만 실제로 Toksvig AA에서는 혼란스러울 수 있다.

(어차피 ~1로 정규화 하고 그 결과는 결국 라이팅에서 허용하는 결과값이기때문에 이상함을 못느낄수 있다는 말인듯..)

이 모든것을 감안해 보면 개발자가 이 기술을 채택하지 않은게 놀라운 사실은 아니다.


이는 또 다른 막다른 골목이었을까?

실제적으로 전혀 아니다. 해결책에 좀 더 가까워졌다.


야생적 직감

사실 간단한 아이디어가 하나 있다.

Toksvig과 LEAN 매핑은 텍스쳐 필터링을 사용하니까 이를 오프라인으로 만드는 것이다.

그림1의 법선맵에서 각 밉 레벨의 가우시안 필터링된 버전으로부터 Toksvig Factor을 

미리 계산해서 가져올 수 있다면 어떨까?

그림 1 : 법선 맵


결과는 그림 2에 나와있으며 무처 직관적이다.

원본 법선맵에서 평평한 부분은 흰색(광택), 지글거리고 울퉁불퉁할 수록 어두워진다.

그림 2 : Toksvig map


올바른 커널 사이즈를 가진 이 Toksvig map은 실시간 Toksvig 메소드와 실제로 잘 어울리며

이를 구현한 실제 데모는 글의 마지막에서 볼 수 있다.


그림 3a : Toksvig AA 확대 관찰

그림 3b : Toksvig map 확대 관찰


우리가 실제로 가지고 있는 것은 자동 생성된 안티 엘리어싱 gloss 맵이며 LEAN 용어와 달리 실제로 잘 압축된다.

또한 이 맵이 아트 임포트 파이프라인의 일부로 생성되면 선택한 압축 방법을 사용하기 전에

고 정밀도 3성분 법선을 무료로 사용할 수 있다.


이 맵을 사용하는 것은 다른 gloss map을 사용하는 것과 마찬가지로 어렵지 않다.


1
2
3
4
float ft = tex2D(gloss_map, uv).x;
float p = ft*s;
float scale = (p + 2)/8;
float spec = scale*pow(saturate(dot(N, H)), p);


하지만 이 글을 주의깊게 읽은 독자라면 Toksvig Map이 특별한 스펙큘러 파워를 염두에 두고 생성되었음을 알 수 있다. 

다행히도 100정도의 값으로 잘 동작하는 것처럼 보이는 적당한 콘트라스트를 제공하는 고정된 power로 

텍스쳐를 만들 수 있고, 유연성이 필요한 경우 나중에 다시 변환 할 수 있다.(예를 들자면 텍스쳐 재사용이나 재질 속성 변경)


ft /= lerp(s/fixed_s, 1, ft);

다른 방법은 적절한 power인  를 Naty Hoffman에 의해 제안된 최대 스펙큘러 지수(범위 0~1)로서 저장하는 것이다.

이 로그 공간 광택 용어는 아티스트가 보다 직관적으로 작업할 수 있으며,

지수에 대한 별도의 머티리얼 속성이 필요하지 않고, 게다가 또한 직접 G버퍼 저장을 위한 편리한 포멧이다.

세번째 방법은 분산 또는 법선의 길이를 대신 저장하고 나머지 계산은 실시간으로 수행하는 것이다.


데이터 자체는 스펙큘러 마스크와 함께 팩 되거나 기존 gloss맵을 조정하는데 사용된다.

심지어 아티스트가 그림을 그리는 시작점으로 직접 사용가능한지도 생각해 봤지만, 이럴경우

엘리어싱이 다시 나타나는 것을 방지하기 위해 주의가 필요하다.


물리 기반 셰이딩 측면에서 gloss 맵이 스펙큘러 마스크보다 더 중요하지만, 아티스트들은

일반적으로 후자에 더 편하다.

게다가, 에너지 보존 스펙큘러로 가능한 높은 강도의 범위에서 안티 엘리어싱은 보다 더 치명적이다.


이러한 이유로 인해 자동 생성되는 텍스쳐에 대한 아이디어는 아직 결과물에 대해 모두 

테스트 해보지는 않았지만 실제로 매력적인 아이디어라고 생각한다.


나는 작업의 80~90프로를 자동으로 해주는 아트 툴을 무척이나 좋아하지만,

여전히 직접 들어가서 결과물을 트윅(조정)할 수 있는 방법을 제공한다.

결과적으로 머리카락을 덜 당기고 폴리싱에 더 시간을 할애 할 수 있다.

이것이 내가 원하는 방식이다.


OK목장의 결투

Toksvig Mapping은 Lean Mapping과 무었이 다른가?

글쎄, 이방성(anisotropy) 부분이 부족한 만큼 확실히 좋지는 않으며,

어떤 경우에는 스펙큘러 하이라이트가 과도하게 넓어지고, 또 어떤 경우에는 엘리어싱 얼룩들을 남긴다.

하지만 Clean Mapping을 대신 미리 저장하는 것이 가능하며, 보수적으로 감쇠하는 경향이 있기 때문에

약간의 하이라이트 블룸이 발생하는 비용으로 나머지 반짝이는 것들을 줄인다.


우리가 이것을 할 수 있는 이유는 Toksvig와 Clean을 행하는 데 있어 서로를 분리해 주는

가장 중요한 것이 분산의 측정이기 때문이다.


Toksvig Maps과 관련해서 앞서 언급한 모든것(베이킹, 평가, power 간 변환 등)도 마찬가지로 여기에 적용된다.

LEAN을 베이킹하는데 성공했지만 다른 이야기를 하겠다.


LEAN 매핑은 기본 사실에 더 가깝지만 미리 구운 Toksvig/CLEAN 매핑은 여전히 바로 지금 

대부분의 사람들이 하는것 처럼 아무것도 하지 않는 것보다는 훨씬 낫다.


어서 가요, 카우보이!

여기에 표준 블린 퐁(기본)과 Toksvig AA및 Toksvig Map 를 전환할 수 있는 간단한 WebGL 데모가 있다.

셰이더 코드도 바로 편집할 수 있다.


데모 업데이트 : Toksvig Map 옵션과 NVIDIA GPU와 관련된 시각적 문제에 대한 보고가 있었다.

Mip맵이 없는 것처럼 보이면 최신 드라이브로 업데이트 하면 문제가 해결된다.


블린 퐁으로 반짝거리는 것 외에도 마우스 휠로 줌 아웃 할 때 울퉁불퉁한게 사라지는 것 처럼 주전자가

어떻게 반짝이는지 확인해라.

대조적으로, 머티리얼은 Toksvig의 외관을 유지한다.


필터링 프로세스를 보여주는 또 다른 작은 예제도 만들었다.







아래 내용은 렌더몽키로 테스트 해 본 내용.

기존 블린 퐁 방법


Tobsk 방법


Tobsk.rfx


nvidia거 : 

ToksvigFactorComparison.zip


Reference Link

중요한 링크들은 원문에 대부분 다 포함되어 있고 아래 링크들은 추가로 달아둔 링크.

원문의 링크도 꼭 한번 다 읽어보길 추천.

1. energy conserving Blinn-Phong

2. best fit normal map generator + source code

3. Selective supersampling

'Study > Graphics' 카테고리의 다른 글

밉맵 텍스쳐링  (0) 2020.01.05
Specular Showdown in the Wild West  (0) 2019.08.16
Clipmaps  (0) 2019.06.15
early z  (0) 2018.08.29
hlod  (0) 2018.07.20
What is Texture  (0) 2018.03.10
Posted by 붕대마음

댓글을 달아 주세요

Clipmaps

Study/Graphics / 2019. 6. 15. 14:55

원문 : 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

- D3D10_TEXTURE2D_DESC msdn

- Clip Mapping

'Study > Graphics' 카테고리의 다른 글

밉맵 텍스쳐링  (0) 2020.01.05
Specular Showdown in the Wild West  (0) 2019.08.16
Clipmaps  (0) 2019.06.15
early z  (0) 2018.08.29
hlod  (0) 2018.07.20
What is Texture  (0) 2018.03.10
Posted by 붕대마음

댓글을 달아 주세요

early z

Study/Graphics / 2018. 8. 29. 16:27

Early Z : 

- Z Test(Depth Test, 깊이 판정)를 미리 해서 비싼 Pixel연산을 피하자

- 일종의 픽셀단위 Culling 기법.


Z Test :

- 깊이 판정은 픽셀의 깊이 값을 깊이 버퍼의 깊이 값과 비교해서 픽셀의 가시성을 판단한다.

- 현재 픽셀이 다른 픽셀의 뒤에 있는지를 판단하여 뒤에 있다면 픽셀을 폐기하고,

  앞에 있다면 그 픽셀을 다음 단계로 넘겨 값을 z버퍼에 기록한다.


그렇다면 기존 Z Test의 순서는?

- 간단하게 말하자면 pixel shader(fragment shader) 다음.

   자세하게 말하자면 파이프 라인 참고.

- 왜 깊이판정을 ps 이후로 했을까?

- ps 연산에 따라 depth가 변경될 수 있기 때문에 ps 앞에서 하지 않음.


Early Z를 위해서는?

- 기존 하드웨어 동작방식이 바뀌는 거기 때문에 하드웨어에서 지원해 줘야 한다.

- Coarse-grained Z Culling vs Fine-grained Z Culling

- Depth Stencil buffer를 사용

- Early Z 비활성화 (Z buffer Optimization, 10page)

  - 그래서 ps에서 depth 값을 write(갱신)한다면 비활성화 된다. 

    (ps에서는 원하는 depth을 저장해야 하니)

  - ps에서 discard 하면 비활성화 된다.

    (ps에서는 버리려는데 write 할 수 있으니)

  - alpha-test을 쓸 경우 비활성화 된다.

    (discard와 마찬가지)

- 결국 ps에서 depth는 왠만하면 건들지 마라.


Early Z의 구현은?

- 하드웨어적으로 지원하고 적용될 수 있는 상황이라면 하드웨어에서

  내부적으로 사용되며 다른 멀티패스 기술(z pass 후 color pass)과 관련이 없다.

- OpenGL 이나 D3D 어디에서도 early z 작업을 지정하지 않는다.

  이 모든 작업은 하드웨어 관련 작업이며 사용 또는 미사용 설정은 할 필요 없이 

  하드웨어에서 자동으로 처리된다.

  즉, early z 는 실행가능한 조건충족하면 하드웨어가 알아서 지원한다.

- Depth/ Stencil buffer 사용

- Early Z를 하드웨어가 실행하는 조건을 충족시켜 주기 위해 사용하는 Early-Z Pass

- 1st pass에서 depth rendering, 2st pass에서 depth write 비활성화 하고 rendering.



Reference Link

- early-z를 이용한 pixel shader 최적화

- everything about early-z

- 3dmp - early z

- early z visibility test (early-z)

- early z and discard

- why discard pixel take a noticeable performance hit?

- 픽셀 셰이더의 기초

- wiki, HyperZ

- Applications of Explicit Early-Z culling, pdf

- Hierarchical Z-Buffer Visiblility

- microsoft early-z

- Triple Depth Culling

- Saving the Z-Cull Optimization

- Early Fragment Tests, Hi Z, Depth, Stencil and other benchmarks

- Early Fragment Test

- You Can Never Have a Custom Z Buffer Distribution and Early Z at the Same Time

- Early-Z

'Study > Graphics' 카테고리의 다른 글

Specular Showdown in the Wild West  (0) 2019.08.16
Clipmaps  (0) 2019.06.15
early z  (0) 2018.08.29
hlod  (0) 2018.07.20
What is Texture  (0) 2018.03.10
texture format  (0) 2018.02.21
Posted by 붕대마음

댓글을 달아 주세요

hlod

Study/Graphics / 2018. 7. 20. 11:59

원문 : http://www.cs.unc.edu/~walk/hlod/

원문의 내용을 필요부분 번역하였습니다.

원문을 보길 강력추천합니다.


그림 1. Double Eagle 대형선박. 이 모델은 126,630개 오브젝트, 그리고 82,361,612 삼각형으로 구성되어 있다.


전통적인 LOD 방식으로는 한번에 하나의 객체만 단순화 할 수 있다.

그래서 여러 객체를 포함하는 화면이 주어지면 이 방식은 장면 전체가 아닌

개별 객체에 대해서만 에러를 최소화 할 수 있다.

HLOD(Hierarchical Level Detail, 계층적 lod)는 전통적인 방식의 LOD를 오브젝트의 

계층적 집합체로 일반화 하는 것이다.

장면의 개별 부분을 같이 단순화 하는 HLOD를 생성하여, 특히 적극적인 단순화를 위해

좀 더 괜찮은 전역 근사값을 생성한다.

원하는 목표 폴리곤 갯수에 따라, LOD는 화면에서 모든 오브젝트들을 렌더하지 못할 수도 있어 균열을 만들수 있다. 

그림 2. Double Eagle의 LOD. 7,887개, 1,922의 페이스로 구성.


HLOD는 보다 견고한 근사값을 제공한다. (그림 3 참고)

그림 3. Double Eagle의 LOD. 7,710개, 1,914의 페이스로 구성.

동적 장면에서, 가능하다면 동적으로 HLOD를 비동기로 계산하여 병렬 하드웨어의 이점을 얻는다.


중요 

충실도 : 오브젝트를 그룹화하여 HLOD를 만들므로 시각적 품질이 좀 더 많이 근사치만큼 올라간다.

자동 생성 : 사용자 개입없이 장면 그래프(scene graph)의 HLOD를 계산한다.

보편성 : 위상 정보나 표현에 대해 아무런 가정을 하지 않는다.

효율성 : LOD는 정적이므로 디스플레이 목록을 사용하여 렌더링 할 수 있다.

유연성 : HLOD 장면 그래프 구조는 일정한 프레임 속도 모드이미지 품질 모드로 렌더링 할 수 있게 해 준다.


HLOD 만들기

기존 방식의 LOD는 환경에서 단일 노드의 지오메트리를 나타내지만, HLOD는 장면 그래프의 전체 분기 또는,

다중 노드의 지오메트리를 나타낸다.

장면그래프를 사용해서 환경을 표현하고 먼저 각 노드에 대한기준 LOD를 계산한다.

그리고 나서 각 노드에서 해당 LOD를 기반으로 상향식으로 HLOD를 계산한다.

- 단말 노드(leaf node, 맨 마지막 끝 노드)의 HLOD는 그 노드의 LOD와 같다.

- 장면 그래프에 있는 내부 노드의 HLOD는 노드 자체에 속한 모든 LOD를 하위 노드의 HLOD와 

  결합하여 계산된다.


HLOD 렌더링

기존 LOD 렌더링 시스템에서는, 출력 알고리즘이 장면그래프의 모든 오브젝트나 노드에 대해

적절한 LOD를 렌더링 한다.

노드의 HLOD는 해당 노드의 지오메트리 뿐만 아니라 하위 노드까지의 근사치 이기 때문에,

장면그래프를 순회하면서 HLOD를 렌더하면 굳이 하위노드는 순회 할 필요가 없다. (그림 4 참고)

이렇게 하면 적절한 시점에 단지 HLOD를 렌더링함으로써, 장면의 전체 부분을 적극적으로 제거할 수 있다.

이미지 품질 모드에서, HLOD와 연관된 오차범위가 화면공간에 투영된 오류제약조건을 만족시킬 때 까지

장면 그래프를 순회하여 렌더링 한다.

목표 프레임 속도 모드에서, 시간적 제약보다 더 많은 폴리곤을 렌더링 할 때 까지 

최대한의 투영 오차를 가지는 가장 조잡한 HLOD를 만든다.

그림 4. 얼굴 모양을 만들기 위해 LOD와 HLOD를 사용하여 렌더링. 

얼굴에서 시작하여 장면그래프를 순회하는 알고리즘이다.

시야가 멀기 때문에, HLOD 0을 사용하여 얼굴모양을 그린다.

이 HLOD는 전체 장면그래프를 나타내므로 시스템은 순회를 멈춘다.




Reference Link

- Unity AutoLOD

- Unreal HLOD

- 번역 원문

- 3D Engine Design for Virtual Globes, page 371

'Study > Graphics' 카테고리의 다른 글

Clipmaps  (0) 2019.06.15
early z  (0) 2018.08.29
hlod  (0) 2018.07.20
What is Texture  (0) 2018.03.10
texture format  (0) 2018.02.21
shader에서 채널값이 모자를 때...  (0) 2018.02.13
Posted by 붕대마음

댓글을 달아 주세요

What is Texture

Study/Graphics / 2018. 3. 10. 15:14

What is Texture란 글을 쓰게 된 이유는 요즘 게임을 개발하다 보니,

Texture을 정말 다양한 용도로, 그리고 다양한 방법으로 쓰게되서

Texture에 대해 여러가지로 정리하는 시작점을 가지고 싶었기 때문이다.


Texture란 무었인가?

이 질문에 나름 모범적인 대답은 아래와 같다.

3d 그래픽에서 폴리곤에 다양한 색상이나 질감을 표현하기 위해 사용되어지는 2D 이미지

게임에서 사용되는 텍스쳐는 내가 생각에는 데이터를 저장하는 storage 라고 생각한다.

Texture에는 폴리곤에 색상이나 질감을 표현하기 위해 많이 사용하기도 하지만, 다른 용도로도 쓸 수 있기 때문이다.

화면톤을 위해 사용할 수도 있고, 계산하기 복잡한 특정 수치값들을 미리 저장해서 쓸수도 있다.


그래서 텍스쳐란 무었인가? 와 무었을 위한 텍스쳐인가? ,를 고민해 봐야 한다.

아래 Texture types 을 참고하면 여러가지 텍스쳐의 쓰임을 알 수 있다.


Reference Link

- Texture types

- Unity Texture compression and optimization

- UV Texture Coordinates and Texture Mapping - OpenGL / DirectX

- tangent space, 접선공간

- DXT 압축

- Texture Format


- 모바일 게임 최적화의 정석 - 텍스처 압축 편

- Surface Texture Mapping

-


'Study > Graphics' 카테고리의 다른 글

early z  (0) 2018.08.29
hlod  (0) 2018.07.20
What is Texture  (0) 2018.03.10
texture format  (0) 2018.02.21
shader에서 채널값이 모자를 때...  (0) 2018.02.13
depth-precision-visualized  (0) 2018.01.31
Posted by 붕대마음

댓글을 달아 주세요

texture format

Study/Graphics / 2018. 2. 21. 17:48

texture에는 file format 이 있고 pixel format이 있다.

파일 포멧이야 파일의 확장자를 보면 알수 있고 이 파일들 마다 독특한 성질을 가지고 있다.

몇가지 예를 들어 보면 tga, bmp, jpeg, png, dds, psd, gif, etc... 가 있다.

각각 데이터의 압축, 압축시 손실유무, 특정 픽셀포멧 지원여부 등의 기능을 따진다.

픽셀포맷은 사용되는 형식에 따라 채널과 비트수, 부호, 부동소수점 등으로 사용된다.

아래 링크에서 각 텍스쳐 포멧에 대한 추가적인 내용을 알 수 있다.


Reference Link

- Difference between floating point render target and normal render target

- Floating point texture가 왜 필요한가

- 텍스쳐 포맷과 기초

- Display color와 Digital texture format의 이해

- Direct3D Surface Format

- Khronos Group, Image Format

-

'Study > Graphics' 카테고리의 다른 글

hlod  (0) 2018.07.20
What is Texture  (0) 2018.03.10
texture format  (0) 2018.02.21
shader에서 채널값이 모자를 때...  (0) 2018.02.13
depth-precision-visualized  (0) 2018.01.31
디더링 (Dithering)  (0) 2018.01.26
Posted by 붕대마음

댓글을 달아 주세요

텍스쳐마다 채널이, R/ RG/ RGB/ RGBA 등이 있는데 가끔 채널 하나가 더 필요할 때가 있다.

예를들어 R채널에는 Diffuse Mask, G채널에는 Rim Mask, B채널에는 Specular Mask, A채널에는 depth를 

넣는데 추가로 offset값을 저장해 줘야 할 때가 있다. 

그럴때 가끔 fmod를 사용해서 하나에 두개의 값을 넣게 하는 방법을 쓴 적이 있다.

z값(B채널)이 0.0f ~ 1.0f 사이이고 offset 값이 1.0f에서 10.0f 사이라면 

z값에 기존값 * 0.01f하고 offset 값을 *0.1f 해서 더한 후 다음 패스에서 이 값을 fmod로 z값을 구한 후

기존값에서 빼면 z값만 구할수 있긴 하다. 다만 offset 값이 1.0f~10.0f 조건에 추가로

단차가 0.1f 단위여야 한다는 조건이 붙는다.

1.1f는 가능하지만 1.01f는 기존값고 정밀도를 더 나눠써야 한다.

가끔 이런방식으로 작업을 해서 쓰긴 하는데 fmod자체가 그렇게 단순 연산만큼 가볍지는 않지만

텍스쳐 한장 더 써서 한번 더 찍어야 하는 상황에서는 나름 괜찮았던것 같다.


float2 fmod(float2 a, float2 b)

{

  float2 c = frac(abs(a/b))*abs(b);

  return (a < 0) ? -c : c;   /* if ( a < 0 ) c = 0-c */

}


Reference Link

- fmod

-

'Study > Graphics' 카테고리의 다른 글

What is Texture  (0) 2018.03.10
texture format  (0) 2018.02.21
shader에서 채널값이 모자를 때...  (0) 2018.02.13
depth-precision-visualized  (0) 2018.01.31
디더링 (Dithering)  (0) 2018.01.26
Texture types  (0) 2017.02.05
Posted by 붕대마음

댓글을 달아 주세요

원문을 보시는 것을 추천드립니다. 


깊이 정밀도 시각화


깊이 정밀도는 모든 그래픽스 프로그래머가 조만간 고민해 봐야 하는 골칫거리다.

많은 아티클과 논문에서 이미 이 주제가 다루어졌고, 서로 다른 게임들, 엔진들,

그리고 디바이스들에서 다양한 깊이버퍼 포멧과 설정을 볼 수 있다.


원근투영과의 상호작용하는 방식 때문에 GPU 하드웨어 깊이 매핑은 다소 난해하며

방정식을 연구한다고 해서 바로 상황이 명확해 지지는 않는다.

어떻게 동작하는지 알기 위해서는 몇장의 그림을 그려보는게 좋다.


이 글은 3개의 메인 파트가 있다.

첫번째 파트에서는, 비선형(nonlinear) 깊이 매핑을 하는 이유에 대해 다룬다.

두번째 파트에서는, 서로 다른 상황에서 비선형 깊이 매핑이 어떻게 수행되는지 이해를 돕는

직관적이며, 시각적인 몇개의 도표를 제공한다.

세번째 파트에서는 Paul Upchurch와 Mathieu Desrun이 2012년도에 제시한 "원근 렌더링의 정밀도 강화"의

주요 결과인 부동 소수점(floating-point) 반올림 오차가 깊이 정밀도에 미치는 영향에 관한 대한 및 재연을 다룬다.


왜 1/z 를 하는걸까?


GPU 하드웨어 깊이 버퍼는 일반적으로 카메라에서 오브젝트까지의 거리를 선형 방식으로 저장하지 않으며,

이는 사람들이 일반적으로 처음 이 부분을 접했을 때 생각하는 방식과 반대다.

그 대신, 깊이버퍼는 월드공간에서의 깊이값에 반비례하는 값을 저장한다.

왜 이렇게 하는지에 대해 간단히 알아보자.


이 글에서, 깊이 버퍼[0~1]에 저장되어 있는값을 d, 월드공간에서의 깊이(즉, 뷰 축에 따른 거리)를 z

사용할 것이며 월드공간에서의 단위는 미터(meter) 같은 거다.

일반적으로 이들의 관계는 아래와 같은 모양이다.



위 식에서 ab는 근거리 평면(near plane)과 원거리평면(far plane)설정 값이다.

다시 말하자면, d는 항상 1/z의 선형으로  재설정(remapping) 된다.


표면상으로는, d를 얻는게 z에 대한 어떤 함수가 될 거라고 생각할 수 있다.

왜 이렇게 특이하게 만들었을까?

이에 대해서 두가지 중요한 이유가 있다.


첫번째로, 1/z 은 원근 투영의 구조에 자연스럽게 맞아떨어진다.

이는 거의 대부분의 변환 범용 클래스가 직선을 유지할수 있도록 보장해 주며,

하드웨어 레스터화가 편리하도록 만들고, 그 후에 화면공간에서 삼각형의 직선 테두리들(straighte edges)이

직선으로 남아있을 수 있도록 해 준다.

하드웨어가 이미 수행하고 있는 원근 나누기(perspective divide)를 이용하여 1/z의 선형 재설정을 만들 수 있다.

각주 : 위에서 1/z가 원근 투영의 구조에 자연스럽게 맞아 떨어진다는 말을 생각해 보자.

원근투영이란 3D를 2D에 투영할 때 원근감을 느낄 수 있도록 하는 투영방법을 말한다.

그렇다면 1/z는 무었일까? 원근감의 핵심은 가까운것은 크게, 먼 것은 작게 보이도록 하는 소실점을 사용한다.

이 소실점을 표현하기 위해서는 어떻게 해야 하는가?

눈(카메라)으로부터 떨어진 거리(z)로 각 값(x,y)을 나누어 주면 된다.

그렇게 되면 z 값이 클 수록(멀어질수록) 물체의 위치는 정점 부근으로 모이게 되며 삼각형을

예로 보면 삼각형을 이루는 세 점 또한 직선을 유지하게 된다.

위의 글은 아마 이 개념을 이야기 하는 것 같다.

동차 좌표계에서 z 값은 z/w이며 위 행렬에서 보면 zc = bz + a, wc = z 이다.

그러므로 zc/wc = (bz+a)/z 가 된다.


물론 이러한 접근 방식의 강점은 투영행렬이 다른 행렬들과 곱해질 수 있다는 점이며,

이는 곧 여러 변환 단계들을 하나로 조합할 수 있다는 뜻이다.


두번째 이유는 Emil Persson이 언급했듯이, 1/z값이 화면공간에서 선형이라는 점이다.

그래서 래스터화를 하는 동안 삼각형에 대해 d를 보간하는 것이 쉽다.

그리고 계층적 Z버퍼(Hierarchical Z-buffer), 초기 Z제외(early Z-culling),

깊이버퍼 압축(depth buffer compression)과 같은 작업들이 모두 수행하기 용이해 진다.


깊이맵(depth map) 그리기.


공식은 어려우니까 그림을 우선 보자.


위 그림은 왼쪽에서 오른쪽으로, 그리고 아래방향으로 보면 된다.

왼쪽 축에 표시되어져 있는 d에서 부터 보자.

d가 위에서 이야기 되었던 내용대로 1/z의 임의 선형 리매핑이 될 수 있으므로,

이 축에서 원하는 위치에 0과 1을 배치할 수 있다.

위 그림에서 눈금 표시는 각각의 깊이 버퍼값을 나타낸다.

설명을 위해, 4bit 정규화된 정수형 깊이버퍼를 시뮬레이션 해 보면, 위와 같이 16개의 균등한 간격의 눈금이 생긴다. (세로축)

세로축을 눈금에서 가로로 1/z 곡선을 연결해서 아래로 내려보면, 그 개별 값들이 월드공간 깊이 범위이다.

위의 그래프는 d3d 및 유사 API들에서 사용되고 있는 전형적인 깊이매핑의 "표준" 을 보여준다.

그림을 보면 1/z 그래프에서 가까운 평면(near plane)쪽에 값들이 몰려서 연결되어 있고 먼 평면(far plane)으로 갈수록

값이 분산되어 있다는 것을 알 수 있다.

또한 near plane이 왜 깊이 정밀도에 큰 영향을 끼치는지를 알 수 있다.

near plane을 당기면 d 범위가 1/z 곡선의 점근선 방향으로 급등하게 만들 수 있으며, 위 그림 보다 

훨씬 더 near plane 쪽으로 값이 몰리게 된다.


이와 유사하게, far plane을 무한대로 미는 것이 왜 그다지 효과적이지 못한지 이 문맥에서 알 수 있다.

단지 d 범위를 1/z = 0에 이르기 까지 약간 확장하는 것이다.


floating-point(부동소수점) 깊이는 어떨까?

아래 그래프는 3개의 지수비트와 3개의 가수비트를 사용하는 부동소수점 형식으로 시뮬레이션 된 눈금을 추가한 것이다.


이제 [0,1] 사이에 40개의 값이 있으므로  이전16개 였을 때 보다 많지만, 그림에서 알 수 있듯이 대부분이

더이상의 정밀도가 필요없는 near plane 쪽으로 몰려있다.


현재 깊이 범위를 역전시키기 위해 널리 알려져 있는 트릭은, near plane에 d=1을 매핑하고 

far plane에 d=0을 매핑하는 것이다.

위 그림의 결과물을 보면 정밀도의 분배가 이전 보다 훨씬 나아졌다.

이제 부동소수점의 준 로그 분포(quasi-logarithmic distribution)는 1/z 비선형화를 다소 줄여주며

위에서 언급됐던 정수깊이 버퍼와 비교 했을 때 near plane에서 유사한 정밀도를 제공해 주며, 

원하는 곳에 광범위하게 정밀도를 증가시킬 수 있게 해 준다.

위 그래프를 보면, 이 정밀도는 멀리 이동할 때 마다 아주 천천히 안좋아 진다.


각주 : float의 설계상 0에 가까운 값일 수록 정밀도가 높다.

그래서 0에 몰빵되서 저장하고 있던 값을 반전시키면 1에 몰빵된 값이 되는데 

float이 0에 가까울수록 정밀도가 좋기에 위와 같이 선형적인 그래프가 된다.

아래 표는 각 영역에서 사용하는 float의 정밀도 간격값이다.

2의 n승  정밀도 간격

-24 :      0.000000059604644775390625 

-14 :      0.00006103515625

-13 :      0.0001220703125

-12 :      0.000244140625

-11 :      0.00048828125

-10 :      0.0009765625

-9 :       0.001953125

-8 :       0.00390625

-7 :       0.0078125

-6 :       0.015625

-5 :       0.03125

-4 :       0.0625

-3 :       0.125

-2 :       0.25

-1 :       0.5 

참고 문서 : float의 정밀도


이 reversed-Z(역전된 Z) 트릭은 아마도 몇번 독자적으로 재연구 되어졌겠지만, 

최소한 Eugene Lapidous와 Guofang Jiao에 의해 작성된 SIGGRAPH '99paper 까지 거슬러 올라간다.

이 연구는 최근에 Matt PettineoBrano Kemen의 블로그 포스트와, SIGGRAPH 2012에서 Emil Persson의

Creating Vast Game World 에서 다시 재조명 되었다.


이전의 모든 다이어그램은 투영 후의 깊이 범위를 D3D 방식대로 [0,1]이라 가정했는데, OpenGL이라면 어떨까?

OpenGL은 기본적으로 투영 후 깊이 범위를 [-1,1]로 가정한다.

정수 형식에서는 차이가 없지만 부동소수점을 사용하면 모든 정밀도가 중간에 쓸데없이 몰려있다.

이 값은 나중에 깊이버퍼에서 [0,1]로 매핑되어 저장되기는 하지만, 초기에 이미 [-1,1] 범위로 매핑되어

범위의 뒤쪽 정밀도를 모두 날려먹었기에 그다지 도움이 되지 않는다.

그리고 대칭으로 보면, reversed-Z 트릭은 여기서는 그다지 필요가 없어 보인다.


다행히도, 데스크탑 OpenGL에서는 ARB_clip_conrol 명령어를 사용하여 이 문제를 수정할 수 있다.

(이제는 OpenGL 4.5의 코에에서 glClipControl를 통해 지원한다.)

불행히도, GL ES에서는 방법이 없다.



반올림(roundoff) 오류의 영향 

1/z 매핑과, float 버퍼 와 정수버퍼중 어느것을 선택할건지는 정밀도 부분에서 큰 부분을 차지하지만 전부는 아니다.

화면을 렌더하기에 충분한 깊이 정밀도라 할지라도,정점 변환 프로세스의 산술적 오류에 의해 정밀도 오류가 쉽게 발생한다.


앞에서 이야기 했듯이, Upchurch 와 Desbrun 은 이 부분에 대해 연구하였고,

반올림 에러를 줄이기 위한 두가지 중요 권장사항을 제시하였다.

1. 무한 far plane을 사용해라.

2. 투영행렬을 다른 행렬과 분리하고, 정점 셰이더에서 뷰행렬에 합치기 보다는 따로 적용시켜라.


Upchurch와 Desbrun은 각 산술연산마다 작은 무작위 변화를 추가함으로써 반올림 에러를 처리하고,

이를 변환과정을 통해 첫번째 순서로 추척한다.

직접 시뮬레이션을 통해 결과를 확인해 보기로 했다.


파이썬(Python) 3.4 numpy로 만든 소스는 이곳을 보면 된다.

이 소스는 near와 far planes 사이에서 무작위 점들을 선형적 또는

대수적으로 간격을 두면서 생성하여 depth(깊이)에 따라 정렬한다.

그리고 나서 이 점들을 32비트 부동 소수점 정밀도를 사용하여 뷰, 투영 행렬과 원근 나누기를 통해 넘기고

최종 결과를 24비트 정수로 양자화 한다.

마지막으로, 시퀀스를 따라 실행하면서 얼마나 여러번 인접해 있는 두 점들이(실제로는 다른 깊이값을 가진) 

같은 깊이 값으로 매핑되어 구분하기 어려워지거나, 순서가 뒤바뀌는지를 센다.

다시 말하자면, Z Fighting 문제처럼 깊이 비교 에러의 발생 비율를 측정한다.


여기 아래 표에 near = 0.1, far =10k 선형적으로 배치된 깊이값들로 얻은 결과값을 보여준다.

(logarithmic depth 방식과 다른 near/far 비율로도 테스트 해 봤는데 세세한 값들이 다양해도 결과는 같은 경향을 보였다.


이 표에서, "indist"는 구분 불가(두 이웃한 깊이값이 최종적으로 같은 깊이 버퍼 값으로 매핑되는)를 의미하며,

"swap"은 두 이웃 깊이값이 순서가 바뀐것을 의미한다.


그래프로 만들지 않아서 미안하지만, 너무 많은 그래프 축을 그려야 해서 그래프로 하기는 힘들다.

어쨋든, 숫자들을 보면, 몇가지 명백한 결과를 알 수 있다.


대부분의 환경에서 보면 float과 integer 깊이 버퍼간에는 차이가 없다.

산술오류는 양자화 오류로 이어진다.

이는 부분적으로 봤을 때, float32와 int24는 [0.5, 1]에서 ulp(Unit in Last Place)가 거의 같은 크기여서

(float32 는 23bit의 가수를 가지기 때문에), 실제로는 거의 모든 깊이 영역에서 추가적인 양자화 오류가 거의 없다.

대부분의 경우, Upchurch와 Desbrun의 권고에 따라 뷰와 투영행렬을 분리하면 약간의 개선이 이루어 진다.

전체적으로 에러률을 낮춰주지는 않지만, 구분하기 힘든 부분에서 맞는 방향으로 바꿔주는 것 처럼 보인다.

 무한 far plane은 오류률 매우 작은 차이를 보여준다. Upchurch와 Desbrun은 수치적으로 에러가 25%감소한다고

했지만, 비교 에러률이 줄여준다고 보기에는 어렵다.


위의 언급한 세가지와는 실제적으로 거의 관련이 없지만, reversed-Z(역전된 Z) 매핑은 기본적으로 환상적이다.


왜 그런지 확인해 보자.

 float 깊이 버퍼는 이 테스트에서 오류룰이 0%다.

물론 입력 깊이 값의 간격을 더 촘촘하게 해서 약간의 에러를 만들어 낼 수도 있다.

하지만 그럼에도 불구하고, float으로 reversed-Z를 사용하는것은 다른 방법들 보다 훨씬 정확하다.

 integer 깊이버퍼로 reversed-Z를 사용하는것은 다른 integer 옵션보다 좋다.

 reversed-Z는 미리 view projection 이 미리 합성된 것을 사용하는 대신 별개로 분리하면서 얻는 이점과,

유한 far 평면에서 무한 far 평면을 사용하면서 얻는 이점을 다 없애버릴 만큼 좋다.

다시 말해 reversed-Z를 사용하면, 정밀도에 영향을 주지 않으면서 투영 행렬을 다른 행렬들과 결합시킬 수 있고,

원하는 방식의 far 평면을 사용할 수 있다는 말이다.


이제 결론은 명백하다.

어떤 원근 투영상황에서도, reversed-Z 부동소수점 깊이 버퍼를 사용하는것이 가장 좋다!!

부동 소수점 깊이버퍼를 사용할 수 없다 하더라도 reversed-Z는 사용해야 한다.

모든 정밀도 에러 상황에서(특히 극단적인 깊이를 포함하는 개방형 월드 환경에서는 더욱) 만병통치약은 아니지만

reversed-Z는 그럼에도 불구하고 깊이 정밀도 문제를 해결하는 데 좋은 시작점이다.


Nathan은 그래픽스 프로그래머로, 현재 NVIDIA의 DevTech 소프트웨어 팀에서 일하고 있다.

그의 글을 더 읽어보길 원한다면 이곳을 참조해라.


---------------------------------------------------------------------------------------------------------------------------------

번역 내용 이외에 개인적인 궁금증.

1. 보통 rendertarget(결국은 텍스쳐)에 값을 쓸 때 0.0~1.0의 값을 쓰는데 integer format은 0~1로 값이 끊기지만

floating format은 -값도, 그리고 1 이상의 값도 넣을 수 있다. 

그런데 대부분 사람들은, 그리도 대부분의 아티클이나 샘플 예제들은 0~1로 값을 제한한다.

이 이유는 무었일까? 그냥 float의 정밀도에 따른 신뢰도의 정도 때문일까?



Reference Link

- 원문 : Depth Precision Visualized

- Tightening the Precision of Perspective Rendering

- 원근투영

- 점근선

- float의 정밀도

- 원문을 쓴 필자의 블로그

- Unity 5.5 graphics changes and improvements

- msdn, Depth Buffers (Direct3D 9)

- You Can Never Have a Custom Z Buffer Distribution andEarly Z at the Same Time

- gamasutra, Logarithmic Depth Buffer

- D3D9 GPU Hacks

- NVIDIA GPU Programming Guide

- Depth를 기록하자

- Advanced DX9 Capabilites for ATI Randeon Cards

- Logarithmic Depth Buffer

- Maximizing Depth Buffer Range and Precision

- Reversed-Z in OpenGL

- 원문 번역 https://blog.naver.com/zmfltbsk2/221207477425

- 원문 번역 http://lifeisforu.tistory.com/365

- Learning to Love your Z-buffer

- Infinity Projection Matrix

'Study > Graphics' 카테고리의 다른 글

texture format  (0) 2018.02.21
shader에서 채널값이 모자를 때...  (0) 2018.02.13
depth-precision-visualized  (0) 2018.01.31
디더링 (Dithering)  (0) 2018.01.26
Texture types  (0) 2017.02.05
rgbm  (0) 2016.12.29
Posted by 붕대마음

댓글을 달아 주세요

디더링 (Dithering)

Study/Graphics / 2018. 1. 26. 17:43

디더링이란 무었인가?

컴퓨터 그래픽스에서 제한된 색을 사용하여 여러 색을 최대한 근사하게 생성하기 위해 사용되는 기술.

화상의 각 면을 다른 계조 색의 도트 집합으로 처리하는 기술에 의존한다.

결국은 없는 색을 만들기 위해 색을 조밀하게 배합하여 다른색상인 것처럼 보이게 하는 방법.

참고 : 앱 개발에 필요한 Image magick 활용

이 기술로 매끄럽지 못하고 계단모양으로 울퉁불퉁한 윤곽선이나 대각선을 완화시킬 수 있다.

참조 : wiki, 디더링


이전 방식? 으로는 망점(halftone screen)이 있는데 망점은 점의 크기나 간격에 따라

연속 색조의 상을 따라 만드는 복사기법으로 점으로 그라디언트와 같은 효과를 낸다.

참조 : wiki, 망점 (halftone screen)



Reference Link

- wiki, 디더링

- 네이버 지식백과

- wiki, 망점 (halftone screen)

- 나무위키, 디더링

- 16bit 프로세싱을 통한 디더링 dither - avs 스크립트

- Of Bit Depths, Banding and Normal Maps


'Study > Graphics' 카테고리의 다른 글

shader에서 채널값이 모자를 때...  (0) 2018.02.13
depth-precision-visualized  (0) 2018.01.31
디더링 (Dithering)  (0) 2018.01.26
Texture types  (0) 2017.02.05
rgbm  (0) 2016.12.29
Efficient Gaussian blur with linear sampling  (0) 2016.10.22
Posted by 붕대마음

댓글을 달아 주세요

최근에 달린 댓글

최근에 받은 트랙백

글 보관함