当前位置: 首页 > news >正文

Direct3D拾取

假设在屏幕上单击,击中的位置为点s=(x,y)。由图可以看出,用户选中了茶壶。但是仅给出点s,应用程序还无法立即判断出茶壶是否被选中。所以针对这类问题,我们需要采用一项称为“拾
取(Picking)”的技术。

茶壶和屏幕点s之间的一种联系是茶壶被投影到了一个包含了s的区域中。更准确地说,茶壶被投影到了投影窗口中一个包含点p(点P位于投影窗口中)的区域中,其中点P对应于屏幕中的点s

我们看到,如果自坐标原点发出一条拾取射线(picking ray)该射线将与那些其投影包围了点p的物体(即茶壶)相交。所以,一旦计算出了拾取射线,我们就可对场景中的每个物体进行遍历,并逐个测试射线是否与其相交,与射线相交的物体即为用户所拾取的物体。如果拾取射线不与场景中的任意物体相交,则用户便没有拾取到任何物体,而是选中了屏幕中的背景或些我们并不感兴趣的东西。

拾取原理步骤

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

屏幕到投影窗口的变换

视口变换的矩阵

\begin{bmatrix} \frac{Width}{2} & 0 & 0 & 0\\ 0 & -\frac{Height}{2} & 0 & 0\\ 0 & 0 & MaxZ-MinZ & 0\\ X+\frac{Width}{2} & Y+\frac{Height}{2} & MinZ & 1 \end{bmatrix}(里面的参数为D3DVIEWPORT9中的参数)

对投影窗口中的点p=(p_{x},p_{y},p_{z})实施视口变换(将p放入1x4矩阵后与视口变换矩阵相乘),就得到了屏幕点s=(s_{x},s_{y})

s_{x}=p_{x}(\frac{Width}{2})+X+\frac{Width}{2}s_{y}=p_{y}(\frac{Height}{2})+Y+\frac{Height}{2}

视口变换之后z坐标并不作为2D图像的一部分进行存储,而是被保存在深度缓存中。解上面的方程后得到p_{x}=\frac{2s_{x}-2X-Width}{Width}p_{y}=\frac{-2s_{y}+2Y+Height}{Height}

假定视口的X和Y成员都为0(一般情况下如此,因为是相对父窗口的大小),这样就得到p_{x}=\frac{2s_{x}}{Width}-1p_{y}=\frac{2s_{y}}{Height}+1p_{z}=1

按照定义投影窗口与平面z=1重合,所以p_{z}=1,但是,至此我们还有一些工作要做。由于投影矩阵对投影窗口中的点进行了比例变换以模拟不同的视场,即呈现出近大远小的效果。为了反求出缩放之前该点的位置,我们必须对该点做一次比例变换的逆运算,设P为投影矩阵,由于项P_{00}P_{11}是该变换矩阵中对应于x和y坐标的比例系数(可根据投影矩阵转换反推得到),所以有
p_{x}=(\frac{2x}{viewporWidth}-1)(\frac{1}{P_{00}})p_{y}=(\frac{2y}{viewporWidth}+1)(\frac{1}{P_{11}})p_{z}=1

拾取射线的计算

射线可用参数方程p(t)=p_{0}+tu,其中p_{0}是射线的起点,它描述了射线的位置,u是一个描述了射线方向的向量,由15.2可看出射线的起点与坐标原点重合,所以p_{0}=(0,0,0),如果射线经过了投影窗口中的p点,则方向向量u为,u=p-p_{0}=(p_{x},p_{y},1)-(0,0,0)=p,则下面函数用于在给定屏幕坐标系中选定点的x和y坐标条件下,计算观察坐标系中的拾取射线。

struct Ray
{D3DXVECTOR3 m_origin;       //射线起点D3DXVECTOR3 m_direction;    //射线方向
};	//通过给定屏幕坐标系中选定点(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->GetTransform(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.m_origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);ray.m_direction = D3DXVECTOR3(px, py, 1.0f);return ray;
}

对射线进行变换

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

借助变换矩阵对其起点P_{0}和方向u分别进行变换,就实现了射线p(t)=p_{0}+tu的变换。注意,起点是按照来变换的,而方向是按照向量来变换的。本章的例程中实现了如下函数用于对射线进行变换。

void d3d::TransformRay(Ray* ray, D3DXMATRIX* T)
{//对起点进行变换 w=1D3DXVec3TransformCoord(&ray->m_origin, &ray->m_origin, T);//对方向进行变换 w=0D3DXVec3TransformNormal(&ray->m_direction, &ray->m_direction, T);D3DXVec3Normalize(&ray->m_direction, &ray->m_direction);
}

 

http://www.lryc.cn/news/228064.html

相关文章:

  • 大洋钻探系列之二IODP 342航次是干什么的?(上)
  • 离散时间系统模型
  • Nginx学习(在 Docker 中使用 Nginx)
  • 【Java】集合(一)单列集合List
  • 实战 | 基于卷积神经网络的蘑菇识别微信小程序
  • 如何选择共享wifi项目服务商,需要注意哪些?
  • ubuntu20.04 MYNTEYE S 相机运行与标定记录
  • 有效降低数据库存储成本方案与实践 | 京东云技术团队
  • 分布式数据库Schema 变更 in F1 TiDB
  • 图形库篇 | EasyX | 图像处理
  • AWTK UI 自动化测试工具发布
  • Java后端开发——JDBC入门实验
  • LCA
  • ts学习02-数据类型
  • javaSE的发展历史以及openjdk和oracleJdk
  • 【入门Flink】- 10基于时间的双流联合(join)
  • 【Python Opencv】图片与视频的操作
  • 【从入门到起飞】JavaAPI—System,Runtime,Object,Objects类
  • 【Git】的分支和标签的讲解及实际应用场景
  • 修改django开发环境runserver命令默认的端口
  • kubeadm安装k8s高可用集群
  • 来看看电脑上有哪些不为人知的小众软件?
  • 一个进程最多可以创建多少个线程?
  • ElasticSearch文档分析
  • Xilinx FPGA平台DDR3设计详解(一):DDR SDRAM系统框架
  • Spring Data JPA方法名命名规则
  • 【Leetcode Sheet】Weekly Practice 15
  • 人力资源社会保障部办公厅关于推行专业技术人员职业资格电子证书的通知
  • 什么是光电耦合器?如何选择型号及种类
  • hive里因为列名用了关键字导致建表失败