游戏开发基础(十五)

原创 2012年03月29日 12:37:18

第十五章
通常,在屏幕上任意位置点击,点击之后,计算出拾取射线,然后对场景中的每个物体进行遍历,并检测其是否与该射线相交,与射线相交的物体即为用户所拾取的物体

拾取步骤:
1 给定所点击的屏幕点s,求出它在投影窗口中的对应点p
2 计算拾取射线,即自坐标原点发出且通过点p的那条射线
3 将拾取射线和物体模型变换至同一坐标系中
4 进行物体/射线的相交判断,相交物体即为用户所拾取的屏幕对象

屏幕到投影窗口看的变换

将屏幕点到投影窗口中,视口变换(viewport transformaion)矩阵为:
 2/Width     0         0         0  
 0       -Height/2     0         0
      [ 0           0       MaxZ-MinZ   0   ]
    X+Width/2   Y+Height/2   MinZ       1
对投影窗口中的点p=(px,py,pz)实施视口变换,就得到了屏幕点s = (sx,sy)
 sx = px(Width/2) + X + Width/2
 sy = py(Height/2) + Y + Height/2

拾取变换之后,z坐标并不作为2D图像的一部分进行存储,而是被保存在深度缓存中

已知的是屏幕点s,求点p的位置,得: 
 px = (2sx -2X-Width)/Width
 py = (-2sy + 2Y + Height)/Height

假定视口中的X和Y成员都为0,得到
 px = 2*sx/Width -1
 py = 2*sy/Width +1
 pz = 1
按照定义,投影窗口与平面z=1重合,所以,pz =1
 
由于投影矩阵已对投影窗口中的点进行了比例变换以模拟不同的视场,即呈现近大远小的效果,为了求出缩放之前该点的位置,必须对该点做一次比例变换的逆运算,设P为投影矩阵,由项P00和P11是该变换矩阵中对应于x和y坐标的比例系数,所以有:
 px = (2x/viewporWidth -1)(1/P00)
 py = (2y/viewporHeight +1)(1/P11)
 pz = 1


拾取射线计算,射线参数方程p(t) = p0+tu来表示,p0是射线的起点,它描述了射线位置,u是一个描述了射线方向的向量

射线的起点与坐标原点重合,所以p0=(0,0,0),如果射线经过了投影窗口中的p点,则方向向量u:
 u = p-p0 = (px,py,1) - (0,0,0) = p

该函数用于在给定屏幕坐标系中选定的x和y坐标的条件下,计算观察坐标系中拾取的射线:
d3d::Ray CalcPickingRay(int x,int y)
{
 float px = 0.0f;
 float py = 0.0f;

 D3DVIEWPORT9 vp;
 Device->GetViewport(&vp);

 D3DXMATRIX proj;
 Device->GetTransfrom(D3DTS_PROJECTION,&proj);
 
 px = (((2.0f*x)/vp.Width)-1.0f)/proj(0,0);
 py = (((-2.0f*y)/vp.Height)+1.0f)/proj(1,1);

 d3d::Ray ray;
 ray._origin    = D3DXVECTOR3(0.0f,0.0f,0.0f);
 ray._direction = D3DXVECTOR3(px,py,1.0f);
 
 return ray;
}

Ray结构:
struct Ray
{
 D3DXVECTOR3 _origin;
 D3DXVECTOR3 _direction;
};


为了进行射线/物体相交测试,射线和物体必须位于同一坐标系中,我们并不打算将所有的物体变换至观察坐标系中,这是因为将射线变换至世界坐标系甚至某个物体的局部坐标系往往更容易

借助变换矩阵对其起点p0和方向u分别进行变换,就实现了射线p(t)=p0+tu的变换,注意,起点是按照点来变换的,而方向是按照向量来变换的

该函数用于对射线进行变换
void TransformRay(d3d::Ray* ray,D3DXMATRIX * T)
{
 //transform the ray's origin ,w = 1
 D3DXVec3TransformCoord(
    &ray->_origin,
    &ray->_origin,
    T);
 // transfrom the ray's direction ,w = 0
 D3DXVec3TransformNormal(
    &ray->_direction,
    &ray->_direction,
    T);
 // normalize the direction
 D3DXVec3Normalize(&ray->_direction,&ray->_direction);
}

D3DXVec3TransformCoord和D3DXVec3TransformNormal均以3D向量作为其参数,注意,D3DXVec3TransformCoord参数第4个分量应理解为w = 1,而是用D3DXVec3TransformNormal时,参数第4个分量应理解为w = 0 ,所以,用D3DXVec3TransformCoord来实现点变换,用D3DXVec3TransformNormal实现向量变换

射线/物体相交判定:用外接球去近似表示每个物体,然后进行射线/外接球的相交测试

注意,射线可能会与多个物体相交,但只有距离摄像机最近的那个物体被拾取,因为距离摄像机较近的物体遮挡了位于其后的物体

给定一个球的圆心点c和半径r,可用隐式方程(implicit equation)来测试点p是否在求面上
 || p -c || - r = 0
如果p满足该方程,称p在球面上

为了判定球体与射线p(t) = p0 + tu是否相交,可将射线方程的参数方程代入隐式的球面方程中,并解出满足球面方程的参数t,这样就求出对应交点的那个参数t。

将射线参数方程代入球面方程,的:
  ||p(t) -c|| -r = 0 
  ||p0 + tu -c || -r = 0
导出一个二次方程(quadratic equation):
 At*t +Bt +C = 0;
其中,A=u*u,B=2(u*(p0-c)),C = (p0-c)*(p0-c)-r*r,若u是单位向量,则A为1
假定u为单位向量,则可解出t0和t1:
 
 t0 = (-B +squrt(B*B-4AC))/2A, t1 = (-B -squrt(B*B-4AC))/2A

下面函数如果射线与球体相交,则返回true,否则返回false
bool RaySphereIntTest(d3d::Ray* ray,d3d::BoundingSphere* sphere)
{

 D3DXVECTOR3 v = ray ->_origin-sphere->_center;
 float b = 2.0f * D3DXVec3Dot(&ray->_direction,&v);
 float c = D3DXVec3Dot(&v,&v) - (sphere->_radius * sphere->_radius) ;
 
 // find the discriminant
 float discriminant = (b*b) - (4.0f*c);

 // test for imaginary number
 float discriminant = (b*b) - (4.0f*c);

 // test for imaginary number
 if(discriminant < 0.0f)
  return false ;

 discriminant = sqrtf(discriminant )

 float s0  = (-b + discriminant) /2.0f;
 float s1  = (-b - discriminant) /2.0f;

        //if a solution is >= 0,then we intersected the sphere
 if (s0 >= 0.0f || s1 >= 0.0f)
  return true;

 return false;
}

struct BoundingSphere
{
 BoundingSphere();
 D3DXVECTOR3 _center;
 float       _radius; 
};

 

相关文章推荐

android游戏开发框架libgdx的使用(十五)—TWL布局

TWL是一个基于OpenGL的图形化用户界面库,它提供了一套非常丰富的窗口小部件,比如标签,编辑框,表格等等。不同的布局方式配合使用可以创建出非常高级的用户界面。 TWL我原来接触过,但是觉得配置文...
  • qlfhqtf
  • qlfhqtf
  • 2012年11月05日 09:54
  • 273

unity3D游戏开发十五之NGUI二

接下来我们讲解下UILabel,UILabel是用来显示文本的脚本,继承自UIWidget。

【Visual C++】游戏开发笔记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

本篇文章中,我们以核心思想为突破口,从原理介绍到一个C++类的写法,一步一步带领大家实现了一个第一人称三维摄像机的C++类。然后我们在这个摄像机类的帮助下,放出了一个几乎贯穿了我们之前学到的所有Dir...

【Visual C++】游戏开发笔记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

本系列文章由zhmxy555(毛星云)编写,转载请注明出处。   文章链接: http://blog.csdn.net/zhmxy555/article/details/8657656 作者:毛星云(...
  • zhmxy555
  • zhmxy555
  • 2013年03月10日 21:33
  • 30550

《C++游戏开发》笔记十五 专栏说明&游戏中的寻路算法(一):概论

大多数游戏中都是需要寻路算法的,因为游戏中人物、怪物、NPC都需要移动,人物可以由玩家操纵运动,但是怪物和NPC就必须有寻路算法来决定他们的移动了。即使像冒险岛那样的横版游戏,其实也是有的,在怪物视野...

Unity3D 游戏引擎之详解游戏开发音频的播放(十五)

Unity3D 游戏引擎之详解游戏开发音频的播放雨松MOMO原创文章如转载,请注明:转载至我的独立域名博客雨松MOMO程序研究院,原文地址:http://www.xuanyusong.com/arch...

【iOS-Cocos2d游戏开发之十五】详解CCProgressTimer 进度条并修改cocos2d源码实现“理想”游戏进度条!

Himi  原创, 欢迎转载,转载请在明显处注明! 谢谢。              原文地址:http://blog.csdn.net/xiaoming...
  • Jerke
  • Jerke
  • 2012年02月21日 15:49
  • 421

游戏开发笔记十五 游戏人工智能(一) 运动型游戏AI

我们常常听闻AI(Artificial Intelligence人工智能)这个名词,比如Dota里面的AI地图。写这篇文章的时候,最新版的Dota AI是6.72f,估计过几天6.73的AI也要出来了...

【Visual C++】游戏开发笔记十五 游戏人工智能(一) 运动型游戏AI

本系列文章由zhmxy555编写,转载请注明出处。  http://blog.csdn.net/zhmxy555/article/details/7434317作者:毛星云    邮箱: happyl...
  • zhmxy555
  • zhmxy555
  • 2012年04月07日 05:44
  • 31996

【Android游戏开发十五】关于Android 游戏开发中 OnTouchEvent() 触屏事件的性能优化笔记!

李华明Himi 原创,转载务必在明显处注明:转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/android-game/336.html很多童鞋说我的代码运...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:游戏开发基础(十五)
举报原因:
原因补充:

(最多只允许输入30个字)