方法一: 可以根据视口四个角坐标计算出平截头体8个角坐标:
根据视口获取平截头体6面方程
//获取视口宽高
double dWidth = pCamera->getViewport()->width();
double dHeight =pCamera->getViewport()->height();
//平截头体近裁剪面左上角坐标
osg::Vec3d windowPosLeftUp1(0.0,dHeight, -1.0 );
osg::Vec3d wordPLeftUpNear = OsgMathUtility::windowPos2WorldPos(pCamera, windowPosLeftUp1);
//平截头体远裁剪面左上角坐标
osg::Vec3d windowPosLeftUp2(0.0, dHeight, 1.0);
osg::Vec3d wordPLeftUpFar = OsgMathUtility::windowPos2WorldPos(pCamera, windowPosLeftUp2);
平截头体近裁剪左下角
osg::Vec3d windowPosLeftBottom1(0.0, 0.0, -1.0);
osg::Vec3d wordPLeftBottomNear = OsgMathUtility::windowPos2WorldPos(pCamera, windowPosLeftBottom1);
平截头体远裁剪左下角
osg::Vec3d windowPosLeftBottom2(0.0,0.0, 1.0);
osg::Vec3d wordPLeftBottomFar = OsgMathUtility::windowPos2WorldPos(pCamera, windowPosLeftBottom2);
平截头体近裁剪右上角
osg::Vec3d windowPosRightUp1(dWidth, dHeight, -1.0);
osg::Vec3d wordPRightUpNear = OsgMathUtility::windowPos2WorldPos(pCamera, windowPosRightUp1);
osg::Vec3d windowPosRightUp2(dWidth, dHeight, 1.0);
osg::Vec3d wordPRightUpFar = OsgMathUtility::windowPos2WorldPos(pCamera, windowPosRightUp2);
平截头体近裁剪右下角
osg::Vec3d windowPosRightBottom1(dWidth, 0.0, -1.0);
osg::Vec3d wordPRightBottomNear = OsgMathUtility::windowPos2WorldPos(pCamera, windowPosRightBottom1);
osg::Vec3d windowPosRightBottom2(dWidth, 0.0, 1.0);
osg::Vec3d wordPRightBottomFar = OsgMathUtility::windowPos2WorldPos(pCamera, windowPosRightBottom2);
///再根据坐标转成向量坐标(注:根据右手向量相乘判断向量方向)
//计算平截头体远平面
OsgPlane* pPlaneFar1 = new OsgPlane();
if (pPlaneFar1)
pPlaneFar1->redeFine(wordPRightUpFar,wordPLeftUpFar, wordPRightBottomFar );
//计算平截头体近平面
OsgPlane* pPlaneNear1 = new OsgPlane();
if (pPlaneNear1)
pPlaneNear1->redeFine(wordPRightUpNear, wordPRightBottomNear, wordPLeftUpNear);
//计算平截头体左平面
OsgPlane* pPlaneLeft1 = new OsgPlane();
if (pPlaneLeft1)
pPlaneLeft1->redeFine(wordPLeftUpNear, wordPLeftBottomNear, wordPLeftUpFar );
//计算平截头右平面
OsgPlane* pPlaneRight1 = new OsgPlane();
if (pPlaneRight1)
pPlaneRight1->redeFine(wordPRightUpNear, wordPRightUpFar, wordPRightBottomNear);
//计算平截头上平面
OsgPlane* pPlaneUp1 = new OsgPlane();
if (pPlaneUp1)
pPlaneUp1->redeFine(wordPRightUpNear, wordPLeftUpFar, wordPRightUpFar);
//计算平截头下平面
OsgPlane* pPlaneBotom1 = new OsgPlane();
if (pPlaneBotom1)
pPlaneBotom1->redeFine(wordPRightBottomNear, wordPRightBottomFar, wordPLeftBottomNear);
方法二:根据视口矩阵和投影矩阵,相乘获取裁剪空间矩阵,最后再根据矩阵值求出平截头体6个面方程:
1、 裁剪矩阵 = 视图矩阵 × 投影矩阵
2、3D世界上的点p =(x,y,z,1)。还考虑一个模型视图矩阵中号和投影矩阵P。使用以下公式,通过矩阵视图矩阵V和投影矩阵P将点p转换为剪辑空间中的点pc =(xc,yc,zc,wc):
pc = pVP
点pc是 齐次坐标,当归一化时变为pcn:
pcn = (xc/wc,yc/wc zc/wc) = (x’, y’, z’)
3、在标准化的剪辑空间中,视锥体是以原点为中心的轴对齐框,并由以下平面限定:
左平面:x'= -1
右平面:x'= 1
顶部平面:y'= 1
底部平面:y'= -1
近平面:z'= -1
远平面:z'= 1
这意味着点pcn =(x’,y’,z’)在视锥体内,如果:
-1< x'<1
-1<y'< 1
-1<z'< 1
在非标准化坐标中,点pc必须满足以下条件才能在视锥体内:
-wc<xc<wc
-wc<yc<wc
-wc<zc<wc
基于该信息,可以在世界坐标中提取限制视锥体的六个平面。如果点pc位于左平面的“右”侧: -wc<xc
其他各平面如上方法计算。
//获取裁剪矩阵
osg::Matrixd VPmat = pCamera->getViewMatrix()*pCamera->getProjectionMatrix();
double XX(0.0),YY(0.0),ZZ(0.0),dd(0.0);
//计算平截头左平面
OsgPlane* pPlaneLeft = m_mapFrustumPlane[FrustumPlane::PLANE_LEFT];
if (pPlaneLeft)
{
XX = VPmat(0,3)+ VPmat(0,0); // 注(row, col)
YY = VPmat(1,3)+ VPmat(1,0); // 注(row, col)
ZZ = VPmat(2,3)+ VPmat(2,0); // 注(row, col)
dd = VPmat(3,3)+ VPmat(3,0); // 注(row, col)
osg::Vec3d normal(XX, YY, ZZ);
dd /= normal.normalize();
pPlaneLeft->redeFine(normal, dd);
}
//计算平截头右平面
OsgPlane* pPlaneRight = m_mapFrustumPlane[FrustumPlane::PLANE_RIGHT];
if (pPlaneRight)
{
XX = VPmat(0,3) - VPmat(0,0);
YY = VPmat(1,3) - VPmat(1,0);
ZZ = VPmat(2,3) - VPmat(2,0);
dd = VPmat(3,3) - VPmat(3,0);
osg::Vec3d normal(XX, YY, ZZ);
dd /= normal.normalize();
pPlaneRight->redeFine(normal, dd);
}
//计算平截头上平面
OsgPlane* pPlaneUp = m_mapFrustumPlane[FrustumPlane::PLANE_UP];
if (pPlaneUp)
{
XX = VPmat(0,3)- VPmat(0,1);
YY = VPmat(1,3)- VPmat(1,1);
ZZ = VPmat(2,3)- VPmat(2,1);
dd = VPmat(3,3)- VPmat(3,1);
osg::Vec3d normal(XX, YY, ZZ);
dd /= normal.normalize();
pPlaneUp->redeFine(normal, dd);
}
//计算平截头下平面
OsgPlane* pPlaneBotom = m_mapFrustumPlane[FrustumPlane::PLANE_BOTTOM];
if (pPlaneBotom)
{
XX = VPmat(0,3)+ VPmat(0,1);
YY = VPmat(1,3)+ VPmat(1,1);
ZZ = VPmat(2,3)+ VPmat(2,1);
dd = VPmat(3,3)+ VPmat(3,1);
osg::Vec3d normal(XX, YY, ZZ);
dd /= normal.normalize();
pPlaneBotom->redeFine(normal, dd);
}
//计算平截头体近平面
OsgPlane* pPlaneNear = m_mapFrustumPlane[FrustumPlane::PLANE_NEAR];
if (pPlaneNear)
{
XX = VPmat(0,3) + VPmat(0,2);
YY = VPmat(1,3) + VPmat(1,2);
ZZ = VPmat(2,3) + VPmat(2,2);
dd = VPmat(3,3) + VPmat(3,2);
osg::Vec3d normal(XX, YY, ZZ);
dd /= normal.normalize();
pPlaneNear->redeFine(normal, dd);
}
//计算平截头体远平面
OsgPlane* pPlaneFar = m_mapFrustumPlane[FrustumPlane::PLANE_FAR];
if (pPlaneFar)
{
XX = VPmat(0,3) - VPmat(0,2);
YY = VPmat(1,3) - VPmat(1,2);
ZZ = VPmat(2,3) - VPmat(2,2);
dd = VPmat(3,3) - VPmat(3,2);
osg::Vec3d normal(XX, YY, ZZ);
dd /= normal.normalize();
pPlaneFar->redeFine(normal, dd);
}