Game Programming Gems 5 - 1.6에 나온 개선된 절두체 선별에 대한 세미나 자료입니다.

내용은 기존의 절두체를 구현해봤다는 혹 할정도의 계산량 개선을 보여줍니다.

벡터의 내적, 외적과 평면방정식, 삼각함수 정도만 알고 있으면 쉽게 이해할 수 있습니다.

PT만으로는 부족할 듯 해서 구현 코드로 부가 설명을 하였습니다.

1. 절두체 생성 과정 비교

- 기존 절두체 생성 코드

bool NMCuller::Make( D3DXMATRIX* pmatViewProj )
{
	bool	bResult	=	true;

	int				i;
	D3DXMATRIXA16	matInv;

	//! [ dunkhoon : 2009-09-09 ] : 절두체를 구성하는 8개 정점을 투영좌표상의 위치로 설정한다.
	m_vFrustum[ 0 ].x = -1.0f; m_vFrustum[ 0 ].y = -1.0f; m_vFrustum[ 0 ].z = 0.0f;
	m_vFrustum[ 1 ].x =  1.0f; m_vFrustum[ 1 ].y = -1.0f; m_vFrustum[ 1 ].z = 0.0f;
	m_vFrustum[ 2 ].x =  1.0f; m_vFrustum[ 2 ].y = -1.0f; m_vFrustum[ 2 ].z = 1.0f;
	m_vFrustum[ 3 ].x = -1.0f; m_vFrustum[ 3 ].y = -1.0f; m_vFrustum[ 3 ].z = 1.0f;
	m_vFrustum[ 4 ].x = -1.0f; m_vFrustum[ 4 ].y =  1.0f; m_vFrustum[ 4 ].z = 0.0f;
	m_vFrustum[ 5 ].x =  1.0f; m_vFrustum[ 5 ].y =  1.0f; m_vFrustum[ 5 ].z = 0.0f;
	m_vFrustum[ 6 ].x =  1.0f; m_vFrustum[ 6 ].y =  1.0f; m_vFrustum[ 6 ].z = 1.0f;
	m_vFrustum[ 7 ].x = -1.0f; m_vFrustum[ 7 ].y =  1.0f; m_vFrustum[ 7 ].z = 1.0f;

	//! [ dunkhoon : 2009-09-09 ] : 변환에 사용된 뷰행렬과 프로젝션 행렬을 결함 행렬을 역행렬로 구해낸다
	D3DXMatrixInverse( &matInv, NULL, pmatViewProj );

	//! [ dunkhoon : 2009-09-09 ] : 절두체를 구성하는 정점을 위에서 구한 역행렬로 변환하여 월드 좌표상의 위치로 변환한다
	for( i = 0; i < 8; i++ )
		D3DXVec3TransformCoord( &m_vFrustum[ i ], &m_vFrustum[ i ], &matInv );

	//! [ dunkhoon : 2009-09-09 ] : 가까운 평면의 왼쪽하단 점과 오른쪽 상단점을 더해 반으로 나눠서 카메라의 중점 좌표를 구한다
	m_vPos	=	( m_vFrustum[ 0 ] + m_vFrustum[ 5 ] ) / 2.0f;

	//! [ dunkhoon : 2009-09-09 ] : 위에서 구한 절두체를 구성하는 8개의 정점을 이용해 6개의 평면을 구성한다
	D3DXPlaneFromPoints( &m_plPlane[ 0 ], m_vFrustum + 4, m_vFrustum + 7, m_vFrustum + 6 );
	D3DXPlaneFromPoints( &m_plPlane[ 1 ], m_vFrustum, m_vFrustum + 1, m_vFrustum + 2 );
	D3DXPlaneFromPoints( &m_plPlane[ 2 ], m_vFrustum, m_vFrustum + 4, m_vFrustum + 5 );
	D3DXPlaneFromPoints( &m_plPlane[ 3 ], m_vFrustum + 2, m_vFrustum + 6, m_vFrustum + 7 );
	D3DXPlaneFromPoints( &m_plPlane[ 4 ], m_vFrustum, m_vFrustum + 3, m_vFrustum + 7 );
	D3DXPlaneFromPoints( &m_plPlane[ 5 ], m_vFrustum + 1, m_vFrustum + 5, m_vFrustum + 6 );

	return bResult;
}
코드를 보면 기존 절두체의 경우는 매번 역행렬을 구하는 과정, 절두체 8개 점에 역행렬을 곱하는 과정, 절두체를 구성하는 6개의 평면을 구하는 과정이 필요합니다.

- 개선된 절두체 생성 코드

void NMFastFrustum::SetPerspective( const float fFOV, const float fViewAspect, const float fNearZ, const float fFarZ )
{
	m_fRightFactor	=	tanf( fFOV );
	m_fUpFactor		=	m_fRightFactor * fViewAspect;
	m_fNear			=	fNearZ;
	m_fFar			=	fFarZ;
}

void NMFastFrustum::Build( const D3DXVECTOR3& vEye, const D3DXVECTOR3& vForward, const D3DXVECTOR3& vRight, const D3DXVECTOR3& vUp )
{
	m_vEyePosition	=	vEye;
	m_vForward		=	vForward;
	m_vRight		=	vRight;
	m_vUp			=	vUp;
}
개선된 절두체의 경우는 코드로 보듯이 초기에 위의 정보만 설정해주면 따로 절두체를 생성하는 과정 자체가 없습니다.

- 결론

생성 과정에서만 보아도 개선된 절두체의 경우 계산량이 거의 없다고 볼 수 있으므로 좋다는 것을 알 수 있습니다.

2. 점 포함 판정 비교

- 기존 절두체 포함 판정 코드
bool NMCuller::IsIn( D3DXVECTOR3* pvPos )
{
	bool	bResult	=	true;

	float	fDist;

	for( int i = 0; i < 6; i++ ) 
	{
		//! [ dunkhoon : 2009-09-09 ] : 평면과 정점을 내적하여.
		// 절두체의 검사대상이 되는 평면과 입력된 정점 위치 관계를 파악한다
		fDist	=	D3DXPlaneDotCoord( &m_plPlane[ i ], pvPos );
		if( fDist > PLANE_EPSILON )
			bResult	=	false;
	}

	return bResult;
}
기존 절두체의 경우는 절두체 평면과 검사 대상이 되는 점을 내적하는 과정이 이 6번 반복 되는 것을 알 수 있습니다.

- 개선된 절두체 포함 판정 코드

bool NMFastFrustum::IsIn( D3DXVECTOR3* pvPos )
{
	D3DXVECTOR3	vOP	=	*pvPos - m_vEyePosition;

	//! [ dunkhoon : 2009-09-09 ] : 앞쪽 벡터와 점을 내적하여 투영된 위치를 구한다.
	float	f	=	D3DXVec3Dot( &vOP, &m_vForward );
	//! [ dunkhoon : 2009-09-09 ] : 점의 위치가 절두체 범위에 벗어나는지 검사한다.
	if( f < m_fNear || m_fFar < f )
		return false;

	//! [ dunkhoon : 2009-09-09 ] : 오른쪽 벡터와 점을 내적하여 투영된 위치를 구한다.
	float	r	=	D3DXVec3Dot( &vOP, &m_vRight );
	float	rLimit	=	m_fRightFactor * f;
	//! [ dunkhoon : 2009-09-09 ] : 점의 위치가 절두체 범위에 벗어나는지 검사한다.
	if( r < -rLimit || rLimit < r )
		return false;

	//! [ dunkhoon : 2009-09-09 ] : 위쪽 벡터와 점을 내적하여 투영된 위치를 구한다.
	float	u	=	D3DXVec3Dot( &vOP, &m_vUp );
	float	uLimit	=	m_fUpFactor * f;
	//! [ dunkhoon : 2009-09-09 ] : 점의 위치가 절두체 범위에 벗어나는지 검사한다.
	if( u < -uLimit || uLimit < u )
		return false;

	return true;
}
개선된 절두체의 경우는 각 방향 벡터와 검사 대상이 되는 점을 내적하여 해당 방향 벡터에 대한 방향 거리를 재고 제한거리에 점이 들어와 있는지로 판별하고 있습니다.

- 결론

점 포함 판정의 경우에서도 개선된 절두체 방식의 계산량이 약 2배 가량 적다고 할 수 있습니다.

3. 비교 결과 화면


사용자 삽입 이미지

실제 계산 속도에서도 2배 가량 빠릅니다.

위 내용은 모두 Game Programming Gems 5 - 1.6에 나온 내용이므로 부족한 설명은 책을 참고하시면 됩니다^^
크리에이티브 커먼즈 라이센스
Creative Commons License
2009/09/07 10:24 2009/09/07 10:24
http://www.monstercode.net/tc/trackback/11
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST