使用OpenGL实现三维坐标的鼠标拣选

OpenGL鼠标拾取和模型控制 这应该平衡上下、左右和前进的组件 安全信息因此,假设我们的相机直接沿着-Z世界轴看,我们 当鼠标位于 屏幕,以及鼠标在屏幕上移动时不太重要的 z 值。这应该在 x [-1:1] y [-1:1] 和 z [-1:1] 的范围内。我们有一个 x 和 y已经,所以我们缩放它们的范围,并反转y的方向。范围 [-1:1, -1:1, -1:1, -1:1]范围 [-x:x, -y:y, -z:z, -w:w]范围 [-x:x, -y:y, -z:z, -w:w]范围 [-1:1, -1:1, -1:1] 阅读详情

一、简介(Introduction

 

       OpenGL是一种比较“纯粹”的3D图形API,一般仅用于三维图形的渲染,对于特定领域的开发者(如游戏开发者)而言,如果选择使用 OpenGL进行开发,类似碰撞检测的机制就都需要自行编写了。但是由于鼠标在图形程序中的应用非常非常之广泛(例如现在已经很少有PC游戏能完全地脱离鼠标),OpenGL在图形库的基础上添加了选择与反馈机制(Select & Feedback)来满足用户使用鼠标实时操作三维图形的需要。但由于种种原因,我们需要更为特殊的选择机制以满足特定需求,在这里我们提出了一种简单迅速的RIPRay-Intersection-Penetration)方法,可以满足绝大多数典型应用的需要。

 

 

二、相关研究(Related Work

 

       用过OpenGL选择与反馈机制的开发者,或多或少可能都会觉得它难以令人满意。大致表现在下面几个方面:

一、编写程序比较繁琐。

想要使用选择反馈机制就需要切换渲染模式,操作命名堆栈,计算拣选矩阵,检查选中记录,这些繁琐的步骤很容易出错,而且非常不便于调试,只会降低工作效率和热情。

 

二、只能做基于图元的选定。

下图(1 - a),使用GL_TRIANGLES绘制了一个三角形,三个顶点分别为 P1P2P3。若使用该机制,你将只能判断是否在三维场景中选中了这个三角形(用户点击处是否在P1P2P3的范围内),而无法判断用户是点击了这个三角形哪一部分(是左边的m区域内还是右边的n区域内),因为所绘制的P1P2P3本身构成的三角形就是一个基本图元,对于拣选机制而言是不可分的。当然,把这个三角形拆成两个三角形再分别进行测试也是一个可行的方案,可是看看图1 - b),这可怎么拆呢?还有1 – c)呢?另外,如果nm两个平面不共面呢?对于使用者而言,OpenGL提供的拣选机制功能的确有限。

      

       三、降低了渲染效率。

OpenGL中的选择和反馈是与普通渲染方式不同的一种特殊的渲染方式。我们使用时一般是先在帧缓存中渲染普通场景,然后进入选择模式重绘场景,此时帧缓存的内容并无变化。也就是说,为了选择某些物体,我们需要在一帧中使用不同的渲染方式将其渲染两遍。我们知道对对象进行渲染是比较耗时的操作,当场景中需要选择的对象多而杂的时候,采用这个机制是非常影响速度的。

 

       另外在OpenGL红宝书中介绍了一种简便易行的办法:在后缓冲中使用不同的颜色重绘所有对象,每个对象用一个单色来标示其颜色,这样画好之后我们读取鼠标所在点的颜色,就能够确定我们拣选了哪个物体。这种方法有一个缺陷,当场景中需要选择的对象的数目超出一定限度时,可能会出现标识数的溢出。对于这个问题,红宝书给出的解决办法就是多次扫描。实践证明这种方法的确简便易行,但仍有不少局限性,而且做起来并不比第一种机制方便多少。限于篇幅,不再赘述。

 

三、具体描述(Related Work

 

       看过了上面两种方法,我们会发现这两种方法都不是十分的方便,而且使用者不能对其进行完全的控制,不能精确地判定鼠标定位与实际的世界空间中三维坐标的关系。那么有什么更好的办法能够更简单更精确地对其加以控制呢?

 

       实际上此处给出的解决方案十分简单,就是一个很普通也很有用的 GLU 函数 gluUnProject()

此函数的具体用途是将一个OpenGL视区内的二维点转换为与其对应的场景中的三维坐标。

转换过程如下图所示(由点P在窗口中的XY坐标得到其在三维空间中的世界坐标):

 

 

       这个函数在glu.h中的原型定义如下:

 

int APIENTRY gluUnProject (

    GLdouble       winx,

    GLdouble       winy,

    GLdouble       winz,

    const GLdouble modelMatrix[16],

    const GLdouble projMatrix[16],

    const GLint    viewport[4],

    GLdouble       *objx,

    GLdouble       *objy,

    GLdouble       *objz);

 

其中前三个值表示窗口坐标,中间三个分别为模型视图矩阵(Model/View Matrix),投影矩阵(Projection Matrix)和视区(ViewPort),最后三个为输出的世界坐标值。

可能你会问:窗口坐标不是只有X轴和Y轴两个值么,怎么这里还有Z值?这就要从二维空间与三维空间的关系说起了。

众所周知,我们通过一个放置在三维世界中的摄像机,来观察当前场景中的对象。通过使用诸如gluPerspective() 这样的OpenGL函数,我们可以设置这个摄像机所能看到的视野的大小范围。这个视野的边界所围成的几何体是一个标准的平截头体(Frustum),可以看做是金字塔状的几何体削去金字塔的上半部分后形成的一个台状物,如果还原成金字塔状,就得到了通常我们所说的视锥(View Frustum)这个视锥的锥顶就是视点(View Point)也就是摄像机所在的位置。平截头体,视锥以及视点之间的关系,如下图所示:

 

在上面的图中,远裁剪面ABCD和近裁剪面A’B’C’D’构成了平截头体,加上虚线部分就是视锥,顶点O就是摄像机所在的视点。我们在窗口中所能看到的东东,全部都在此平截头体内。这跟前面的窗口坐标Z值有什么关系呢?看下图:

 

 

如此图所示,点P和点P’分别在远裁剪面ABCD和近裁剪面A’B’C’D’上。我们点击屏幕上的点P,反映到视锥中,就是选中了所有的从点P到点P’的点。举个形象的例子,这就像是我们挽弓放箭,如果射出去的箭近乎笔直地飞出(假设力量非常之大近乎无穷),从挽弓的地点直至击中目标,在这条直线的轨迹上任何物体都将被一穿而过。对应这里的情况,用户单击鼠标获得屏幕上的某一点,即是指定了从视点指向屏幕深处的某一方向,也就确定了屏幕上某条从O点出发的射线(在图中即为OP)。在这里,我们称呼其为拣选射线。

因此,从窗口的XY坐标,我们仅仅只能获得一条出发自O点的拣选射线,并不能得到用户想要的点在这条射线上的确切位置。

这时候窗口坐标的Z值就能派上用场了。我们通过Z值,来指定我们想要的点在射线上的位置。假如用户点击了屏幕上的点(100,100)得到了这条射线OP,那么我们传入值1.0f就表示近裁剪面上的P点,而值0.0f则对应远裁剪面上的P’点。

这样,我们通过引入一个窗口坐标的Z值,就能指定视锥内任意点的三维坐标。与此同时,我们还解决了前面红宝书给出的方法中存在的缺陷——同一位置上重叠物体的选择问题。解决办法是:从屏幕坐标得到射线之后,分别让重叠的物体与该射线求交,得到的交点,然后根据这些与视点的远近确定选择的对象。如此我们就不必受“仅仅只能选取屏幕中离观察者最近的物体”的限制了。这样一来,如果需要的话,我们甚至可以用代码来作一定的限定,通过判断交点与视点的距离,使得与该拣选射线相交的物体中,离视点远的对象才能被选取,这样就能够对那些暂时被其他对象遮住的物体进行选取。

       至于如何求拣选射线与对象的交点,在各种图形学的书中的数学部分均有讲述,在此不再赘述。

      

 

四、例程(Sample Code Fragment

      

       前面讲述了RIP方法,现在我们来看如何编写代码以实现之,以及一些需要注意的问题。

       由于拣选射线以线段形式存储更加便于后面的计算,况且我们可以直接得到纵跨整个平截头体的线段(即前面图中的线段PP’),故我们直接计算出这条连接远近裁剪面的线段。我们将拣选射线的线段形式称之为拣选线段。     

       在下面的代码前方声明有两个类Point3fLineSegment这分别表示由三个浮点数构成的三维空间中的点,以及由两个点构成的空间中的一条线段。

       应注意代码中用到了类Point3f的一个需要三个浮点参数的构造函数,以及类LineSegment的一个需要两个点参数的构造函数。

       获取拣选射线的例程如下所示(使用C++语言编写):

 

class Point3f;

class LineSegment;

LineSegment GetSelectionRay(int mouse_x, int mouse_y) {

    // 获取 Model-ViewProjection 矩阵 & 获取Viewport视区

    GLdouble    modelview[16];

    GLdouble    projection[16];

    GLint       viewport[4];

    glGetDoublev (GL_MODELVIEW_MATRIX, modelview);

    glGetDoublev (GL_PROJECTION_MATRIX, projection);

    glGetIntegerv (GL_VIEWPORT, viewport);

 

    GLdouble world_x, world_y, world_z; 

 

    // 获取近裁剪面上的交点

    gluUnProject( (GLdouble) mouse_x, (GLdouble) mouse_y, 0.0,

                    modelview, projection, viewport,

                    &world_x, &world_y, &world_z);

    Point3f near_point(world_x, world_y, world_z);

 

    // 获取远裁剪面上的交点

    gluUnProject( (GLdouble) mouse_x, (GLdouble) mouse_y, 1.0,

                    modelview, projection, viewport,

                    &world_x, &world_y, &world_z);

    Point3f far_point(world_x, world_y, world_z);

 

    return LineSegment(near_point, far_point);

}

       如果你是使用Win32平台进行开发,那么应当注意传入正确的参数。因为无论是使用Win32 API 还是DirectInput 来获取鼠标坐标,得到的Y值都应取反后再传入。因为OpenGL默认的原点在视区的左下角,Y轴从左下角指向左上角,而Windows默认的原点在窗口的左上角,而Y轴方向与OpenGL相反,从左上角指向左下角。如下图所示:

0

X

Win32 默认窗口坐标样式

OpenGL 默认窗口坐标样式

0

X

Y

Y

我们可以看到代码被注释分为了三个部分:获取当前矩阵及视区,获取近裁剪面的交点,获取远裁剪面的交点。

我们通过OpenGL提供的查询函数轻松得到当前的ModelViewProjection矩阵,以及当前的Viewport(视区,也就是窗口的客户端区域,如果整个窗口区域用于OpenGL渲染的话)。

获得两个裁剪面上的交点的代码基本上是一样的,唯一的不同点是我们前面曾经详细地讨论过的窗口的Z坐标。不错,这个坐标表示的就是“深浅”的概念。它的值从点P’到点P的变化是从0.0f逐渐增至1.0f。此处类似于OpenGL的深度测试机制。

在得到两个交点之后,我们使用它们通过返回语句直接构建一条线段。在这里仅仅作为实例代码,故简捷清晰地直接返回线段对象,而没有通过引用参数来提高效率。

此时用户可以使用这个函数来判断所选择的对象了。只需在需要的地方判断对象是否与此线段相交即可判断对象是否被选中,还可以通过进一步计算其交点位置来得到详细的交点信息。这些计算均是常见的计算机图形学与三维数学计算,比如线段与三角形求交,线段与面求交,线段与球体求交,线段与柱体或锥体求交,等等。请参考所列出的计算机图形学书籍。

 

 

五、结论(Conclusion

      

       在本文中,我们介绍了一种行之有效的三维坐标拾取方法,主要使用GLU库中的实用工具实现。这种方法速度快,效率高,能在不必重新绘制对象的前提下完成拣选工作。对比OpenGL自带的拣选机制来看,RIP的确在各种方面均有一定的优势。 
C#与OpenGL实现3D正方体绘制 正方体是3D图形编程中最基础的几何体之一,了解和掌握其顶点坐标和法线向量的定义对于任何3D图形的开发都是必不可少的。本章节将深入探讨如何在C#中使用OpenGL定义和初始化正方体的顶点坐标和法线向量。法线向量用于定义一个表面上的垂直方向,它们在光照计算和表面着色中扮演着关键角色。法线向量是垂直于表面的单位向量,在3D图形编程中主要用于顶点法线贴图、光照计算等。对于正方体而言,每个面都有一个法线,指向该面的外部。对于正方体的每个面,法线都是一个单位向量,且与该面垂直。 阅读详情

相关推荐

使用OpenGL实现三维坐标鼠标拣选_-_Y___Y的专栏_-_CSDN博客

使用OpenGL实现三维坐标鼠标拣选_-_Y___Y的专栏_-_CSDN博客

OpenGL 鼠标拾取模型

在我们的场景中,使用鼠标光标点击或“挑选”一个3d对象是很有用的。一种方法是从鼠标投射3d光线,通过相机,进入场景,然后检查光线是否与任何物体相交。这通常被称为光线投射。我们不是从局部空间中的网格开始,而是从视口空间中的2d鼠标光标位置开始。我们用逆矩阵来逆向进行变换,得到世界空间中的一条射线。首先在局部空间中有一个局部坐标(0.5,1,0)然后乘以model矩阵变换到世界空间坐标(10.5,1,-20,1),这里增加了齐次坐标w=1。

希望能帮助大家,希望大家多多支持,你们的支持是我前进的动力。 3519

opengl实现鼠标坐标的显示

在VC++环境中调用opengl函数,在opengl窗口中捕捉鼠标位置,并将坐标位置显示在窗口左下角

QtOpenGL(一)简单的三维

OpenGL网上的例子也很多,之前也一直在用,最近又系统的看了一下,也总结了一下,我我们开始吧。先绘制一些简单的三维点。

weixin_45128059的博客 1806

使用OpenGL实现三维坐标鼠标拣选( 二 )

<br />我们可以通过gluUnProject函数来求得世界坐标void ILGameModule::getWorldPos( int x, int y, vec3& vWorld )<br />{<br />     JFIX vx = (JFIX)x;<br />     JFIX vy = (JFIX)camera->viewport.h - y;<br />     JFIX winz = 0.f;<br />     glReadBuffer(GL_FRONT);<br />     

大美妞苏小菲 2041

OpenGL三维坐标系绘图程序

本例实现三维坐标系中绘制图形,可以从COM口接收数据,并根据接收的数据在三维坐标系中绘制图像。同时实现了把三维的曲线投影到三个平面上。

使用OpenGL实现三维坐标鼠标拣选(一 )

<br />使用OpenGL实现三维坐标鼠标拣选<br /> Implementation of RIP(Ray-Intersection-Penetration)<br /> 3D Coordinates Mouse Selection Using OpenGL <br /> 顾 露 (武汉理工大学 计算机系 中科院智能设计与智能制造研究所 湖北武汉 430070)<br /> 摘要(Abstract):<br /> 本文提出并实现一种用于三维坐标拣选的RIP(Ray-Intersection

大美妞苏小菲 3532

OpenGL: 实现三维坐标鼠标拣选

一、简介(Introduction)       OpenGL是一种比较“纯粹”的3D图形API,一般仅用于三维图形的渲染,对于特定领域的开发者(如游戏开发者)而言,如果选择使用 OpenGL进行开发,类似碰撞检测的机制就都需要自行编写了。但是由于鼠标图形程序中的应用非常非常之广泛(例如现在已经很少有PC游戏能完全地脱离鼠标),OpenGL图形库的基础上添加了选择与反馈机制(Select &

Augusdi的专栏 2867

使用OpenGL实现三维坐标鼠标拣选(转)

一、简介(Introduction)          OpenGL是一种比较“纯粹”的3D图形API,一般仅用于三维图形的渲染,对于特定领域的开发者(如游戏开发者)而言,如果选择使用 OpenGL进行开发,类似碰撞检测的机制就都需要自行编写了。但是由于鼠标图形程序中的应用非常非常之广泛(例如现在已经很少有PC游戏能完全地脱离鼠标),OpenGL图形库的基础上添加了选择与反馈机制(Sele

killsakura的专栏 669

OPENGL 射线拾取法

opengl的射线拾取方法解决记录

gpp6025的博客 3182

Windows系统下采用Visual Studio + QT + OpenGL实现RVIZ显示平面网格、坐标系和三维激光点云等功能(三)

本篇文章将介绍采用Visual Studio2019 + Qt +OpenGL在Qt窗口中利用OpenGL Widget组件实现平面网格和三维坐标轴的显示。

weixin_42174523的博客 3318

QT框架下的OpenGL使用---实战篇---鼠标选取点对象

本篇文章是点云显示和交互以及摄像机这两篇文章的后续,如有疑惑可查看这两篇文章。 在OpenGL使用中,用其显示3D点云是非常常见的操作,而进一步让用户可以选择点云以查看其信息可以使得开发变得更加简单,本篇文章我们将讨论怎么实现这一功能。 要用鼠标圈选点云,要解决的问题就是得到鼠标圈选的范围内有哪些点,也就是要知道OpenGL的顶点到屏幕坐标之间的映射关系。这一映射关系我们在摄像机这篇文章已经得到了,它就是: QMatrix4x4 mvp; 这个对象,我们在顶点着色器中用它和顶点坐标相乘直接得到了顶点.

weixinhum 4139

open3d 点云上交互选点

有时候我想选定点云上的特定点,但一般来讲点云是无序的。下面的教程可以使用鼠标点选。 链接 http://www.open3d.org/docs/release/tutorial/visualization/interactive_visualization.html

m0_38139098的博客 2468
上一篇: WinXP程序删除自身的方法
下一篇: OpenGL中线框图的消隐
Y___Y
博客等级 码龄20年 63粉丝 61原创
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值