基于OpenGL显示平台功能开发-鼠标操作篇

本文介绍在OpenGL应用程序中实现用户交互的方法,包括通过鼠标操作完成场景模型的平移、旋转、缩放,以及如何在正交投影与透视投影之间进行切换。

基于OpenGL显示平台功能开发-鼠标操作篇

简介

前面章节已经讲了如何在MFC的 (View/Doc) 工程下搭建opengl应用程序,接下来主要讲如何对所搭建的应用程序添加用户操作,如场景模型支持鼠标的平移、旋转、缩放及透视投影与正交投影之间的相互切换功能。

场景的平移与旋转

对场景的平移与旋转操作就好比一个静止不动的相机拍摄物体,此时如果相机视场范围内的物体发生了位移,那么物体投影到相机里所成的像也相应地发生了位移,那么就可以认为对场景里的模型进行了平移,对模型的旋转也是一样的道理。在OpenGL中,对场景模型进行平移和旋转所对应的操作分别要用到glTranslatef(…)和glRotatef(…)两个函数,功能代码实现如下:

BOOL CMyOpenGLProjectView::RunScene()
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    // 实施投影变换,正投影,透视投影
    Projection();

    glPushMatrix();
    // 视图沿 x,y轴平移
    glTranslatef(m_xTranslation,m_yTranslation,0.0);

    // 场景绕 x,y,z轴旋转
    glRotatef( sceneRot[0], 1.0F, 0.0F, 0.0F );
    glRotatef( sceneRot[1], 0.0F, 1.0F, 0.0F );
    glRotatef( sceneRot[2], 0.0F, 0.0F, 1.0F );

    // 绘制场景
    glPushMatrix();
    m_pSceneRoot->Draw();
    glPopMatrix();

    glPopMatrix();  

    //交互缓冲区
    ::SwapBuffers(m_pDC->GetSafeHdc());     
    return TRUE;
}

场景的缩放

对场景的缩放不能简单地对模型直接用glScalef(…)函数来实现,这样虽然能实现物体的缩放功能,但并不是理想的效果。在实际应用中,我们可以调整相机与物体之间的距离或改变相机的视角来实现物体的缩放效果。但我们往往会选择后者,因为调整相机与物体之间的距离并不能保证物体始终处于相机的视眼范围之内,也就是在相机的近裁剪面与远裁剪面之外了,这样给用户的操作体验很不好。所以我们会从改变相机的观察视角来做这件事。
1. 正投影和透视投影
物体在相机上的投影可分为正投影和透视投影。

正投影可用如下相机模型来表示:
这里写图片描述
这个函数的原型为:
glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far) ;
六个参数, 前两个是x轴最小坐标和最大坐标,中间两个是y轴,最后两个是z轴值
它创建一个平行视景体(就是一个长方体空间区域)。
实际上这个函数的操作是创建一个正射投影矩阵,并且用这个矩阵乘以当前矩阵。
其中近裁剪平面是一个矩形,矩形左下角点三维空间坐标是(left,bottom,-near),
右上角点是(right,top,-near);远裁剪平面也是一个矩形,左下角点空间坐标是(left,bottom,-far),右上角点是(right,top,-far)。
注意,所有的near和far值同时为正或同时为负, 值不能相同。如果没有其他变换,正射投影的方向平行于Z轴,且视点朝向Z负轴。这意味着物体在视点前面时far和near都为负值,物体在视点后面时far和near都为正值。
只有在视景体里的物体才能显示出来。

透视投影可以用如下相机模型来表示:
这里写图片描述
这个函数原型为:
void glFrustum(GLdouble left, GLdouble Right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
创建一个透视型的视景体。其操作是创建一个透视投影的矩阵,并且用这个矩阵乘以当前矩阵。这个函数的参数只定义近裁剪平面的左下角点和右上角点的三维空间坐标,即(left,bottom,-near)和(right,top,-near);最后一个参数far是远裁剪平面的离视点的距离值,其左下角点和右上角点空间坐标由函数根据透视投影原理自动生成。near和far表示离视点的远近,它们总为正值(near/far 必须>0),
gluPerspective(…)是glFrustum(…)的一个特例。

场景缩放可以通过如下调整相机的视角来实现,实现代码如下:

BOOL CMyOpenGLProjectView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if (zDelta<0)
    {
        if (m_fViewAngle>=0.001f&&m_fViewAngle<2.5f)
        {
            m_fViewAngle+=0.1f;
        }
        else if (m_fViewAngle>=2.5f&&m_fViewAngle<179.0f)
        {
            m_fViewAngle += 2.5f;
        }
    }
    else 
    {
        if (m_fViewAngle>=0.001f&&m_fViewAngle<=2.5f)
        {
            m_fViewAngle -= 0.1f;
        }
        else if (m_fViewAngle >2.5f&&m_fViewAngle<=179.0f) 
            m_fViewAngle -= 2.5f;
    }

    if (m_fViewAngle<0.01f) m_fViewAngle = 0.01f;
    if (m_fViewAngle >=179.0f) m_fViewAngle = 179.0f;
    RunScene();
    return CView::OnMouseWheel(nFlags, zDelta, pt);
}

透视投影与正交投影的相互变换关系

根据相机的透视投影与正交投影模型,两者之间存在着特定的变换关系。实现代码如下:

void  CMyOpenGLProjectView::Projection()
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    if (m_iProjectionMode == _ORTHO)   // 正投影
    {
        float oAngle,oLeft,oRight,oBottom,oTop; 
        oAngle = m_fViewAngle*M_PI/180.0/2;
        oTop = camPos[2]*tan(oAngle);   // 1500表示相机距离原点位置
        oBottom = -oTop;
        oRight = oTop*m_width/m_height;
        oLeft = -oRight;
        glOrtho(oLeft,oRight,oBottom,oTop,1.0f,1000000.0f);
    }
    else     
    {
        float pAngle,pLeft,pRight,pBottom,pTop;
        pAngle = m_fViewAngle*M_PI/180.0/2;
        pTop = 1.0f*tan(pAngle);          //1.0f表示近距裁剪面
        pBottom = -pTop;
        pRight = pTop*m_width/m_height;
        pLeft = -pRight;
        glFrustum(pLeft,pRight,pBottom,pTop,1.0f,1000000.0f);
        //gluPerspective(45.0F, m_width/m_height, 1.0F, 1000000.0F);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(camPos[0],camPos[1],camPos[2],0,0,0,0,1,0);
}

这样在实际应用时,只要改变m_iProjectionMode 这个参数的传入值就可以实现场景的透视投影与正交投影的相互变换了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值