겜브리오의 Frame Render Sytstem 에서는 랜더클릭별로 알파프로세스를 지정해 줄 수 있다.
구조를 설명하자면.. 각 Mesh들은 랜더클릭의 랜더링 함수가 호출될때 클릭별로 설정된 CullingProcess에 의하여 보여지는 영역들에 대한 메쉬들의 집합을 구한다. 이 RenderObject들의 리스트(NiVisibleArray)를 가지고 랜더링을 하게되는데 이것은 SceneGraph상의 순서(생성순, 노드어태치순)으로 일괄적으로 랜더링되며 AssetViewer나 NiApplication에서는 NiAlphaSortProcessor를 사용한다. 이것을 연결하지 않을시 디폴트로된 기본 솔트프로세스가 연결되어지는데 이건 그냥 순서대로 그리는것이다.
NiAlphaSortProcessor를 보면 NiBackToFrontSortProcessor를 상속하게 되는데 위에 불투명한랜더링객체(알파가 없는, NiSortedAdjustNOde가 적용된)를 우선적으로 그리고, 알파가 있는 랜더링 객체를 기본적으로 Mesh의 피봇중심(BoundCenter)과 카메라와의 거리(ZBuffer)을 이용하여 뒤에서부터 앞으로 정렬하여 나온 NiVisibleArray의 내용을 그린다.
하지만 위와같은 랜더링시 문제점도 있어서
1) 알파텍스쳐가 쓰인 큰오브젝트(폭포, 스카이박스)의 경우 폭포의 피봇중심과 그 폭포앞의 오브젝트들(폭포앞물튐파티클등)이 서로 카메라의 위치에 따라 혹은 폭포와 스카이박스피봇와의 거리차등..의 문제로 인해서 앞뒤판정이 실제와 차이나게 생길수있고
2) 알파를 적용한 빌보드와 파티클이 같이 나오는 경우 파티클이 뒤에서 빌보드를 통과한다거나 앞에서 뒤로 빌보드를 통과 하거나 혹은 빌보드와 근접시.. 카메라의 위치에 따라 잘못판단되는 경우가 생길수있다.
예) 두장의 알파빌보드를 근접하여 놓았을경우 카메라 방향에 따라 앞뒤면이 잘못 나올수있다.
3) NiSortAdjustNode를 썼을 경우에도 불투명 오브젝트로 처리하게 되니 만약 NiSortAdJustNode 뒤에 불투명한 오브젝트가 있고 그것이 나중에 랜더링 된다면 이 NiSortAdjustNode를 쓴 알파오브젝트에는 실제로 뒤에 오브젝트가 나오지 않게 될 것이다.
예) NiSortAdjustNode를 쓴 폭포뒤에 불투명한 동굴이 있다고 쳤을때 실제로 어태치된 순서가 동굴이
나중에 어태치 되어있다면 동굴은 폭포수에 그려지지 않을 것이다.
위와 같은 문제를 해결하기 위해선 디자이너들이 알파에 대한 정렬 프로세스를 확실히 이해 한 상태에서 작업을 해야 한다는것이다.
파티클이나 빌보드의 경우 겹쳐저도 상관없는 경우 ZTest만 하고 ZWrite는 하지 않게 하여 (Zmode10), 테스트를 하여 투명우산 앞에 그릴지 뒤에 그릴지를 판단하고 그려질땐 Zbuffer를 쓰지 않아 같은 파티클의 경우 섞이게 만들고. 넓은 반투명 빌보드의 겹침현상은 기본적으로 두개의 오브젝트의 피봇중심이 가까워서 일어나는 현상임으로 이런경우는 실제피봇중심만 거리를 두게끔 처리함으로서 해결할수 있을 것이다. 이렇게 되어도 카메라의 거리에 따라 문제가 생길수 있는 부분은 존재하는데 확실히 랜더링 순서를 알고있는 메쉬집합체의 경우 깊이를 쓰는 부분에서 순서대로 깊이를 판단해서 NiVisibleArray에 넣어 주어 랜더링 하는 방법이 있는데
나의 경우에는 디자이너와 협의하여 순서를 확실히 아는 오브젝의 경우 Aset_이라는 이름으로 판단하였다.
아래는 사용중인 CustomAlphaSortProcessor 의 핵심 부분이다.
void NiCustomAlphaSortProcessor::PreRenderProcessList(const NiVisibleArray* pkInput, NiVisibleArray& kOutput, void* pvExtraData)
{
if (!pkInput) return;
NiRenderer* pkRenderer = NiRenderer::GetRenderer();
NIASSERT(pkRenderer);
NiPoint3 kWorldLoc, kWorldDir, kWorldUp, kWorldRight;
NiFrustum kFrustum;
NiRect<float> kViewport;
pkRenderer->GetCameraData(kWorldLoc, kWorldDir, kWorldUp, kWorldRight,
kFrustum, kViewport);
const unsigned int uiInputCount = pkInput->GetCount();
if (m_uiAllocatedDepths < uiInputCount)
{
NiFree(m_pfDepths);
m_pfDepths = NiAlloc(float, uiInputCount);
m_uiAllocatedDepths = uiInputCount;
}
NiSortedObjectList kItems0;
unsigned int uiDepthIndex = 0;
bool bSet = false;
float f;
for (unsigned int ui = 0; ui < uiInputCount; ui++)
{
NiRenderObject& kMesh = pkInput->GetAt(ui);
// NiSortAdjustNode인 경우
if( !kMesh.GetSortObject() ) // NiSortAdjustNode는 알파로 처리하되.. 알파보다는 먼저 랜더링 되게 변경.
{
kItems0.AddTail(&kMesh);
}
else if (IsTransparent( kMesh ) )
{
NiNode * pkParent = kMesh.GetParent();
NiFixedString name = kMesh.GetName();
if( name.Contains("Aset_") || ( pkParent && pkParent->GetName().Contains("Aset_") ) )
{
kOutput.Add(kMesh);
if( bSet == false )
{
f = ComputeDepth(kMesh, kWorldDir);
m_pfDepths[uiDepthIndex++] = f;
}
else
{
f = f-0.1f;
m_pfDepths[uiDepthIndex++] = f;
}
bSet = true;
}
else
{
bSet = false;
kOutput.Add(kMesh);
m_pfDepths[uiDepthIndex++] = ComputeDepth(kMesh, kWorldDir);
}
}
else
{
// 불투명한 오브젝트는 바로그린다. (NoSort 포함);
NiAVObject* pkAVObj = &kMesh;
if (NiIsKindOf(NiRenderObject, pkAVObj))
{
// FOutputLOG( (char *)(const char *) kMesh.GetName() );
kMesh.RenderImmediate(pkRenderer);
}
}
}
// NiSortAdjustNode는 불투명랜더링이 끝나면 이것도 바로 랜더링
while (kItems0.GetSize())
{
NiRenderObject* pkGeom = kItems0.RemoveHead();
pkGeom->RenderImmediate(pkRenderer);
}
//
SortObjectsByDepth(kOutput, 0, kOutput.GetCount() - 1);
/* // 로그를 남겨보자..
FOutputLOG("====알파만 걸러서 순서가 어뜨케 되나..=====================");
for(int i = 0; i<kOutput.GetCount(); i++)
{
NiRenderObject& kMesh = kOutput.GetAt(i);
FOutputLOG((char*)(const char*)kMesh.GetName());
}
*/
}