OpenGL拾取问题,请高手指点 我实现了OpenGL拾取三维模型的功能,但为什么每当鼠标点在模型之外的时候,本应该不拾取任何元素,但跟踪拾取的返回值,发现总是拾取到模型中第一个和最后一个绘制的元素(比如四边形),此时还未做深度的比较。我的选择模式的视景体和绘制模式下是一样的,无论作没作矩阵变换都有上面的情况。请高手指点一下。 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 第一步、在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);}编译执行程序,当在物体(茶壶)上单击鼠标左键时,提示拾取信息。 呃,我能看懂这些代码,而且我的程序和这个大致思路相同,但问题是:比如我绘制了一个立方体,它包括六个面,分别命名为1~6.,如果我鼠标点击立方体的外面,应该不选中任何东西,但其返回的selectBuf里面却总是有第一个和最后一个绘制的面,也就是selectBuf[0]=1,selectBuf[1]=6;这是为什么呢? 视景体设得不好吧。要不就是wglMakeCurrent()这个函数出了问题 请问一下,绘制模式和选择模式下的视景体和视点、参考点、视线向上向量都是一样的,但我在同一个地方,分别按绘制模式和选择模式下获得投影矩阵和模型矩阵,发现模型矩阵ModelMatrix是一样的,但投影矩阵ProjMatrix不一样,这可能会是什么造成的呢?? if(!m_pGLDC->IsSelectionMode())或者if(m_pGLDC->IsSelectionMode()) { glGetDoublev(GL_MODELVIEW_MATRIX,ModelMatrix); glGetDoublev(GL_PROJECTION_MATRIX,ProjMatrix); glGetIntegerv(GL_VIEWPORT,Viewport); } 窗口尺寸Viewport是一样的,而且得到的视景体的尺寸(width,height)也一样 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);} 问题原因已找到,但不知如何解决——是我在绘制模型后,又在屏幕左下角绘制了坐标轴,其中用到了glTranslate,影响了矩阵,但是我使用了操纵矩阵堆栈glPushMatrix();和glPopMatrix();难到不能消除其影响么?? 为什么 glSelectBuffer中的m_selectBuff总是随机数,选不中? 关于BOOL 关于键盘上下移动的实现???? 请问如果打仗的话,计算机的哪个专业最有用? 急!如何比较文件修改时间? 怎样完全去掉SDI中的菜单和工具栏? 关于调试的问题! 请问各位:能不能推荐一两本关于SDK的好书!(请说清楚:作者,书名,出版社)万分感谢! 各位大虾,在64位操作系统下,如何让主板发声 求教VC ADO数据操作语句报错问题! 如何使MFC单文档文字输出显示在视图客户区 Web无法得到 MFC ActiveX插件的事件?急救??? CFormView的Create问题
在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);
}
编译执行程序,当在物体(茶壶)上单击鼠标左键时,提示拾取信息。
要不就是wglMakeCurrent()这个函数出了问题
{
glGetDoublev(GL_MODELVIEW_MATRIX,ModelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX,ProjMatrix);
glGetIntegerv(GL_VIEWPORT,Viewport);
}
{
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);
}