`

深入探索3D拾取技术

 
阅读更多

from: http://blog.csdn.net/popy007/article/details/8477484

 

深入探索3D拾取技术

3D拾取

 

在游戏中,玩家需要通过点击2D屏幕来选择3D物体,这个过程就是拾取(picking)。拾取是3D游戏必不可少的基本操作,它实现了玩家和游戏世界内对象的交互。

 

虽 然拾取技术很基本,但它却迷惑了很多3D初学者。很多朋友都问过我关于拾取的细节问题,这让我觉得很有必要具体探讨一下该技术。 其实,拾取之所以让很多开发者感到复杂,主要原因在于它跨域了流水线的多个阶段,并且是逆流水线上行。另外,它是一个2D信息扩展到3D的过程,必须对信 息做相应的扩展和额外的计算才能够得到正确的结果。下面我门具体分析一下这个技术。

 

 

水流线主要阶段分析

 

我们来直观地看一下从相机空间到viewport的变换图:

 



相机空间中的一个顶点v,经过透视变换后进入了CVV中。这个变换矩阵实际上完成了两个工作:

 

1)将顶点从3D空间投影到2D的投影平面(Projection Plane)上。

2)将投影平面上的2D投影点通过线性插值变换到齐次裁剪空间CVV中。

 

这些变换都通过透视矩阵一次完成(如果对此不是很了解,请参考《深入探索透视投影变换》一文)。 我之所以把这一步分解为两步,因为这对于分析拾取很重要。

 

顶点进入齐次裁剪空间并经过CVV裁剪,最终进行透视除法从4D齐次形式变回成3D形式。然后经过一个线性插值(被封装在视口(viewport)变换中),变换到viewport中,多个点以三角形的形式经过光栅化后被玩家看到。最后一步的点变换可以描述为:

 

3)将CVV中的点通过线性插值变换到viewport中。

 

分 析了这个变换过程之后,我们知道了从相机空间开始实际处理点位置信息的操作,就是上面的三个步骤。这样,我们可以先把顶点从viewport中先变换回投 影平面上,也就是我们可以先完成(2)和(3)的逆处理。这里我们不用考虑裁剪和透视除法这些操作,因为反推的时候,处于视口中的点,已经是经过裁剪后留 下的有效点了,必定处于CVV内,也必定处于projection plane内!而且从viewport逆变换到projection plane,点一直保持2D形式。

 

picking 的开始是玩家在屏幕上点击一个位置——这实际上是在viewport中进行了点击。我们通过响应玩家的点击事件,得到在viewport中的点击位置,记 为P0(Xp0,Yp0)。然后我们把p0从viewport中线性插值到CVV中,得到P1(Xp1,Yp1):


上面的线性插值(如果对线性插值公式不熟悉,请参考《深入探索透视投影变换》一文中的线性插值部分)公式在x方向上计算出了CVV中的P1,y方向的公式同理。接下来我们再把P1从CVV中变换到projection plane中,得到P2(Xp2,Yp2):

 

y方向的计算同理。P2就是viewport中玩家点击的点在projection plane上所对应的位置。

 

目前来看很好。我们已经获得了相机空间中的投影平面上,玩家点击的位置。但目前的点是一个2D点——它处于投影平面上。玩家需要拾取的是一个3D对象,因此我们需要将2D信息拓展到3D中。

 

 

向3D世界拓展

 

将2D的点信息拓展到3D空间进行picking,会使用射线(ray)进行。ray就是一端固定,另一端无限延伸的线性模型。如下图所示:



 
在相机空间中,红线标明的就是用于picking的ray。它的固定端就是 eye的位置(也就是相机空间的原点),并且穿过我们刚刚求出来的projection plane上面的点P2。射线向空间无限延伸,第一个穿过的polygon应该就是picking到的结果。在图中,有两个polygon被 picking到:绿色和黄色的。其中黄色的polygon是第一个被穿过的,因此picking操作返回的结果就是这个polygon。在实现中,我们 一般有两种方式来表示一个ray:

 

    struct Ray3D  
    {  
        Point3D m_startingPos;  
        Point3D m_penetratedPos;  
    };  
      
    struct Ray3D  
    {  
        Point3D m_startingPos;  
        Point3D m_direction;  
    };  

 

第一种方式标明了ray的起始点m_startingPos和任意一个穿过点m_penetrated。第二种方式标明了ray的起始点m_startingPos和方向m_direction。这两种方式可以很方便的相互转换。

 

在有了ray的表示法之后,我们要做的就是判断ray是否和各个polygon产生了相交——这实际上是一个射线和三角形的相交判断算法。这种算法很普遍,很容易找到,这里我们不进行讨论。最基本的拾取算法(相机空间中)如下所示;

 

extern float ray_triangle_intersection( const Ray3D& ray, const Polygon& polygon );  
  
GameObject* picking( const Point3D& P2, const std::vector< GameObject* >& objects )  
{  
    Ray3D ray;  
    ray.m_startingPos = Point3D( 0, 0, 0 );  
    ray.m_penetratedPos = P2;  
      
    float minDistance = 10000.0; // Big enough  
    GameObject* intersected = NULL;  
    for( int i = 0; i < objects.size(); ++i )  
    {  
        GameObject* obj = objects[i];  
        for( int j = 0; j < obj->number_polygons(); ++j )  
        {  
            Polygon* triangle = obj->get_polygon( j );  
            float distance = ray_triangle_intersection( ray, *triangle );  
            if( distance > 0.0 ) // Penetrated  
            {  
                if( distance < minDistance )  
                {  
                    minDistance = distance;  
                    intersected = obj;  
                }  
            }  
        }  
    }  
    return intersected;  
} 

 

这 个暴力算法首先生成了相机空间中的ray,然后遍历所有的游戏对象,并遍历每个游戏对象的每一个多边形,用 ray_triangle_intersection函数做射线交叉判断,如果返回正值,则证明穿插并表示ray起始点到穿插的距离,负值则表示没有穿 插。函数判断每个穿插的多边形,保留最近的一个返回,如果没有任何穿插,则返回NULL。这里值得一提的是关于判断的优化问题。 ray_triangle_intersection算法虽然可以优化,但对于一个规模较大的场景或者模型的polygon数量比较大的场景,通过这种暴 力法遍历所有polygon,在效率上是不能够接受的。需要采用以下两种方式进行优化:

 

1)采用场景管理方法,用层次结构的方法提前剔除大量不在视线中的多边形。只留下视线中的多边形。

2)以包围体为单位进行ray相交判断,而不是三角形。比如包围盒、包围球等等,变成了ray和矩形、球体进行相交判断。一个游戏对象一般都可以分解为多个包围体。

 

除 了上面的方法,还有很多其他高级的方式可以用于这种优化,这通常和你的游戏场景管理方法和3D对象表示方法有关。特别地,如果使用的是正交投影 (Orthogonal Projection),则不需要使用ray,直接在平面上判断就可以了,这将退化为一个2D picking问题。

 

回到世界空间?

 

接下来的问题会有一些策略性。我们要决定的是picking在当前相机空间种进行还是在世界空间进行。我们已经处理了相机空间中的picking,但有一个问题:在程序中,我们一般不会保留相机空间中每个3D物体的位置,因此在这种情况下,我们会采用两个办法之一:

 

1)将ray用逆相机矩阵变换到世界空间中。

2)将物体用世界矩阵和相机矩阵变换到相机空间中进行picking,就如我们上面的处理方式。

 

但 就算我们采用了第1种方式,我们也必须用世界矩阵变换一下模型,让他们从模型空间变换到世界空间中——这导致在这种picking方式下,我们既需要变换 模型,又要变换ray(除非把ray一直变到模型空间中,这也行)。但在一般的游戏中,我们会保留一个“模型-视图”矩阵——也就是世界矩阵和相机矩阵的 归并,在这种情况下,采用第2种方式的代价比第1种要小——ray不需要任何变换。值得注意的一点是,如果采用第一种方式,并且使用了上面所描述的ray 的第2种表达结构,对于m_direction的变换,需要采用相机逆矩阵的逆转置,这和变换polygon的法线是同一个道理,对于此问题迷惑的读者可 以搜索法线变换相关主题。

 

总结

 

到 目前位置,我们已经探讨了关于3D picking的主要理论方法。很多图形API(比如OpenGL)都提供了相关的方法来简化picking操作,可能picking的阶段有所差异,或 者进行了某些优化,或者CVV定义不同,而原理大同小异。但一个统一的要求就是需要对流水线有一个细节层次上的认识,如此才能在不断变化的需求中找到合理 的解决方案。

 

 

 

  • 大小: 93.2 KB
  • 大小: 5.4 KB
  • 大小: 7.4 KB
  • 大小: 55.6 KB
分享到:
评论

相关推荐

    opengl 拾取实例[太阳系]

    OpenGL拾取技术是计算机图形学中的一个重要概念,它允许用户在3D场景中选择或交互特定的对象。在“OpenGL拾取实例[太阳系]”这个主题中,我们将深入探讨如何利用OpenGL来实现对太阳系模型中各个行星或其他天体的选择...

    DirectX+10+3D游戏编程入门

    第三部分则探索如何使用Direct3D来实现一系列复杂而有趣的特效和技术,例如网格使用、地形渲染、对象选取(拾取)、粒子系统、环境贴图映射、法线贴图映射、阴影生成和渲染到纹理等高级功能。这些技术不仅增强了游戏...

    3D game studio 教程(包含模型教程和脚本使用教程)

    《3D Game Studio 教程:模型创建与C-Script脚本详解》 3D Game Studio是一款强大的3D游戏开发工具,它为游戏开发者提供了一站式的解决方案,从建模到...让我们一起探索3D Game Studio的世界,开启游戏开发的旅程吧!

    unity 3D基础教程

    教程不仅覆盖了Unity的基础操作,还深入探讨了一些进阶技术,例如动画、粒子系统、脚本编写等。 #### 学习目标 - 理解Unity的工作流程。 - 掌握基本的GameObject、组件、资源和预制件的使用方法。 - 学会创建和控制...

    Android 3D JPCT 引擎

    本文将围绕“Android 3D JPCT引擎”这一主题,深入探讨如何利用该引擎开发出高质量的3D游戏,为你的毕业设计增添亮点。 JPCT(Java Portable Class Tools)是一款强大的Java 3D图形引擎,特别适用于Android平台。它...

    Introduction to 3D Game Programming with DirectX 10(中文版)(含书签)

    通过以上介绍可以看出,《3D游戏编程与DirectX 10》不仅适合初学者作为入门教材,也为有一定基础的开发者提供了深入探索DirectX 10特性的宝贵资源。无论是想要进入游戏开发领域的新人还是希望提升自身技能的专业人士...

    JLU D3D(计算机游戏与动画技术)作业项目

    【标题】"JLU D3D(计算机游戏与动画技术)作业项目"揭示了这是一个以DirectX技术为核心的项目,主要用于教学目的,...通过研究和模仿这样的项目,学生可以深入理解游戏开发的关键技术,并提升自己的编程和设计能力。

    虚拟现实技术在房地产展示系统中的应用.pdf

    通过高分辨率的3D图像和立体声音效,购房者能够在虚拟环境中感受到如同身临其境般的体验,仿佛真的走在小区的街道上,观察建筑的内外细节,甚至进入房屋内部查看布局和装修。这种沉浸感让购房者能更直观地了解房产的...

    Beginning 3D Game Development with Unity

    - **动作对象**:设计可以响应玩家操作的动作物体,例如可拾取物品、按钮等。 #### 六、状态管理与对象元数据 - **状态管理**:构建一套灵活的状态管理系统,用于跟踪游戏中物体的不同状态。 - **对象元数据**:为...

    UIRS 3DOF机械臂小车_零件图_机械工程图_机械三维3D设计图打包下载.rar

    这样的系统常见于自动化、机器人技术和机械工程领域,用于执行各种精确的动作,如拾取、放置或装配任务。3DOF意味着该机械臂可以在三个轴线上移动——通常是前后、左右和上下方向。 描述中的内容与标题一致,强调了...

    D3D DX9 游戏demo源码 场景loading 人物骨骼动画

    通过深入学习和实践这个demo,开发者能够掌握3D游戏开发的基本流程和技术要点,为创建更复杂、更生动的游戏世界打下坚实基础。 总之,D3D DX9游戏demo展示了游戏开发中的关键技术和实践,包括场景的构建、角色动画...

    协作机器人Bin-picking应用.pdf

    在这一领域中,雄克、KUKA和Roboception三家公司进行了深入的研究与合作,探索了如何有效地实施协作搬运方案,以解决在动态环境中机器人与人工协作的难题。 首先,协作机器人在进行Bin-picking任务时,需要能够处理...

    First Person Exploration Kit 2.2.3.7z

    它允许玩家以极其流畅和自然的方式在游戏世界中移动,包括行走、奔跑、跳跃等基本动作,同时还可以实现复杂的交互操作,如开门、拾取物品等。 2. **环境交互**:环境交互是第一人称探索游戏的关键元素。此插件提供...

    IOS应用源码——samquinan-tcCollision-cc5bc02.rar

    "SamQuinan-tcCollision-cc5bc02"项目提供了一次深入探索iOS游戏开发,特别是碰撞检测技术的宝贵机会。下面我们将详细剖析这个项目的各个方面。 首先,项目名称中的"tcCollision"暗示了这是一个关于碰撞检测的项目...

    Packt.OpenSceneGraph.3.Cookbook.Mar.2012 OSG必读书籍

    《OpenSceneGraph 3 Cookbook》是面向OpenSceneGraph(OSG)开发者的实用指南,旨在帮助读者深入理解和熟练运用这个强大的3D图形库。OpenSceneGraph是一个开源的高性能3D图形API,基于OpenGL标准,广泛应用于游戏...

    并联机器人的研究现状与展望.pdf

    近年来,随着科技的发展,对并联机器人技术的研究不断深入,其应用领域也日益广泛,包括航空航天、汽车制造、生物医疗、精密加工等多个行业。 并联机器人主要分为笛卡尔坐标系、极坐标系和关节坐标系等几大类。...

    模拟实现diablo2代码

    7. **渲染技术**:在VC++中,使用DirectX或OpenGL等图形库进行3D渲染,实现游戏画面的显示。包括模型的绘制、纹理应用、视口变换等,使得游戏画面栩栩如生。 通过以上分析,我们可以看到,模拟实现Diablo II代码不...

    DMax教程——粒子系统教程PPT课件.pptx

    本教程将深入解析DMax的粒子系统,包括其创建、基本参数设置、粒子阵列、空间扭曲的应用,以及如何通过后期视频处理来增强效果。 首先,粒子系统的创建是在“几何体”菜单下选择“粒子系统”,其中有多种类型供选择...

Global site tag (gtag.js) - Google Analytics