⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 zfrustum.cpp

📁 《3D游戏编程》随书光盘 12章: 设计阴影的多种技巧, 有详细源代码
💻 CPP
字号:
#include "ZFrustum.h"
#include "ZFLog.h"

ZFrustum::ZFrustum()
{
	ZeroMemory( m_vtx, sizeof( m_vtx[0] ) * 8 );
	ZeroMemory( m_plane, sizeof( m_plane[0] ) * 6 );
}

// 输入摄像机(view)*投影矩阵(projection),得到6个平面.
BOOL ZFrustum::Make( D3DXMATRIXA16* pmatViewProj )
{
	int				i;
	D3DXMATRIXA16	matInv;

	// 如果经过投影矩阵,所有的三维世界坐标的点都变为 (-1,-1,0) ~ (1,1,1)之间的值.
	// 将同次空间的临界值填入m_vtx.
	m_vtx[0].x = -1.0f;	m_vtx[0].y = -1.0f;	m_vtx[0].z = 0.0f;
	m_vtx[1].x =  1.0f;	m_vtx[1].y = -1.0f;	m_vtx[1].z = 0.0f;
	m_vtx[2].x =  1.0f;	m_vtx[2].y = -1.0f;	m_vtx[2].z = 1.0f;
	m_vtx[3].x = -1.0f;	m_vtx[3].y = -1.0f;	m_vtx[3].z = 1.0f;
	m_vtx[4].x = -1.0f;	m_vtx[4].y =  1.0f;	m_vtx[4].z = 0.0f;
	m_vtx[5].x =  1.0f;	m_vtx[5].y =  1.0f;	m_vtx[5].z = 0.0f;
	m_vtx[6].x =  1.0f;	m_vtx[6].y =  1.0f;	m_vtx[6].z = 1.0f;
	m_vtx[7].x = -1.0f;	m_vtx[7].y =  1.0f;	m_vtx[7].z = 1.0f;

	// 求得view * proj的逆矩阵.
	D3DXMatrixInverse(&matInv, NULL, pmatViewProj );

	// Vertex_最终 = Vertex_local * Matrix_world * Matrix_view * Matrix_Proj ,
	// Vertex_world = Vertex_local * Matrix_world的缘由,
	// Vertex_最终 = Vertex_world * Matrix_view * Matrix_Proj .
	// Vertex_最终 = Vertex_world * ( Matrix_view * Matrix_Proj ) 
	// 逆矩阵( Matrix_view * Matrix_Proj )^-1两边相乘
	// Vertex_最终 * 逆矩阵( Matrix_view * Matrix_Proj )^-1 = Vertex_World .
	// 因此, m_vtx * matInv = Vertex_world, 可以得到世界坐标系的Frustum坐标.
	for( i = 0; i < 8; i++ )
		D3DXVec3TransformCoord( &m_vtx[i], &m_vtx[i], &matInv );

	// 0号和 5号为平截头体中近平面的左上方和右下方,将两个坐标相加再除2,
	// 可以得到摄像机的坐标.(不是完全一致.)
	m_vPos = ( m_vtx[0] + m_vtx[5] ) / 2.0f;
	
	// 通过得到的世界坐标制作平截头体平面.
	// 向量由平截头体内部指向外部的平面.
//	D3DXPlaneFromPoints(&m_plane[0], m_vtx+4, m_vtx+7, m_vtx+6);	// 上平面(top)
//	D3DXPlaneFromPoints(&m_plane[1], m_vtx  , m_vtx+1, m_vtx+2);	// 下平面(bottom)
//	D3DXPlaneFromPoints(&m_plane[2], m_vtx  , m_vtx+4, m_vtx+5);	// 近平面(near)
	D3DXPlaneFromPoints(&m_plane[3], m_vtx+2, m_vtx+6, m_vtx+7);	// 远平面(far)
	D3DXPlaneFromPoints(&m_plane[4], m_vtx  , m_vtx+3, m_vtx+7);	// 左平面(left)
	D3DXPlaneFromPoints(&m_plane[5], m_vtx+1, m_vtx+5, m_vtx+6);	// 右平面(right)

	return TRUE;
}

/// 如果一个点 v在平截头体内,就会返回TRUE,反之为FALSE.
BOOL ZFrustum::IsIn( D3DXVECTOR3* pv )
{
	float		fDist;
//	int			i;

	// 现在只使用 left, right, far plane.
//	for( i = 0 ; i < 6 ; i++ )
	{
		fDist = D3DXPlaneDotCoord( &m_plane[3], pv );
		if( fDist > PLANE_EPSILON ) return FALSE;	// plane的 normal向量指向 far,如果为正数,表示在平截头体外部.
		fDist = D3DXPlaneDotCoord( &m_plane[4], pv );
		if( fDist > PLANE_EPSILON ) return FALSE;	// plane的 normal向量指向 left,如果为正数,表示在平截头体的左边.
		fDist = D3DXPlaneDotCoord( &m_plane[5], pv );
		if( fDist > PLANE_EPSILON ) return FALSE;	// plane的 normal向量指向 right,如果为正数,表示在平截头体的右边.
	}

	return TRUE;
}

/** 如果具有中心(v)和 半径(radius)的边界球体位于平截头体内,
 *  就会返回TRUE,反之为FALSE.
 */
BOOL ZFrustum::IsInSphere( D3DXVECTOR3* pv, float radius )
{
	float		fDist;

	fDist = D3DXPlaneDotCoord( &m_plane[3], pv );
	if( fDist > (radius+PLANE_EPSILON) ) return FALSE;	// 平面和中心圆点的距离比半径大的话,表示不在平截头体内.
	fDist = D3DXPlaneDotCoord( &m_plane[4], pv );
	if( fDist > (radius+PLANE_EPSILON) ) return FALSE;	// 平面和中心圆点的距离比半径大的话,表示不在平截头体内.
	fDist = D3DXPlaneDotCoord( &m_plane[5], pv );
	if( fDist > (radius+PLANE_EPSILON) ) return FALSE;	// 平面和中心圆点的距离比半径大的话,表示不在平截头体内.

	return TRUE;
}

/// 在画面上绘制平截头体.
BOOL ZFrustum::Draw( LPDIRECT3DDEVICE9 pDev )
{
	WORD		index[] = { 0, 1, 2,
							0, 2, 3,
							4, 7, 6,
							4, 6, 5,
							1, 5, 6,
							1, 6, 2,
							0, 3, 7,
							0, 7, 4,
							0, 4, 5,
							0, 5, 1,
							3, 7, 6,
							3, 6, 2 };

    D3DMATERIAL9 mtrl;
    ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) );

	typedef struct tagVTX
	{
		D3DXVECTOR3	p;
	} VTX;

	VTX		vtx[8];

	for( int i = 0 ; i < 8 ; i++ )
		vtx[i].p = m_vtx[i];

	pDev->SetFVF( D3DFVF_XYZ );
	pDev->SetStreamSource( 0, NULL, 0, sizeof(VTX) );
	pDev->SetTexture( 0, NULL );
	pDev->SetIndices( 0 );
	pDev->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_DISABLE );
	pDev->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
	pDev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE);
	pDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
	pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
	pDev->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

	// 上、下平面绘制为蓝色.
    pDev->SetRenderState( D3DRS_LIGHTING, TRUE );
    ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) );
    mtrl.Diffuse.b = mtrl.Ambient.b = 1.0f;
    pDev->SetMaterial( &mtrl );
	pDev->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, 8, 4, index, D3DFMT_INDEX16, vtx, sizeof( vtx[0] ) );

	// 左、右平面绘制为绿色.
    ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) );
    mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
    pDev->SetMaterial( &mtrl );
	pDev->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, 8, 4, index+4*3, D3DFMT_INDEX16, vtx, sizeof( vtx[0] ) );

	// 远、近平面绘制为红色.
    ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) );
    mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
    pDev->SetMaterial( &mtrl );
	pDev->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, 8, 4, index+8*3, D3DFMT_INDEX16, vtx, sizeof( vtx[0] ) );

	pDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
    pDev->SetRenderState( D3DRS_LIGHTING, FALSE );
	pDev->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
	return TRUE;
}

void ZMakePlane( D3DXPLANE* pPlane, D3DXVECTOR3* pv0, D3DXVECTOR3* pv1, D3DXVECTOR3* pv2 )
{
	D3DXPlaneFromPoints( pPlane, pv0, pv1, pv2 );
//  直接求取平面方程式的源代码
//	for OpenGL
//	D3DXVECTOR3	v0, v1, v2;
//	v1 = *pv1 - *pv0;
//	v2 = *pv2 - *pv0;
//	D3DXVec3Cross( &v0, &v1, &v2 );
//	D3DXVec3Normalize( &v0, &v0 );

//	pPlane->a = v0.x;
//	pPlane->b = v0.y;
//	pPlane->c = v0.z;
//	pPlane->d = -( v0.x * pv0->x + v0.y * pv0->y + v0.z * pv0->z );
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -