三维漫游的过程实际上就是相机运动的过程,包括相机位置和相机视线的运动改变。人眼所看到的三维动画就好比看到的DV录像。要知道,相机拍照是不会拍到自己的。(除非有镜子,排除这种情况)。但是有些时候,我们希望从全局看到相机本身的运动过程。因此,我们希望在场景中虚拟出这样一个对象来模拟相机的运动,称为“虚拟相机”(Virtual Camera).这样做有2个优点:一来可以帮助我们更好的宏观上了解和定制漫游路线和漫游效果,比如我们想指定,当相机经过A楼大门前,视线必须经过对面马路的红绿灯;另外,也可以检查漫游效果是否平滑,以及不平滑出现的位置和时间等。 虚拟相机的绘制包括两个部分:视锥体和相机体。(参见下图:3DSMax中的虚拟相机)。其中视锥体为由近裁切面和远裁切面为底面构成的棱锥台,其轴线为视线方向。为方便模拟,一般近裁切面直接表示为一个点,该点位于相机体镜头的中心。而远裁切面由于一般设置很大,我们只需要控制在一定距离内进行示意即可,例如 在运动过程中,相机体本身会发生位置和姿态变化,但形态不会变化,因此可以从外部文件(obj,3ds)导入已经制作好的几何模型来表示,但是本文不建议这么做。因为数据量可能很大,降低场景绘制效率;而视锥体的形态、位置和姿态都在发生变化,可以每帧都直接绘制棱锥台来表示。因此本文建议整个虚拟相机都采用简单的几何体(primitive)来组合进行示意,包括镜身,连接处和镜头三个部分;同时绘制时建议采用线框模式。如果用填充方式,会产生一些不必要的场景遮挡,同时影响对相机运动过程的观察。 本文将给出两种绘制方法。一种是直接利用GLU库进行绘制,另一种是根据应用程序自行设计的线、面绘制来进行组装(暂时略)。 我们首先给出以Z轴为轴线,镜头中心在原点的虚拟相机绘制。因为相机的其他姿态可以直接通过平移和旋转变换得到。
long CRenderEngine::Draw3DCamera(CVirtualCamera* pCam, int m_nDrawMode) {
一般,我们经常把看到的三维场景类比于相机拍照(OpenGL编程指南第四版),因此视点变换也称为相机变换。后来有些人总是误解“视点”的概念,因为视点既可以理解为“相机(眼睛)所在的位置”,也可以理解为“相机(眼睛)所看到的地方”。前者是指相机位置,而后者则是场景中心(参见gluLookAt函数)。为了方便起见,本文不采用“视点”的概念,而用相机位置来代替说明。
图:3DSMax中的虚拟相机
CPoint3d camPoint = pCam->m_ptCamPos;
m_pProject->Project(camPoint);
// 将场景中心从大地坐标系投影到OpenGL绘制坐标系
CPoint3d tarPoint = pCam->m_ptTarPos;
m_pProject->Project(tarPoint);
// 绘制颜色
float rgb[3];
// 计算视线长度和方向,并进行单位化
CPoint3d vVec = tarPoint - camPoint;
double dDis = vVec.Length();
vVec /= dDis;
glPushMatrix();
{
// 绘制虚拟相机轴线
if(pCam->m_bShowAxies)
{
if(pCam->GetDrawMode() == MODE_HIGHLIGHT) // 高亮模式绘制
memcpy(rgb,pCam->m_camSelColor,sizeof(float)*3);
else // 普通模式绘制
memcpy(rgb,pCam->m_camPathColor,sizeof(float)*3);
glColor
// 直接用LineStrip绘制
glBegin(GL_LINE_STRIP);
glVertex
glVertex
glEnd();
}
// 绘制虚拟相机视椎体和相机体
if(pCam->m_bShowFrustum)
{
GLUquadricObj *qObj;
qObj = gluNewQuadric();
gluQuadricDrawStyle(qObj,GLU_SILHOUETTE);
// 获得当前相机fov
double currentFov = m_camera.GetFov();
// 获得当前相机宽高比
double dPerRatio = m_camera.GetAspectRatio();
// 计算当前相机在目标点处的裁切面高度
double dPerHeight = 2 * dDis * tan((currentFov/2) *
MathEx::dDegToRad);
// 绘制视椎体
glPushMatrix();
{
glScalef(dPerRatio,1.0,1.0);
glRotatef(45,0,0,1);
// 绘制以z轴为中心线的圆柱,底面位于平面z=0上,顶面位于z = dDis上。
gluCylinder(qObj,0,dPerHeight,dDis,4,4);
}
glPopMatrix();
// 绘制镜头
double dBomR = pCam->m_dBaseLength; // 虚拟相机基本大小
double dTopR = dBomR / 2.0;
double dHeight = dBomR / 1.5;
glPushMatrix();
{
glScalef(1.5,1.0,1.0);
glRotatef(45,0,0,1);
gluCylinder(qObj,dBomR,dTopR,dHeight,4,4);
}
glPopMatrix();
glPushMatrix();
{
double dCylHeight = pCam->m_dBaseLength / 2;
// 绘制连接处1
glTranslatef(0,0,dHeight);
glPushMatrix();
{
glRotatef(45,0,0,1);
gluCylinder(qObj,dTopR / 1.2,dTopR /
1.2,dCylHeight,4,4);
}
glPopMatrix();
dCylHeight *= 1.2;
// 绘制连接处2
glTranslatef(0,0,dCylHeight);
glPushMatrix();
{
glRotatef(45,0,0,1);
gluCylinder
(qObj,dTopR/1.5,dTopR/1.5,dCylHeight,4,4);
}
glPopMatrix();
}
glPopMatrix();
gluDeleteQuadric(qObj);//释放内存空间
}
glPopMatrix();
}
return 1;
}
这样我们就可以绘制一个位置在原点、视线方向与z轴平行、up分量与x轴平行的虚拟相机了。
评论