转自:http://blog.sina.com.cn/s/blog_4a9aa55c0100vu57.html

以下内容主要整理《OpenGL编程指南》第13章的内容。主要解决以下问题:

(1)如何允许用户选择屏幕上的一块区域或者挑选屏幕上所绘制的一个物体?

一. 选择

1. OpenGL的选择机制如何实现

当我们打算使用OpenGL的选择机制时:

(1)首先把整个场景绘制到帧缓冲区中;

(2)然后进入选择模式,并且对场景进行重绘,此时,帧缓冲区的内容将不会被修改;

(3)退出选择模式时,OpenGL就会返回与视景体相交的图元列表,与视景体相交的每一个图元都产生一个所谓的“选择点击”。

(图元列表:实际上是以点击记录的形式返回,包含了图元的名称以及相关的数据。我们可以访问该列表并处理其中的内容)

2. 基本步骤

//在绘制了场景之后,进入以下步骤

(1)

#define BUFSIZE 512

GLuint selectBuf[BUFSIZE];

glSelectBuffer( BUFSIZE, selectBuf );   //指定将“图元列表”(点击记录)返回到selectBuf数组中

(2)

glRenderMode( GL_SELECT );   //进入选择模式

(3)

glInitNames();    // //初始化名字堆栈并压入初始元素

glPushName();

(4)

glPushMatrix();   //为重绘设置好投影矩阵,注意,为了不影响绘制模式,要用glPushMatrix和glPopMatrix

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(0.0,5.0,0.0,5.0,0.0,10.0);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

(5)

glLoadName(1);   //将名字堆栈的堆顶设置为1,因此,之后绘制的物体的名字为1

drawTri();

glLoadName(2);   //将名字堆栈的堆顶设置为2,因此,之后绘制的物体的名字为2

drawTri();

glLoadName(3);   //将名字堆栈的堆顶设置为3,因此,之后绘制的物体的名字为3

drawTri();

drawTri();

(6)

glPopMatrix();   //对应glPushMatrix

glFlush();

(7)

//返回绘制模式,hits记录的是产生的点击的个数,即在视景体内的图元的个数

Gluint hits = glRenderMode(GL_RENDER);

(8)

//处理点击记录

printf("hits = %d\n", hits); //输出一共产生的点击的个数

//注意:

//图元列表(点击记录)selectBuf中记录了所有的点击记录
//某一个点击记录来说,由四个项目组成:
//(1)当点击发生时,名字堆栈中的名称数量

//(2)自上一个点击记录之后,与视景体相交的所有顶点的最小和最大窗口坐标z值
//(3)当点击发生时,名称堆栈的内容,从最底部的元素开始

GLuint *ptr = selectBuf;

GLuint names;

for( i=0; i<hits; i++ )

{

names = *ptr;   //点击发生时,名字堆栈中的名称数量

printf("names = %d\n",names );

ptr++;

ptr += 2;  //跳过两个z值

for( j=0; j<names; j++ )

{

printf("%d", *ptr);  //输出点击发生时,名字堆栈中所有的名称

ptr++;

}

}

二. 拾取

1. 如何拾取

前一节介绍了OpenGL的选择机制如何实现和使用,这一节将深入的介绍如何利用选择模式来确定一个物体是否被挑选。

为了实现这个目的,可以在选择模式中使用一个特殊的挑选矩阵,结合投影矩阵,把绘图限制在视口的一个小区域内,一般是在靠近光标的位置。这样,只有靠近光标位置的物体才会引起点击。

gluPickMatrix( GLdouble x, GLdouble y, GLdouble width, GLdouble height, GLint viewport[4] );

(x,y)就是挑选区域的中心的窗口坐标,width和height定义了屏幕坐标下这个挑选区域的大小,viewport是指视口边界,通过glGeIntegerv(GL_VIEWPORT, GLint *viewport)获得

(2) 具体实例

下面,用OpenGL编程指南上的实例来介绍如何实现拾取。

//该程序完成的功能是:绘制9个方块,鼠标左键点击,改变方块的颜色

#include <gl/glut.h>

int board[3][3];   //存储几个方块的颜色

#define BUFSIZE 512

//处理点击记录:
//hits为产生的点击的数量,buffer中存储点击记录,每个点击记录由四个项目组成
void processHits(GLint hits, GLuint buffer[])
{
 unsigned int i, j;
 GLuint ii, jj, names, *ptr;

ptr = ( GLuint * )buffer;

for( i=0; i<hits; i++ )    //处理每一个点击记录
 {

//某一个点击记录来说,由四个项目组成:
//(1)当点击发生时,名字堆栈中的名称数量

//(2)自上一个点击记录之后,与视景体相交的所有顶点的最小和最大窗口坐标z值
//(3)当点击发生时,名称堆栈的内容,从最底部的元素开始

names = *ptr;      //获得名字堆栈中的名称数量
  ptr += 3;               //跳过前三个记录

for( j=0; j<names; j++ ) //开始处理名字堆栈中的内容,获取被选中的方块的index
  {
   //对应于绘制方块时,压入名字堆栈中的内容

if ( j == 0)        //x方向上的index
    ii = *ptr;
   else if( j== 1)  //y方向上的index
    jj = *ptr;
   ptr++;
  }

}
 board[ii][jj] = (board[ii][jj] + 1) % 3;   //改变被选中方块的颜色

}

//绘制所有方块,参数有GL_RENDER和GL_SELECT两种模式
void drawSquares(GLenum mode)
{
 GLuint i,j;

for(i=0; i<3; i++)
 {
  if( mode == GL_SELECT )       //如果是在选择模式下,将名字堆栈的首元素换成x方向上的索引
   glLoadName(i);

for( j=0; j<3; j++ )
  {
   if( mode == GL_SELECT )    //将y方向上的索引压入名字堆栈
     glPushName(j);

//绘制方块,在GL_SELECT模式下,某一个方块会被选中,因此,会产生一个点击记录

//该点击被记录时,名字堆栈中有两个名称,分别是i和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 pickSquares(int button, int state, int x, int y)
{
 GLuint selectBuf[BUFSIZE];   //存储点击记录
 GLint hits;                                 //点击记录的个数
 GLint viewport[4];                    //视口

if( button != GLUT_LEFT_BUTTON || state != GLUT_DOWN )
  return;

glGetIntegerv(GL_VIEWPORT, viewport);  //获得视口

glSelectBuffer( BUFSIZE, selectBuf );    //指定存储点击记录的数组
 glRenderMode( GL_SELECT );          //进入选择模式

glInitNames();           //初始化名字堆栈并压入初始元素
 glPushName(0);

glMatrixMode(GL_PROJECTION);
 glPushMatrix();
 glLoadIdentity();
 
 //设置挑选矩阵,挑选区域的中心坐标是(x,viewport[3]-y),大小是(5,5)
 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);           //处理点击记录selectBuf

glutPostRedisplay();
}

void init()
{
 glEnable(GL_DEPTH_TEST);
 glShadeModel(GL_FLAT);
 for( int i=0;i <3; i++ )            //初始化9个方块的颜色
  for( int j=0; j<3; j++ )
   board[i][j] = 0;
}
void display()
{
 glClearColor(0.0,0.0,0.0,0.0);
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_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 | GLUT_DEPTH);
 glutInitWindowSize(200,200);
 glutInitWindowPosition(100,100);
 glutCreateWindow("pick");
 init();
 glutMouseFunc(pickSquares);   //当鼠标点击时,调用pickSquares,进入选择模式进行绘制
 glutReshapeFunc(reshape);
 glutDisplayFunc(display);      //display只完成基本的绘制
 glutMainLoop();
 return 0;
}

OpenGL-选择与拾取的更多相关文章

  1. OpenGL中的拾取模式( Picking)

    1. Opengl中的渲染模式有三种:(1)渲染模式,默认的模式:(2)选择模式, (3)反馈模式.如下 GLint glRenderMode(GLenum mode) mode可以选取以下三种模式之 ...

  2. Modern OpenGL用Shader拾取VBO内单一图元的思路和实现

    Modern OpenGL用Shader拾取VBO内单一图元的思路和实现 什么意思? 拾取 最简单的理解拾取的方式大概是到(http://www.yakergong.net/nehe/course/t ...

  3. Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(2)

    Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(2) 上一篇里介绍了Color-Coded Picking的思路和最基本的实现.在处理GL_POINTS时已经没有问题,但是处 ...

  4. Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3)

    Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3) 到上一篇为止,拾取一个VBO里的单个图元的问题已经彻底解决了.那么来看下一个问题:一个场景里可能会有多个VBO,此时每个 ...

  5. OpenGL 太阳系行星拾取例子(GL_SELECT) VS2008 + glut实现

    太阳系:Solar System 以太阳(Sun)为中心,由内到外分别是: 水星(Mercury) 金星(Venus) 地球(Earth) 火星(Mars) 木星(Jupiter) 土星(Saturn ...

  6. 深入理解OpenGL拾取模式(OpenGL Picking)

    深入理解OpenGL拾取模式(OpenGL Picking) 本文转自:http://blog.csdn.net/zhangci226/article/details/4749526 在用OpenGL ...

  7. NeHe OpenGL教程 第三十二课:拾取游戏

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. OpenGL 3D拾取文章(转)

    参考文章 深入探索3D拾取技术 OpenGL 3D拾取 射线和三角形的相交检测(ray triangle intersection test) 3D拾取的方法有两种 1.基于几何计算的射线-三角形相交 ...

  9. CSharpGL(18)分别处理glDrawArrays()和glDrawElements()两种方式下的拾取(ColorCodedPicking)

    CSharpGL(18)分别处理glDrawArrays()和glDrawElements()两种方式下的拾取(ColorCodedPicking) 我在(Modern OpenGL用Shader拾取 ...

  10. 【转】OpenGL超级宝典笔记——纹理映射Mipmap

    原文地址 http://my.oschina.net/sweetdark/blog/177812 , 感谢作者,若非法转载请联系本人. 目录[-] Mipmapping Mipmap过滤 构建Mip层 ...

随机推荐

  1. POJ 3041 匈牙利算法模板题

    一开始预习是百度的算法 然后学习了一下 然后找到了学长的ppt 又学习了一下.. 发现..居然不一样... 找了模板题试了试..百度的不好用 反正就是wa了..果然还是应当跟着学长混.. 图两边的点分 ...

  2. UIbutton 和UIview 切单角

    UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];    btn.backgroundColor = [UIColor cya ...

  3. [转]理解OAuth 2.0

    作者: 阮一峰 OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版. 本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释, ...

  4. jwplayer直播

    <div class='container'> <div class='row'> <div class='col-sm-10 col-md-10 col-sm-offs ...

  5. 运行EFDC出现这样的错误:forrt1:severe<157>:Program Exception-access violation

    经过检查是由于TQSER出现读写错误,原来我的数据输入文件的时间是427天,后来延长到639,但其中有一个点的时间仍然维持在427.故此出现这个错误.EFDC是用Fortran编译的,通过debug才 ...

  6. 设置session失效时间

    以X5部署在Tomcat上为例,说明如何设置session失效时间. 可以设置session失效时间的地点有三处,分别是 1.BusinessServer的session设置 \runtime\Bus ...

  7. SQL2008全部数据导出导入两种方法【转】

        方法一:生成脚本导出导入sql2008全部数据 第一步,右键要导出的数据库,任务--生成脚本 第二步,在设置脚本编写选项处,点击--高级(A),选择要编写脚本的数据的类型为:架构和数据 如果找 ...

  8. 关于Bitcode的探索

    Bitcode概述         Bitcode is an intermediate representation of a compiled program. Apps you upload t ...

  9. 多选列表Select之双击删除与添加Demo

    双击任一Select控件,查看效果: srcA srcC srcB targetC targetB targetA   源码: <html> <head> <script ...

  10. [LeetCode]题解(python):102 Binary Tree Level Order Traversal

    题目来源 https://leetcode.com/problems/binary-tree-level-order-traversal/ Given a binary tree, return th ...