아주 기초적인 것이지만, 막상 문서로 정리된 것도 없는 거 같아서, 나름대로 적용하고 있는 정책을 간단하게 정리해 봅니다.
1. 알파 메시는 비 알파 블랜딩 메시를 렌더링한 후에 렌더링한다.



[그림1]은 알파 이미지를 먼저 렌더링한 후에 카메라에서 더 멀리 떨어져있는 주전자를 렌더링 했을 때의 결과 입니다. 실제로 알파 메시 렌더링시 기록된 Z 값 때문에 주전자가 렌더링 되지 못하고 있습니다.
[그림2]는 알파 테스트를 통해서 알파가 0인 픽셀은 안 그린 (z 값도 기록하지 않는) 이미지 입니다. [그림1]에 비해서 나아졌지만 역시 필터링 되어 뭉개진 부분에는 주전자가 렌더링 되고 있지 않습니다.
[그림3]은 간편하게 문제를 해결하는 방법으로 알파가 0xFF 가 아니면 무조건 렌더링 안하도록 알파테스트 값을 조정한 이미지입니다. 실제로 Z때문에 통과되어 렌더링이 안되는 픽셀은 없어졌지만 알파 메시 자체가 의도된 것과는 다르게 나올 수 있습니다.
[그림4]는 가장 정석적이고 올바른 해결 방법으로, 주전자를 먼저 렌더링하고 뒤에 알파 메시를 렌더링한 결과입니다.
알파 블랜딩 되는 부분은 실제로 가려질 부분의 프레임이미지와 블랜딩 되어야 하므로 [그림4]처럼 비 알파 메시를 먼저 렌더링한 후에 알파 렌더링을 해야 합니다.
=> 알파 텍스처를 쓴 메시나, 버텍스 알파를 사용하는 메시는 나중에 그린다.
2. 알파 메시는 거리로 소팅해서 멀리 있는 메시부터 그린다.

이제는 알파 메시간의 렌더링 순서에 대해서 생각해보겠습니다.
흰 바탕에 빨간 주전자와 누런 주전자를 알파를 넣어 투명하게 렌더링한다고 하면 [그림6]이 올바른 결과물입니다. 즉, 누런 주전자가 투명한 주전자이므로 빨간 주전자가 비쳐야 하기 때문입니다. 하지만 누런 주전자를 먼저 렌더링 할 경우 그림 5와 같은 결과가 나옵니다. 즉, 누런 주전자의 Z값 때문에 빨간 주전자가 렌더링 되지 않는 것입니다. (일반적으로 알파 렌더링시에 렌더링의 불필요한 부하를 줄여주기 위해 ZWrite 를 Disable 시키고 렌더링하는데, ZWrite를 안한다면 가정해도 소팅을 하지 않는다면 [그림 5]처럼 나오지는 않겠지만, 빨간색 주전자의 알파 블랜딩 값은 누런 주전자가 렌더링된 프레임과 연산이 되므로, 원하는 결과를 기대할 수 없습니다.)
=> 알파 메시에 대해서는 멀리 있는 것부터 아까운 메시 순으로 렌더링한다.
3. (tip) 오브젝트 내의 겹침은 2 pass 렌더링으로 해결한다.

아마 버텍스 알파로 오브젝트를 렌더링 해보면 [그림 7]처럼 겹치는 부분이 생겨서 메시의 속이 보이는 것을 경험해보셨을 것입니다.
아마 이전의 가속을 고려하지 않는 환경이라면 페이스단위로 소팅을 한 후 가까운 것에서 먼 것으로 Z를 기록하면서 렌더링하는 식이면 쉽게 [그림 8]과 같은 깔끔한 이미지를 얻을 수 있을 것입니다. (약 3년전 마무리 되었던 프로젝트에서는 이 방법을 사용해서 큰 문제는 없었습니다.) 하지만, 보통의 가속 받는 환경에서는, 별다른 처리 없이 내부 메시의 순서 변환 없이는 [그림 8]처럼 속이 보이지 않는 깔끔한 이미지를 얻을 수 없습니다.
이런 경우에 쓸만한(?) 아이디어로 2 PASS로 [그림8]과 같이 깨끗한 이미지로 렌더링 하는 방법을 소개합니다.
step A. 실제로 렌더링하되, Z 값만 렌더링한다. (화면에는 렌더링하지 않지만 Z값은 가까운 상태로 남게 됩니다.)
step B. Z 비교 함수를 equal로 만든 후 렌더링한다. (앞에 있는 픽셀은 당연히 같은 위치에 같은 Z값을 가지므로 렌더링 될 것이고, 속안의 겹치는 픽셀은 Z test 에서 실패해서 렌더링 되지 않을 것입니다.)
간단히 아래처럼 주전자를 렌더링한다고 하면
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); g_pTeapot->DrawSubset(0);
|
아래처럼 간단하게 수정할 수 있을 것입니다.
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); g_pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); g_pTeapot->DrawSubset(0);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); g_pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_EQUAL); g_pTeapot->DrawSubset(0);
|
2 pass로 인한 속도저하를 걱정하시는 분에게 비추천입니다만, 첫번째 렌더링에선 텍스처없이 렌더링하고, 두번째 패스에선 ZWrite를 끄는 식으로 몇 가지 아이디어만 적용해도 속도상 불이익을 만회할 수 있다고 생각합니다. (저 개인적으론 전혀 걱정 안합니다. ^^)
=> 투명한 메시(혹은 오브젝트)에 대해선 겹치지 않도록 2pass 렌더링한다.
댓글을 달아 주세요
참고로 알파블랜드가 꺼져 있으면 z만 write 가 안됩니다. 왠지 납득은 안되지만 설정된 D3DRS_SRCBLEND, D3DRS_DESTBLEND가 무시되고 화면에 그려져 버립니다.
conaman님의 말씀은 3.오브젝트의 겹침해결 부분에 관한말씀이신가요?
첫번째 것에서 DEST를 찍지 말고 아예 Color를 안 찍는 방법도 있습니다.
아..3번팁은..좋네요...오랜만에 왔다가 좋은거 또 배우고 갑니다.
너는 우수한 위치가 있는다!
너는 우수한 위치가 있는다!