구멍이 보이시죠??^^ 




균열방지
LOD를 적용하면서 균열이 생긴걸 봤다. 한번 땜빵해보자
결국은 LOD 단계가 다른 메시들이 만나는 경계부분에 때우기용 분할 삼각형을 추가해주면 된다.


균열부위 땜질하기
균열된 부분을 해결하려면 경계부분에 추가로 삼각형을 몇개 끼워넣으면 되는데 다음과 같은 경우를 생각해보자




 


 균열이 생길 수 있는 경계선 부분이 땜질 삼각형으로 덮이기 때문에 균열이 발생하지 않는다.

이런 땜질 삼각형을 LOD단계가 달라지는 모든 노드에 상하좌우 네방향으로 만들어주면 된다.


균열부위를 땜질하려면 현재 자신의 노드 이외의 이웃한 노드들의 정보도 있어야하는데, 그러기 위해서 4가지 함수가 추가되었다.


/// 이웃노드를 만든다.(삼각형 찢어짐 방지용)

void _BuildNeighborNode( ZQuadTree* pRoot, TERRAINVERTEX* pHeightMap, int cx )

{

int n;

int _0, _1, _2, _3;


for( int i=0 ; i<4 ; i++ )

{

_0 = m_nCorner[0];

_1 = m_nCorner[1];

_2 = m_nCorner[2];

_3 = m_nCorner[3];

// 이웃노드의 4개 코너값을 얻는다.

n = _GetNodeIndex( i, cx, _0, _1, _2, _3 );

// 코너값으로 이웃노드의 포인터를 얻어온다.

if( n >= 0 ) m_pNeighbor[i] = pRoot->_FindNode( pHeightMap, _0, _1, _2, _3 );

}


// 자식노드로 재귀호출

if( m_pChild[0] )

{

m_pChild[0]->_BuildNeighborNode( pRoot, pHeightMap, cx );

m_pChild[1]->_BuildNeighborNode( pRoot, pHeightMap, cx );

m_pChild[2]->_BuildNeighborNode( pRoot, pHeightMap, cx );

m_pChild[3]->_BuildNeighborNode( pRoot, pHeightMap, cx );

}

}

주변의 4개 노드값을 얻어 이웃노드를 만든다


/// 쿼드트리를 만든다.(Build()함수에서 불린다)

BOOL _BuildQuadTree( TERRAINVERTEX* pHeightMap );


/// 쿼드트리를 검색해서 4개 코너값과 일치하는 노드를 찾는다.

ZQuadTree* _FindNode( TERRAINVERTEX* pHeightMap, int _0, int _1, int _2, int _3 )

{

ZQuadTree* p = NULL;

// 일치하는 노드라면 노드값을 리턴

if( (m_nCorner[0] == _0) && (m_nCorner[1] == _1) && (m_nCorner[2] == _2) && (m_nCorner[3] == _3) )

return this;


// 자식 노드가 있가?

if( m_pChild[0] )

{

RECT rc;

POINT pt;

int n = ( _0 + _1 + _2 + _3 ) / 4;


// 현재 맵상에서의 위치

pt.x = (int)pHeightMap[n].p.x;

pt.y = (int)pHeightMap[n].p.z;


// 4개의 코너값을 기준으로 자식노드의 맵 점유범위를 얻는다.

SetRect( &rc, (int)pHeightMap[m_pChild[0]->m_nCorner[CORNER_TL]].p.x, 

  (int)pHeightMap[m_pChild[0]->m_nCorner[CORNER_TL]].p.z, 

  (int)pHeightMap[m_pChild[0]->m_nCorner[CORNER_BR]].p.x, 

  (int)pHeightMap[m_pChild[0]->m_nCorner[CORNER_BR]].p.z );

// pt값이 점유범위안에 있다면 자식노드로 들어간다.

if( IsInRect( &rc, pt ) )

return m_pChild[0]->_FindNode( pHeightMap, _0, _1, _2, _3 );


SetRect( &rc, (int)pHeightMap[m_pChild[1]->m_nCorner[CORNER_TL]].p.x, 

  (int)pHeightMap[m_pChild[1]->m_nCorner[CORNER_TL]].p.z, 

  (int)pHeightMap[m_pChild[1]->m_nCorner[CORNER_BR]].p.x, 

  (int)pHeightMap[m_pChild[1]->m_nCorner[CORNER_BR]].p.z );

if( IsInRect( &rc, pt ) )

return m_pChild[1]->_FindNode( pHeightMap, _0, _1, _2, _3 );


SetRect( &rc, (int)pHeightMap[m_pChild[2]->m_nCorner[CORNER_TL]].p.x, 

  (int)pHeightMap[m_pChild[2]->m_nCorner[CORNER_TL]].p.z, 

  (int)pHeightMap[m_pChild[2]->m_nCorner[CORNER_BR]].p.x, 

  (int)pHeightMap[m_pChild[2]->m_nCorner[CORNER_BR]].p.z );

if( IsInRect( &rc, pt ) )

return m_pChild[2]->_FindNode( pHeightMap, _0, _1, _2, _3 );


SetRect( &rc, (int)pHeightMap[m_pChild[3]->m_nCorner[CORNER_TL]].p.x, 

  (int)pHeightMap[m_pChild[3]->m_nCorner[CORNER_TL]].p.z, 

  (int)pHeightMap[m_pChild[3]->m_nCorner[CORNER_BR]].p.x, 

  (int)pHeightMap[m_pChild[3]->m_nCorner[CORNER_BR]].p.z );

if( IsInRect( &rc, pt ) )

return m_pChild[3]->_FindNode( pHeightMap, _0, _1, _2, _3 );

}


return NULL;

}

나머지 함수는 _BuildNeighborNode함수를 지원하기 위해 만들어진 함수로 모두 이웃노드를 검색하는데 사용된다.


/// 4개 방향(상단,하단,좌측,우측)의 이웃노드 인덱스를 구한다.

int _GetNodeIndex( int ed, int cx, int& _0, int& _1, int& _2, int& _3 )

{

int n, _a, _b, _c, _d, gap;

_a = _0;

_b = _1;

_c = _2;

_d  = _3;

gap = _b - _a; // 현재 노드의 좌우폭값


switch( ed )

{

case EDGE_UP: // 위쪽 방향 이웃노드의 인덱스

_0 = _a - cx * gap;

_1 = _b - cx * gap;

_2 = _a;

_3 = _b;

break;

case EDGE_DN: // 아래 방향 이웃노드의 인덱스

_0 = _c;

_1 = _d;

_2 = _c + cx * gap;

_3 = _d + cx * gap;

break;

case EDGE_LT: // 좌측 방향 이웃노드의 인덱스

_0 = _a - gap;

_1 = _a;

_2 = _c - gap;

_3 = _c;

break;

case EDGE_RT: // 우측 방향 이웃노드의 인덱스

_0 = _b;

_1 = _b + gap;

_2 = _d;

_3 = _d + gap;

break;

}


n = ( _0 + _1 + _2 + _3 ) / 4; // 가운데 인덱스

if( !IS_IN_RANGE( n, 0, cx * cx - 1 ) ) return -1;


return n;

}

출력할 삼각형의 인덱스를 만들면서 상하좌우 노드의 LOD값을 검색한 뒤, 현재 노드의 LOD값과 다른노드가 있는 경우 그 방향으로 땜질삼각형을 분할 생성해주면 된다.



그림은 서로다른 자식노드에 이웃노드가 걸쳐있는 경우

그림기준으로 노드들은 상하좌우 2x2



이것으로 지형처리 끝났는데

요고말고도 ROAM(Realtime Optimally Adaptive Mesh)기법이 있다.

LOD처리시 삼각형 땜질하는 방법이나 분할하는 방법에 따라 다양한 기법들이 제시되어왔고 여기에 요즘에는 정점 셰이더를 사용하는 방법도 제시되고 있다.

그냥 수많은 방법중 하나를 해본 것 뿐

그리고 결국은 쿼드트리를 기반으로 한게 대부분이다.

3D렌더링 기법도 결국 자료구조가 밑받침 되어야 제대로된 성능을 발휘할 수 있다!

틈날때마다 기초수학과 자료구조에 대한 공부를 해두자


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

Direct X - 스크립트와 XML파서  (0) 2014.09.05
Direct X - 애니메이션 기법  (0) 2014.09.03
Direct X - LOD (Level Of Detail)  (2) 2014.08.21
Direct X - 쿼드트리 컬링  (0) 2014.08.16
Direct X - 쿼드트리  (0) 2014.08.14
Posted by 긍정왕오킹