正文

虚拟相机的绘制方法(一)2010-05-14 16:49:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/yuqiexing/51130.html

分享到:



  一般,我们经常把看到的三维场景类比于相机拍照(
OpenGL编程指南第四版),因此视点变换也称为相机变换。后来有些人总是误解“视点”的概念,因为视点既可以理解为“相机(眼睛)所在的位置”,也可以理解为“相机(眼睛)所看到的地方”。前者是指相机位置,而后者则是场景中心(参见gluLookAt函数)。为了方便起见,本文不采用“视点”的概念,而用相机位置来代替说明。

 

  三维漫游的过程实际上就是相机运动的过程,包括相机位置和相机视线的运动改变。人眼所看到的三维动画就好比看到的DV录像。要知道,相机拍照是不会拍到自己的。(除非有镜子,排除这种情况)。但是有些时候,我们希望从全局看到相机本身的运动过程。因此,我们希望在场景中虚拟出这样一个对象来模拟相机的运动,称为“虚拟相机”(Virtual Camera.这样做有2个优点:一来可以帮助我们更好的宏观上了解和定制漫游路线和漫游效果,比如我们想指定,当相机经过A楼大门前,视线必须经过对面马路的红绿灯;另外,也可以检查漫游效果是否平滑,以及不平滑出现的位置和时间等。

 

  虚拟相机的绘制包括两个部分:视锥体和相机体。(参见下图:3DSMax中的虚拟相机)。其中视锥体为由近裁切面和远裁切面为底面构成的棱锥台,其轴线为视线方向。为方便模拟,一般近裁切面直接表示为一个点,该点位于相机体镜头的中心。而远裁切面由于一般设置很大,我们只需要控制在一定距离内进行示意即可,例如100m,或者在路径漫游时,如果用户设定了相机路径和目标路径,那么可以直接把“远裁切面”中心设置在目标路径上。

 


                                            图:3DSMax中的虚拟相机

 

 

  在运动过程中,相机体本身会发生位置和姿态变化,但形态不会变化,因此可以从外部文件(obj3ds)导入已经制作好的几何模型来表示,但是本文不建议这么做。因为数据量可能很大,降低场景绘制效率;而视锥体的形态、位置和姿态都在发生变化,可以每帧都直接绘制棱锥台来表示。因此本文建议整个虚拟相机都采用简单的几何体(primitive)来组合进行示意,包括镜身,连接处和镜头三个部分;同时绘制时建议采用线框模式。如果用填充方式,会产生一些不必要的场景遮挡,同时影响对相机运动过程的观察。

 

 


  本文将给出两种绘制方法。一种是直接利用GLU库进行绘制,另一种是根据应用程序自行设计的线、面绘制来进行组装(暂时略)。

 

  我们首先给出以Z轴为轴线,镜头中心在原点的虚拟相机绘制。因为相机的其他姿态可以直接通过平移和旋转变换得到。

 

long CRenderEngine::Draw3DCamera(CVirtualCamera* pCam, int m_nDrawMode)

{

       // 将相机位置从大地坐标系投影到OpenGL绘制坐标系

 

       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);

                    

                     glColor3f(rgb[0],rgb[1],rgb[2]);

 

                     // 直接用LineStrip绘制

 

                     glBegin(GL_LINE_STRIP);

 

                     glVertex3f(0,0,0);

 

                     glVertex3f(0,0,dViewLength);

 

                     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轴平行的虚拟相机了。

 


 

阅读(4105) | 评论(1)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册