OpenGL中的选择模式及其应用

OpenGL中的选择模式及其应用

一、概述

OpenGL中绘制物体通常是在其缺省的绘图模式下进行的,而为了对物体进行标记以区分在指定区域上绘制了那些物体,则需要进入选择模式;选择模式为用户提供了一种拾取物体的机制。下面通过先列出应用选择模式的一般步骤,再通过一个例子具体说明选择模式的应用。

二、应用选择模式的一般步骤

1、创建返回命中记录的数组,由glSelectBuffer()函数实现;

2、进入选择模式,由glRenderMode (GL_SELECT)实现;

3、使用glInitNames()glPushName()初始化名称堆栈;

4、定义用于选择模式的视景体,拾取区域;

5、建立名称堆栈;

6、推出选择模式,处理返回的信息(命中记录)。

三、应用实例

第一步、在VC中建立一个名为Selection的单文档工程

SelectionView.h文件头部添加:

#include "gl/gl.h"

#include "gl/glu.h"

#include "gl/glaux.h"

 

#include "math.h"

#define DR 0.01745329252//pi/180

打开菜单Project,选择Settings,在弹出的对话框中选择Link标签,在Object/Libaray Modules 栏中增加OpenGL32.libglu32.libglaux.lib几个文件。

第二步、OpenGL初始化

CSelectionView类中声明Public型成员变量:

CClientDC   *m_pDC;

CRect           rect;

GLdouble     fovy;      //视场角

GLfloat        aspect;   //高度与宽度的比率

GLdouble     zNear;    //近剪切面离视点的距离  

GLdouble     zFar;      //远剪切面离视点的距离    

GLdouble     eyex,eyey,eyez;//视点

GLdouble     lookat_X,lookat_Y,lookat_Z;//观察点

GLdouble     A,E,R;   //观察点相对于视点的方位角、高低角、距离

GLdouble     xMove,yMove,zMove;//x,y,z方向的偏移量

int                 What_flag;

添加保护成员函数:

BOOL bSetupPixelFormat();//设置像素格式

void Init();//初始化

GLvoid ReSize();

GLvoid Calculate();

void CreateSence();

void DrawNameObjects(GLenum mode);

void DrawScene();//绘场景

void WhatHits(Glint hits, GLuint* buffer);//拾取提示

编辑以上函数:

BOOL CSelectionView::bSetupPixelFormat()

{

       static PIXELFORMATDESCRIPTOR pfd =

       {

        sizeof(PIXELFORMATDESCRIPTOR), 

        1,

        PFD_DRAW_TO_WINDOW |PFD_SUPPORT_OPENGL|

        PFD_DOUBLEBUFFER,

        PFD_TYPE_RGBA,

        24,

        0, 0, 0, 0, 0, 0,

        0,

        0,

        0,

        0, 0, 0, 0,

        32,

        0,

        0,

        PFD_MAIN_PLANE,

        0,

        0, 0, 0

    };//参数的含义请参看联机帮助

    int pixelformat;

    if ((pixelformat=ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd)) == 0)

    {

        MessageBox("ChoosePixelFormat failed");

        return FALSE;

    }

    if (SetPixelFormat(m_pDC->GetSafeHdc(), pixelformat, &pfd) == FALSE)

    {

        MessageBox("SetPixelFormat failed");

        return FALSE;

    }

    return TRUE;

}

 

void CSelectionView::Init()

{

       PIXELFORMATDESCRIPTOR pfd;

    int         n;

       HGLRC              hrc;

    //设置像素格式

       m_pDC = new CClientDC(this);

    ASSERT(m_pDC != NULL);

    if (!bSetupPixelFormat()) return;

       //创建并设置着色描述表

    n =::GetPixelFormat(m_pDC->GetSafeHdc());

    ::DescribePixelFormat(m_pDC->GetSafeHdc(), n, sizeof(pfd), &pfd);

    hrc = wglCreateContext(m_pDC->GetSafeHdc());

    wglMakeCurrent(m_pDC->GetSafeHdc(), hrc);

}

 

GLvoid CSelectionView::ReSize()

{

       //OpenGL视口及视景体

       GetClientRect(rect);

    aspect = (GLfloat) rect.right/rect.bottom;

    glViewport(0,0,rect.right,rect.bottom);

       glMatrixMode(GL_PROJECTION);

       glLoadIdentity();

    gluPerspective(fovy,aspect,zNear,zFar);

}

 

GLvoid CSelectionView::Calculate()

{

       eyex=lookat_X+R*cos(A*DR)*cos(E*DR);

       eyey=lookat_Y+R*sin(A*DR)*cos(E*DR);

       eyez=lookat_Z+R*sin(E*DR);

}

 

void CSelectionView::CreateSence()

{  

       glNewList(1,GL_COMPILE_AND_EXECUTE);

              glDisable(GL_LIGHTING);

              glDisable(GL_DEPTH_TEST);

              glColor 3f ( 0.0f , 1.0f , 0.0f );

              glPushMatrix();

                     auxSolidTeapot(5.0);   

              glPopMatrix();

              glEnable(GL_DEPTH_TEST);

              glEnable(GL_LIGHTING);

       glEndList();

}

 

void CSelectionView::DrawNameObjects(GLenum mode)

{

       if(mode == GL_SELECT) glLoadName(1);

       for(int j=1;j<=1;j++)//循环显示列表

       {

              if(mode == GL_SELECT)glPushName(j);

              glCallList(j);

              if(mode == GL_SELECT)glPopName();

       }

}

 

void CSelectionView::DrawScene()

{

       glClearColor( 0.0f , 0.0f , 0.0f , 1.0f );

       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 

       glMatrixMode(GL_MODELVIEW);

       glLoadIdentity();

       Calculate();

       gluLookAt(eyex+xMove,eyey+yMove,eyez+zMove,

                   lookat_X+xMove,lookat_Y+yMove,lookat_Z+zMove,

                     0.0,0.0,1.0);

    DrawNameObjects(GL_RENDER);

 

    glFinish();

       SwapBuffers(wglGetCurrentDC());

}

 

void CSelectionView::WhatHits(GLint hits, GLuint* buffer)

{

       What_flag=0;

    if(hits==0) return;

       for(int i=0;i<hits;i++) What_flag=(*(buffer+4+i*5));

}

变量初始化:

CSelectionView::CSelectionView()

{

       m_pDC=NULL;

       fovy=60.0;

       zNear=1.0;   //近剪切面离视点的距离  

       zFar=1000.0;//远剪切面离视点的距离    

       eyex=eyey=eyez=0.0;

       lookat_X=lookat_Y=lookat_Z=0.0;

       A=270.0;E=75.0;R=80.0;

       xMove=yMove=zMove=0.0;

}

CSelectionView类中添加WM_CREATE消息响应函数OnCreate

int CSelectionView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

       if (CView::OnCreate(lpCreateStruct) == -1) return -1;

       Init();

       CreateSence();

       return 0;

}

编辑OnDraw函数如下:

void CSelectionView::OnDraw(CDC* pDC)

{

       CSelectionDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       DrawScene();

    pDC->SetTextColor(RGB(255,0,90));

    pDC->SetBkMode(TRANSPARENT);

       switch(What_flag)

       {

       case 1: MessageBox("拾取茶壶");break;

       default:break;

       }

}

添加WM_LBUTTONDOWN消息响应函数OnLButtonDown ,该函数完成拾取模式的主要步骤:

void CSelectionView::OnLButtonDown(UINT nFlags, CPoint point)

{

       GLuint selectBuf[512];

       GLint hits;

       GLint viewport[4];

       glGetIntegerv (GL_VIEWPORT, viewport);

     glSelectBuffer (512, selectBuf);

       glRenderMode (GL_SELECT);

       glInitNames();

       glPushName((unsigned)-1);

       glMatrixMode (GL_PROJECTION);

       glPushMatrix();

              glLoadIdentity();

              gluPickMatrix(point.x,viewport[3]-point.y,5.0,5.0, viewport);

              gluPerspective(fovy, aspect,zNear,zFar);

              glMatrixMode(GL_MODELVIEW);

              glLoadIdentity();

              gluLookAt(eyex+xMove,eyey+yMove,eyez+zMove,

                           lookat_X+xMove,lookat_Y+yMove, lookat_Z+zMove,

                            0.0,0.0,1.0);

              DrawNameObjects(GL_SELECT);

              glMatrixMode(GL_PROJECTION);

       glPopMatrix ();

       hits = glRenderMode(GL_RENDER);

       WhatHits (hits,selectBuf);

       Invalidate();

       CView::OnLButtonDown(nFlags, point);

}

添加WM_SIZE消息响应函数OnSize

void CSelectionView::OnSize(UINT nType, int cx, int cy)

{

       CView::OnSize(nType, cx, cy);

       ReSize();

}

为避免闪烁添加WM_ERASEBKGND消息响应函数OnEraseBkgnd

BOOL CSelectionView::OnEraseBkgnd(CDC* pDC)

{

       return TRUE;//CView::OnEraseBkgnd(pDC);

}

编译执行程序,当在物体(茶壶)上单击鼠标左键时,提示拾取信息。

四、结束语

本文以gluPerspective透视投影为例介绍了OpenGL中的拾取模式,其他投影变换与此类似;需注意的是,为了能在场景变化的情况下正确拾取物体,绘图模式和拾取模式的透视投影应该一致。读者可添加光照、纹理、贴图等以改善显示效果。

读者可通过改变例子中的视场角、视点、观察点和方位角、高低角、距离以及x,y,z方向的偏移量等的值体验变场景下的物体拾取;也可参看例子中的OnKeyDown函数。该函数通过小键盘上的“+”、“-”键完成距离(焦距)的增减,通过方向键高低、方位角的增减,通过小键盘上的“*”、“/”键完成视场角的增减等。

例子程序在Windows2000VC6.0下调试通过。

 

 
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值