OpenGL拾取遇到点麻烦 (找错误)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenGL拾取遇到点麻烦 (找错误)相关的知识,希望对你有一定的参考价值。
我做一个鼠标拾取的程序,但总是拾取错误。
问题简单描述下,我用的Win32做的一个图形程序,我的绘制图形的函数代码如下
void OpenGL::Draw(GLfloat position_x,GLfloat position_y,GLfloat position_z,GLfloat angle,GLfloat angle_xz,GLfloat distance,GLfloat jump)
//这个结构是主显示渲染过程
glClearColor(0.5f,0.5f,0.5f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glInitNames();
glPushName(0);
glRotatef(angle_xz,1.0f,0.0f,0.0f);
glRotatef(angle,0.0f,1.0f,0.0f);
glTranslatef(0.0f,0.0f-jump,0);
glTranslatef(position_x+0.0f,position_y-1.0f,position_z);
//glPushMatrix();
//glTranslatef(5.0f,0.0f,0.0f);
//zhuzi(1.0f,1.0f,10.0f,10,10);
//glPopMatrix();
//地面绘制
glBindTexture(GL_TEXTURE_2D, texture[0]);
glBegin(GL_QUADS);
glTexCoord2f(100,100);
glVertex3f(50.0f,0.0f,50.0f);
glTexCoord2f(-100,100);
glVertex3f(-50.0f,0.0f,50.0f);
glTexCoord2f(-100,-100);
glVertex3f(-50.0f,0.0f, -50.0f);
glTexCoord2f(100,-100);
glVertex3f(50.0f,0.0f, -50.0f);
glEnd();
glPushMatrix();
glLoadName(1);
glBegin(GL_TRIANGLES);
glVertex3f(0.0f,0.0f,0.0f);
glVertex3f(1.0f,1.0f,0.0f);
glVertex3f(1.0f,0.0f,0.0f);
glEnd();
glPopMatrix();
glPopMatrix(); //恢复最初模型矩阵
SwapBuffers(hDC);
我在响应了一个鼠标左键的电击事件,事件代码如下
GLuint selectBuff[1024];
GLint hits, viewport[4];
glSelectBuffer(1024, selectBuff);
glGetIntegerv(GL_VIEWPORT, viewport);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glRenderMode(GL_SELECT);
glLoadIdentity();
gluPickMatrix(x, viewport[3] - y, 2,2, viewport);
gluPerspective(45.0f,(GLfloat)800/(GLfloat)600,0.1f,200.0f);
gl.Draw(camera.x,camera.y,camera.z,camera.angle,camera.angle_xz,camera.distance,camera.jump);
hits = glRenderMode(GL_RENDER);
if(hits == 1)
int i = hits; //这里出问题
// Restore the projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();
// Go back to modelview for normal rendering
glMatrixMode(GL_MODELVIEW);
我设置了断点,发现每次拾取的时候总是拾取一个命名为0的图元,我并没有命名0号图元啊,我想拾取我绘图函数中最后一个三角形,但总不成功,我还找不到问题的所在,希望达人指点下,不胜感激~~
我想知道我程序哪里出问题了,拾取的例子我有很多,但我的程序哪里出问题了我总找不到答案,希望有人能找到我程序的问题,谢谢了
我想要鼠标移动到 每个方块时方块改变颜色 移开时颜色在回复原来的样子
可是这个 程序只是在移动到方块上时改变颜色 而移动开时没有任何的变化
想了 很久不知道该如何解决这个问题
希望各位路过的哥哥 可以帮我看看 给我个解决的方法 谢谢
#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>
int board[3][3];
void init(void)
int i,j;
for(i=0;i<3;i++)
for(j=0;j<3;j++)
board[i][j] = 0;
glClearColor(0.0, 0.0, 0.0, 0.0);
void drawSquares(GLenum mode)
GLuint i,j;
for (i=0;i<3;i++)
if(mode == GL_SELECT)
glLoadName(i);
for(j=0;j<3;j++)
if(mode == GL_SELECT)
glPushName(j);
glColor3f((GLfloat)i/3.0, (GLfloat)j/3.0,
(GLfloat)board[i][j]/3.0);
glRecti(i,j,i+1,j+1);
if(mode == GL_SELECT)
glPopName();
void processHits(GLint hits,GLuint buffer[])
GLint i,j;
GLuint ii = 0,jj = 0,names,*ptr;
printf("hits = %d\n",hits);
ptr = (GLuint *) buffer;
for(i=0;i < hits;i++)
names = *ptr;
printf(" number of names for this hit = %d\n", names);
ptr++;
ptr++;
ptr++;
printf(" names are ");
for (j =0;j<names;j++)
printf("%d ", *ptr);
if (j==0)
ii = *ptr;
else if (j == 1)
jj = *ptr;
ptr++;
printf("\n");
board[ii][jj] = 1;
#define BUFSIZE 512
void pickSquares(int x,int y)
GLuint selectBuf[BUFSIZE];
GLuint hits;
GLint viewport[4];
//if(hits >= 10)
// return;
glGetIntegerv(GL_VIEWPORT, viewport);
glSelectBuffer(BUFSIZE,selectBuf);
(void) glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPickMatrix((GLdouble) x,(GLdouble)(viewport[3]-y),
5.0,5.0, viewport);
gluOrtho2D(0.0,3.0,0.0,3.0);
drawSquares(GL_SELECT);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glFlush();
hits = glRenderMode(GL_RENDER);
processHits(hits,selectBuf);
glutPostRedisplay();
void display(void)
glClear(GL_COLOR_BUFFER_BIT);
drawSquares(GL_RENDER);
glFlush();
void reshape(int w,int h)
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,3.0,0.0,3.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
int main(int argc,char** argv)
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(200,200);
glutInitWindowPosition(100,100);
glutCreateWindow(argv[0]);
init();
glutPassiveMotionFunc(pickSquares);
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutMainLoop();
return 0;
作者:eastcowboy 发表时间:2008-9-22 22:10:00
第1楼
/*
OpenGL演示程序
使用到的技术
1. 多窗口显示
2. OpenGL选择功能
3. 根据二维的窗口坐标计算出三维坐标
操作步骤
1. 程序运行后可以看到左边的“工具栏”和右边的“主窗口”
2. 在工具栏中用鼠标点击任意物体,即可选择该物体。
3. 在主窗口中移动鼠标,如果有物体被选择,则将该物体的“预览”绘制到鼠标处
4. 在主窗口中按下鼠标,如果有物体被选择,则在鼠标处添加一个与选择物体相同的物体
5. 最多可以添加MAX_OBJECT个物体
编程语言:C语言
调试环境:软件 -- Windows XP SP2 + Visual Studio 2005 SP1英文 + GLUT工具包3.7版
硬件 -- 奔腾4 3.0G + DDR 2G + IBM945G集成显卡
*/
//============================================================================
// 所需要的头文件
//============================================================================
#include <gl/glut.h>
#include <stdio.h>
//============================================================================
// 常量定义
//============================================================================
// 最大可以添加的物体数
#define MAX_OBJECT 10
//============================================================================
// 类型定义
//============================================================================
// “物体”数据,保存了物体的类型和位置
typedef struct _tag_Object
int type; // 1 - 茶壶, 2 - 球, 3 - 圆锥
GLdouble pos_x;
GLdouble pos_y;
GLdouble pos_z;
Object;
//============================================================================
// 全局变量
//============================================================================
// 当前已经添加的物体(数组)
Object object_list[MAX_OBJECT];
// 当前已经添加的物体数量
int object_count = 0;
// 主窗口中鼠标的位置(x, y)
int mouseX_AtMain = 0;
int mouseY_AtMain = 0;
// 当前选择的物体类型(0 - 未选择, 1 - 茶壶, 2 - 球, 3 - 圆锥)
int select_object_type = 0;
作者:eastcowboy 发表时间:2008-9-22 22:11:00
第2楼
//============================================================================
// 左边“菜单栏”所需要的函数
//============================================================================
// 画出三个可以选择的物体
// 注意本函数有两个地方会用到。一是正常的绘制,二是用于OpenGL选择模式。
// 因此会用glLoadName这样的函数
void __left_display()
static const GLfloat color_selected[] = 0.75f, 0.75f, 0.95f;
static const GLfloat color_unselected[] = 0.4f, 0.4f, 0.4f;
// 画一个茶壶
glLoadName(1);
glColor3fv( (select_object_type==1) ? color_selected : color_unselected );
glPushMatrix();
glTranslatef(75.0f, 470.0f*3/4, 0);
glutSolidTeapot(30);
glPopMatrix();
// 画一个球
glLoadName(2);
glColor3fv( (select_object_type==2) ? color_selected : color_unselected );
glPushMatrix();
glTranslatef(75.0f, 470.0f/2, 0);
glutSolidSphere(30.0, 20, 20);
glPopMatrix();
// 画一个圆锥
glLoadName(3);
glColor3fv( (select_object_type==3) ? color_selected : color_unselected );
glPushMatrix();
glTranslatef(75.0f, 470.0f/4, 0);
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
glutSolidCone(30.0, 50.0, 30, 1);
glPopMatrix();
// 菜单栏需要绘制时调用此函数
void left_display(void)
glClear(GL_COLOR_BUFFER_BIT);
__left_display();
glutSwapBuffers();
glutReportErrors();
// 在鼠标点击菜单栏时调用此函数
// 函数中会使用OpenGL的选择功能来判断哪个物体被选中
void left_mouse(int button, int state, int x, int y)
GLdouble saved_projection_matrix[16];
GLint viewport[4];
GLuint select_buffer[512];
GLint buffer_count;
if( state != GLUT_DOWN )
return;
glGetDoublev(GL_PROJECTION_MATRIX, saved_projection_matrix);
glGetIntegerv(GL_VIEWPORT, viewport);
y = viewport[3] - y;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPickMatrix(x, y, 1, 1, viewport);
glMultMatrixd(saved_projection_matrix);
glMatrixMode(GL_MODELVIEW);
glSelectBuffer(
sizeof(select_buffer)/sizeof(select_buffer[0]), select_buffer);
glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
__left_display();
buffer_count = glRenderMode(GL_RENDER);
if( buffer_count < 0 )
printf("OpenGL 选择缓冲区溢出\n");
else if( buffer_count == 0 )
select_object_type = 0;
printf("未选择任何物体\n");
else
int i;
GLuint* ptr = select_buffer;
for(i=0; i<buffer_count; ++i)
int name = select_buffer[i*4+3];
switch(name)
case 1:
printf("选择了茶壶\n");
select_object_type = 1;
break;
case 2:
printf("选择了球\n");
select_object_type = 2;
break;
case 3:
printf("选择了圆锥\n");
select_object_type = 3;
break;
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glutPostRedisplay();
作者:eastcowboy 发表时间:2008-9-22 22:11:00
第3楼
//============================================================================
// 左边“菜单栏”所需要的函数
//============================================================================
// 根据二维的窗口坐标计算出三维坐标,
// 计算结果放到pObj->pos_x, pObj->pos_y, pObj->pos_z中
// 注意:要正确使用本函数,必须开启深度测试。
// 即必须分配深度缓冲区,并且使用glEnable(GL_DEPTH_TEST)
void calc_xyz(int winx, int winy, Object* pObj)
GLdouble modelview_matrix[16];
GLdouble projection_matrix[16];
GLint viewport[4];
GLfloat winz;
glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
glGetIntegerv(GL_VIEWPORT, viewport);
glReadPixels(winx, winy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winz);
gluUnProject(winx, winy, winz,
modelview_matrix, projection_matrix, viewport,
&(pObj->pos_x), &(pObj->pos_y), &(pObj->pos_z));
// 根据物体类型的不同,绘制出不同的物体
// 1 - 茶壶, 2 - 球, 3 - 圆锥
void draw_object(int type)
switch( type )
case 1:
glColor3f(0.2f, 0.7f, 0.5f);
glTranslatef(0.0f, 0.3f, 0.0f);
glutSolidTeapot(0.3);
break;
case 2:
glColor3f(0.3f, 0.6f, 0.8f);
glTranslatef(0.0f, 0.3f, 0.0f);
glutSolidSphere(0.3, 20, 20);
break;
case 3:
glColor3f(0.6f, 0.6f, 0.3f);
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
glutSolidCone(0.3, 0.4, 30, 1);
break;
// 鼠标在主窗口中移动时,记录鼠标的位置
// 如果选择了物体类型,则通过glutPostRedisplay重新绘制主窗口,
// 绘制时会把所选的物体绘制在鼠标位置处
void main_passiveMotion(int x, int y)
mouseX_AtMain = x;
mouseY_AtMain = 470 - y;
if( select_object_type != 0 )
glutPostRedisplay();
// 鼠标点击主窗口,尝试添加物体
// 若添加成功则调用glutPostRedisplay重新绘制,以便让添加的物体被绘制出来
void main_mouse(int button, int state, int x, int y)
if( state != GLUT_DOWN )
return;
if( !select_object_type )
return;
if( object_count >= MAX_OBJECT )
printf("物体数量达到最大,无法再添加\n");
return;
object_list[object_count].type = select_object_type;
calc_xyz(x, 470 - y, &object_list[object_count]);
printf("在(%f, %f, %f)处增加物体。\n",
object_list[object_count].pos_x,
object_list[object_count].pos_y,
object_list[object_count].pos_z);
++object_count;
glutPostRedisplay();
// 主窗口需要绘制时调用本函数
void main_display(void)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// disable lighting
glDisable(GL_LIGHTING);
// ground
glColor3f(0.4f, 0.2f, 0.2f);
glBegin(GL_QUADS);
glVertex3f(-2.0f, 0.0f, -2.0f);
glVertex3f(-2.0f, 0.0f, 2.0f);
glVertex3f( 2.0f, 0.0f, 2.0f);
glVertex3f( 2.0f, 0.0f, -2.0f);
glEnd();
// mouse object
if( select_object_type != 0 )
Object dummy;
calc_xyz(mouseX_AtMain, mouseY_AtMain, &dummy);
glPushMatrix();
glTranslated(dummy.pos_x, dummy.pos_y, dummy.pos_z);
draw_object(select_object_type);
glPopMatrix();
// enable lighting
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
// objects
int i;
glColor3f(1.0f, 1.0f, 0);
for(i=0; i<object_count; ++i)
glPushMatrix();
glTranslated(object_list[i].pos_x,
object_list[i].pos_y,
object_list[i].pos_z);
draw_object(object_list[i].type);
glPopMatrix();
glutSwapBuffers();
glutReportErrors();
// 最大的窗口在需要绘制时调用本函数
void display(void)
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glutSwapBuffers();
作者:eastcowboy 发表时间:2008-9-22 22:12:00
第4楼
// main函数
int main(int argc, char* argv[])
int window, left_window, main_window;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowPosition(100, 100);
glutInitWindowSize(640, 480);
// 父窗口
window = glutCreateWindow("OpenGL");
glutDisplayFunc(&display);
// 左边的子窗口
left_window = glutCreateSubWindow(window, 5, 5, 150, 470);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 150.0, 0.0, 470.0, 100.0, -100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.375f, 0.375f, 0.0f);
glutDisplayFunc(&left_display);
glutMouseFunc(&left_mouse);
// 主体的子窗口
main_window = glutCreateSubWindow(window, 165, 5, 470, 470);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, 1.0, 0.5, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(4.0, 3.0, 3.0,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0);
glutDisplayFunc(&main_display);
glutMouseFunc(&main_mouse);
glutPassiveMotionFunc(&main_passiveMotion);
glutMainLoop();
参考技术B 你解决了吗?我也遇到同样的问题?能不能告诉我怎么回事?
OpenGL ES on android 微调光线拾取代码
【中文标题】OpenGL ES on android 微调光线拾取代码【英文标题】:OpenGl ES on android fine tuning of ray picking code 【发布时间】:2012-03-07 06:07:28 【问题描述】:我已经使用 min3d 和一些我在网上找到的代码实现了光线拾取,但是当我实现它时,我注意到它有时有点偏离,这意味着当我点击一个框时,有时会选择错误的那个。有时被选中的那个甚至不在屏幕上。这需要相当多的代码才能实现,但我调用的主要两种方法如下:
private void rayPicking(float x, float y)
//intersection with near plane
float[] near = new float[4];
GLU.gluUnProject(x, scene.getViewport()[3] - y, 0f, scene.getGrabber().mModelView, 0, scene.getGrabber().mProjection, 0, scene.getViewport(), 0, near, 0);
if (near[3] != 0)
near[0] = near[0] / near[3];
near[1] = near[1] / near[3];
near[2] = near[2] / near[3];
Number3d near3 = new Number3d(near[0], near[1], near[2]);
//and far plane
float[] far = new float[4];
GLU.gluUnProject(x, scene.getViewport()[3] - y, 1f, scene.getGrabber().mModelView, 0, scene.getGrabber().mProjection, 0, scene.getViewport(), 0, far, 0);
if (far[3] != 0)
far[0] = far[0] / far[3];
far[1] = far[1] / far[3];
far[2] = far[2] / far[3];
Number3d far3 = new Number3d(far[0], far[1], far[2]);
Box firstPicked = null;
Box currentModel = null;
Number3d currentCoords = null;
Number3d firstPickedCoords = new Number3d();
if (!sceneBoxes.isEmpty())
//here we check each model if it was tapped and if several models were tapped, we take only that which is closer to the near clipping plane
Iterator<Box> itr = sceneBoxes.iterator();
while (itr.hasNext())
currentModel = itr.next();
currentCoords = new Number3d(currentModel.position().x, currentModel.position().y, currentModel.position().z);
if (picked (far3, near3, currentCoords, 1.2f))
if (firstPicked==null)
firstPicked = currentModel;
firstPickedCoords.setAllFrom(currentCoords);
else if (Number3d.add(currentCoords, near3).length() < Number3d.add(firstPickedCoords, near3).length())
firstPicked = currentModel;
firstPickedCoords.setAllFrom(currentCoords);
if (firstPicked != null) // if model picked
String temp = firstPicked.getTitle();
String temp2 = firstPicked.getLink();
Log.w("3d touch working "+temp, "3d touch working "+temp);
Intent intent = new Intent(CurAct.this, WebViewer.class);
intent.putExtra("clkURL", temp2);
intent.putExtra("clkUID", curr_uid);
intent.putExtra("rid", curr_rid);
startActivity(intent);
firstPicked = null;
temp = null;
temp2 = null;
private boolean picked(Number3d a, Number3d b, Number3d q, float r)
float ab = (float) Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
float aq = (float) Math.sqrt((a.x-q.x)*(a.x-q.x)+(a.y-q.y)*(a.y-q.y)+(a.z-q.z)*(a.z-q.z));
float bq = (float) Math.sqrt((b.x-q.x)*(b.x-q.x)+(b.y-q.y)*(b.y-q.y)+(b.z-q.z)*(b.z-q.z));
float p = (ab + aq + bq) / 2;
float hh = (float) Math.sqrt(p * (p - ab) * (p - aq) * (p - bq));
float h;
if (ab!=0) h = 2 * hh / ab; else h = 2*hh;
if (aq<h) h = aq;
if (bq<h) h = bq;
if (h<r)return true; else return false;
现在我也尝试实现截锥体剔除,但这样做会减慢速度,因为我没有做太多优化它并且它没有在本机运行。但是,如果有人可以看,也许能给我一些关于如何使它更准确的指示,那就太棒了。这里我称之为拣货方法。
_glSurfaceView.setOnTouchListener(
new View.OnTouchListener()
@Override
public boolean onTouch(View arg0, MotionEvent e)
switch (e.getAction() & MotionEvent.ACTION_MASK)
case MotionEvent.ACTION_DOWN:
startX = e.getX();
startY = e.getY();
trace = 0.0f;
time = System.currentTimeMillis();
touchMode = DRAG;
break;
case MotionEvent.ACTION_UP:
// we use ray picking only when the tap wasn't longer than 0.5 sec and if we almost didn't move our finger
//Log.w("this is touch y"+e.getY()+" viewport is"+scene.getViewport()[3], "this is touch x"+e.getX());
if ((System.currentTimeMillis()-time < 500) && (trace<scene.getViewport()[3]*0.075)) rayPicking(e.getX(), e.getY());
time = 0;
case MotionEvent.ACTION_POINTER_UP:
touchMode = NONE;
break;
return true;
);
【问题讨论】:
【参考方案1】:您遇到的错误听起来像是您在碰撞检测中犯了一个错误,我在点积的函数中遇到了一个错误,并导致了一些非常奇怪的结果。你能发布更多你的碰撞算法吗?
我在这里实现了光线拾取:http://android-raypick.blogspot.ca/ 我没有在此处粘贴代码,因为它有很多,请随意将您的代码与此代码进行比较,此代码已经过测试并且可以工作。
【讨论】:
我现在不记得是怎么回事了,但我能够纠正我在光线拾取准确性方面遇到的问题。根据我的记忆,我从我应该添加的某个地方减去,反之亦然,它把所有东西都扔掉了。有机会我会浏览一下我的代码并回复你。 我确认来自android-raypick.blogspot.ca 的代码正在运行,我正在我的应用程序中使用它。那里的所有计算都是正确的,但是内存分配很大。您可能需要重新设计代码,以免分配千字节的内存来检查光线与 900 个三角形的交点。 @keaukraine :我做不到,在这里发了一个问题***.com/questions/14185279/…,希望你能看看我是否走在正确的轨道上。 @Araw 如果您得到多个碰撞检测,您应该选择最接近相机的一个 - 这将是正确的一个。该代码检查与光线的交点,以便它可以选择被其他三角形遮挡的三角形。最近的一个没有被其他三角形遮挡。以上是关于OpenGL拾取遇到点麻烦 (找错误)的主要内容,如果未能解决你的问题,请参考以下文章