OpenGL-选择与拾取
转自: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-选择与拾取的更多相关文章
- OpenGL中的拾取模式( Picking)
1. Opengl中的渲染模式有三种:(1)渲染模式,默认的模式:(2)选择模式, (3)反馈模式.如下 GLint glRenderMode(GLenum mode) mode可以选取以下三种模式之 ...
- Modern OpenGL用Shader拾取VBO内单一图元的思路和实现
Modern OpenGL用Shader拾取VBO内单一图元的思路和实现 什么意思? 拾取 最简单的理解拾取的方式大概是到(http://www.yakergong.net/nehe/course/t ...
- Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(2)
Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(2) 上一篇里介绍了Color-Coded Picking的思路和最基本的实现.在处理GL_POINTS时已经没有问题,但是处 ...
- Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3)
Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3) 到上一篇为止,拾取一个VBO里的单个图元的问题已经彻底解决了.那么来看下一个问题:一个场景里可能会有多个VBO,此时每个 ...
- OpenGL 太阳系行星拾取例子(GL_SELECT) VS2008 + glut实现
太阳系:Solar System 以太阳(Sun)为中心,由内到外分别是: 水星(Mercury) 金星(Venus) 地球(Earth) 火星(Mars) 木星(Jupiter) 土星(Saturn ...
- 深入理解OpenGL拾取模式(OpenGL Picking)
深入理解OpenGL拾取模式(OpenGL Picking) 本文转自:http://blog.csdn.net/zhangci226/article/details/4749526 在用OpenGL ...
- NeHe OpenGL教程 第三十二课:拾取游戏
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- OpenGL 3D拾取文章(转)
参考文章 深入探索3D拾取技术 OpenGL 3D拾取 射线和三角形的相交检测(ray triangle intersection test) 3D拾取的方法有两种 1.基于几何计算的射线-三角形相交 ...
- CSharpGL(18)分别处理glDrawArrays()和glDrawElements()两种方式下的拾取(ColorCodedPicking)
CSharpGL(18)分别处理glDrawArrays()和glDrawElements()两种方式下的拾取(ColorCodedPicking) 我在(Modern OpenGL用Shader拾取 ...
- 【转】OpenGL超级宝典笔记——纹理映射Mipmap
原文地址 http://my.oschina.net/sweetdark/blog/177812 , 感谢作者,若非法转载请联系本人. 目录[-] Mipmapping Mipmap过滤 构建Mip层 ...
随机推荐
- java.lang.String 类的所有方法
java.lang.String 类的所有方法 方法摘要 char charAt(int index) 返回指定索引处的 char 值. int codePointAt(int index) 返回指定 ...
- Office 2010 KMS激活原理和案例分享 - Your Office Solution Here - Site Home - TechNet Blogs
[作者:葛伟华.张玉工程师 , Office/Project支持团队, 微软亚太区全球技术支持中心 ] 为了减低部署盗版(可能包含恶意软件.病毒和其他安全风险)的可能性,Office 2010面向企 ...
- PHP == 和 ===
== 只判断两边的值是否相等,例如: 5555 == "5555" ,为真 === 判断两边的值和类型是否相当,5555 === "5555" False,因 ...
- HDU 1542 Atlantis(线段树扫描线+离散化求面积的并)
Atlantis Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total S ...
- Sql Server 常用方法、存储过程备用
常用方法 --字符串转换成数字 --CAST("1" AS int) --CONVERT(int,"1") --截取字符串 SUBSTRING(OccurreA ...
- jQuery 判断表单中多个 input text 中至少有一个不为空
html: 名称1:<input class="seasoning_name" type="text" name="seasoning_name ...
- GDC2016【全境封锁(Tom Clancy's The Division)】对为何对应Eye Tracked System,以及各种优点的演讲报告
GDC2016[全境封锁(Tom Clancy's The Division)]对为何对应Eye Tracked System,以及各种优点的演讲报告 原文 4Gamer編集部:松本隆一 http:/ ...
- 【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(2)
Lighting和Shading(2)镜面反射的控制和模拟次级表面散射技术 http://www.4gamer.net/games/216/G021678/20140703095/index_2.ht ...
- 【翻译】Kinect v2程序设计(C++) Body 篇
Kinect SDK v2预览版的主要功能的使用介绍,基本上完成了.这次,是关于取得Body(人体姿势)方法的说明. 上一节,是使用Kinect SDK v2预览版从Kinect v2预览版取得B ...
- 20145235 《Java程序设计》第8周学习总结
教材学习内容总结 15.1.1日志API简介 使用日志的起点是logger类,logger实例的创建有许多要处理的要素,必须使用logger的静态方法getLogger(). 通常在哪个类上取得的lo ...