我实现了OpenGL拾取三维模型的功能,但为什么每当鼠标点在模型之外的时候,本应该不拾取任何元素,但跟踪拾取的返回值,发现总是拾取到模型中第一个和最后一个绘制的元素(比如四边形),此时还未做深度的比较。我的选择模式的视景体和绘制模式下是一样的,无论作没作矩阵变换都有上面的情况。请高手指点一下。

解决方案 »

  1.   

    第一步、在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.lib、glu32.lib和glaux.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);
    glColor3f(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);
    }
    编译执行程序,当在物体(茶壶)上单击鼠标左键时,提示拾取信息。
      

  2.   

    呃,我能看懂这些代码,而且我的程序和这个大致思路相同,但问题是:比如我绘制了一个立方体,它包括六个面,分别命名为1~6.,如果我鼠标点击立方体的外面,应该不选中任何东西,但其返回的selectBuf里面却总是有第一个和最后一个绘制的面,也就是selectBuf[0]=1,selectBuf[1]=6;这是为什么呢?
      

  3.   

    视景体设得不好吧。
    要不就是wglMakeCurrent()这个函数出了问题
      

  4.   

    请问一下,绘制模式和选择模式下的视景体和视点、参考点、视线向上向量都是一样的,但我在同一个地方,分别按绘制模式和选择模式下获得投影矩阵和模型矩阵,发现模型矩阵ModelMatrix是一样的,但投影矩阵ProjMatrix不一样,这可能会是什么造成的呢?? if(!m_pGLDC->IsSelectionMode())或者if(m_pGLDC->IsSelectionMode())
    {
    glGetDoublev(GL_MODELVIEW_MATRIX,ModelMatrix);
    glGetDoublev(GL_PROJECTION_MATRIX,ProjMatrix);
    glGetIntegerv(GL_VIEWPORT,Viewport);
    }
      

  5.   

    窗口尺寸Viewport是一样的,而且得到的视景体的尺寸(width,height)也一样
      

  6.   

    void COpenGLDC::BeginSelection(int xPos,int yPos,UINT* items)
    {
          wglMakeCurrent(m_hDC,m_hRC);
       GLint viewport[4];
       glSelectBuffer(BUFFER_LENGTH,m_selectBuff);
       
    glGetIntegerv(GL_VIEWPORT,viewport);//得到当前窗口大小    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
     
    glRenderMode(GL_SELECT);
      gluPickMatrix(xPos,viewport[3]-yPos, 1, 1, viewport );//设置拾取矩阵 //设置选择视景体,和绘制模式下的视景体相同
    double left =  - m_width/2.0;
    double right =  m_width/2.0;
    double bottom =  - m_height/2.0;
    double top =  m_height/2.0; glOrtho(left,right,bottom,top,m_near,m_far);

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    gluLookAt(m_eye.x,m_eye.y,m_eye.z,m_ref.x,m_ref.y,m_ref.z,
    m_vecUp.dx, m_vecUp.dy, m_vecUp.dz);//设置视点 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glInitNames();//初始化名字堆栈
    glPushName((unsigned)-1);     glMatrixMode(GL_PROJECTION);
    int hits = glRenderMode(GL_RENDER);
    for(int i=0;i<hits;i++)
         {
    items[i] = m_selectBuff[i*4+3];
    }
    wglMakeCurrent(m_hDC,NULL);
    }
      

  7.   

    问题原因已找到,但不知如何解决——是我在绘制模型后,又在屏幕左下角绘制了坐标轴,其中用到了glTranslate,影响了矩阵,但是我使用了操纵矩阵堆栈glPushMatrix();和glPopMatrix();难到不能消除其影响么??

      

  8.   

    为什么 glSelectBuffer中的m_selectBuff总是随机数,选不中?