您好,欢迎来到叨叨游戏网。
搜索
您的当前位置:首页计算机图形学【GAMES-101】7、光线追踪原理(线面求交、预处理空间划分)

计算机图形学【GAMES-101】7、光线追踪原理(线面求交、预处理空间划分)

来源:叨叨游戏网

快速跳转:














1 光栅化和光线追踪

光栅化:已知三角形在屏幕上的二维坐标,找出哪些像素被三角形覆盖。物体 找 像素点
光线追踪:从相机出发,对每个像素发射射线去探测物体,判断这个像素被谁覆盖。像素点 找 物体

  • 光栅化
    光栅化不能很好的模拟全局光照效果
    基本只考虑光线弹射一次,即从光源打到物体表面弹射到相机
    是一种近似的效果,不准确、不真实
  • 光线追踪的优势
    更方便地处理间接光照(光线到达相机之前弹射多次)
    更好实现软阴影、真实感金属等效果
    准确、真实、符合物理规律、质量非常高
  • 光线追踪唯一的缺点:特别慢

2 Whitted-Style光线追踪

2.1 定义图形学中光线的性质

不同于光在波动学中的定义,对于图形学中的光线简化(并不是很正确、很物理)的定义:
(1)光沿直线传播
(2)光线之间不会相互影响、碰撞
(3)光本该从光源出发照射到物体反射进入人眼,但图形学中反着来,眼睛发射光线。

2.2 光线仅弹射一次的光追

Whitted-Style光追,实际上只考虑了直接光照,即直接从光源照射到物体,然后物体反射光进入人眼。它并未考虑间接光照的影响,即别的物体反射的光打到另一个物体上再进入人眼。所以它不能很好的模拟全局光照的效果。

算法过程(眼睛发射光)
(1)通过摄像机对每一个像素发出一道射线(View Ray)
(2)射中物体,把该点直接连接到光源(Shadow Ray),判断是否在阴影中(没被挡住就不在阴影内)
(3)计算着色(Blinn Phong model)
光追算法其实很好理解,摄像机(眼睛)到某像素两点一线发出射线,击中一个或多个点后,取最近一个,连到光源,若没被阻挡则不在阴影内,被阻挡则在阴影内。之后对每一个像素做一遍这个操作,有几百万个像素就要射出几百万根,所以对并行计算性能要求特别高。

如图看透明球,不仅能看到透明球,还能看到球后面的物体,这不就是光线击中球后,还进行了折射最后击中后面的地面,那交点有多个 每个交点都进行着色计算,然后按照一定的权重加到像素上

光线命名区分

  • primary ray:从摄像机射出的光线
  • secondary ray:反射或折射(一次或多次)后的光线
  • shadow rays:交点到光源的连线

3 光线-物体求交点

定义光线:光源点、方向。t时刻光线的位置 = 光源点坐标 + t * 方向向量

3.1 光线与球体表面

球体方程

注意这个跟平常的定义不太一样(x-x0)2+(y-y0)2+(z-z0)2=R2;但其实是另一种表示而已,比如p其实就是球面上任意一点(x,y,z),c就是圆心(x0,y0,z0) 这俩相减得到一个向量,再平方(自己跟自己点积)得到的是一个标量,这个量就是R2;所以下面这个方程表示的就是位于圆心在c半径为R的球表面上的任意一点。

射线球面相交与p点(已知:球心c,半径R,光源o,射线方向d),求解p点坐标
求解过程就是解方程组
(1) o + td = p
(2) (p - c)2 - R2 = 0

把(1)带入(2)得到关于t的 一元二次方程 解之即得t,交点是p = o+td,(注意:根t必须为实数才有意义,即△ ≥ 0)
解出来的是t,交点p = o + td

3.2 光线与隐式表面求交

3.3 光线与显式表面(三角形)求交

通过光线和三角形求交可以实现

  • 渲染:可判断可见性,计算阴影、光照
  • 几何:可判断点是否在物体内(很简单,光源发射射线,判断该射线与物体的交点有几个
    奇数个交点表示在物体内,偶数个交点则在物体外)

求交方法:遍历物体每个三角形,判断与光线是否相交
(1)光线-平面求交(三角形属于平面)
(2)计算交点是否在三角形内(3次叉乘)

那么如何定义一个平面呢?

  • 法线N+面内一个点p’(跟法线向量垂直的面有无数个,再额外给定某个点,即可唯一确定一个面)
  • 平面方程: 平面内所有点p满足 (p - p')·N = 0,只有p在该平面内,(p-p’)点乘N才为0,垂直嘛。打开括号,其中p' · N 为已知常量d,另一部分只有p的坐标未知p · N = ax + by + cz
  • 然后求交点——解方程组
    p = r(t) = o+td,(p - p’)·N = 0 联立这两个方程即可得到t的结果,很简单甚至没有二元一次方程了
    t的右侧全都是已知量,注意验证t必须是实数

    到这里只是算出t,交点= o + td,算出交点还不够(这是和平面的交点),还要满足这个点在三角形内,才能说这个点是光线和三角形的交点,即还要叉乘判断内外,结束。
  • 有点麻烦啊,算这么多步骤,能不能一步到胃?——阔以

Möller-Trumbore射线-三角形求交算法

  • 这种算法更方便快捷
  • 他以另外一种方式描述了平面(重心坐标)
    等式左边是射线(光线)方程,t是未知量
    等式右边是三角形的重心坐标,P0,P1,P2是三角形三个已知顶点,b1b2 是P1,P2的权重,也是交点的重心坐标的两个分量,第三个分量可以1-b1-b2算出
    所以未知量一共3个 t,b1,b2
    由于坐标都是三维的所以每个分量都可以对应建立1个方程,即3个未知数3个方程,可以解方程组得到3个未知量

    解线代方程组得到结果。要验证(t>0;b1+b2+b3 = 1,且非负)

4 预处理 —— 包围盒求交加速

  • 目前求交算法的花销
    一根光线与场景每一个三角形都判断一次是否有交点,找到最近的击中点(通过找最小的t)。
    计算次数 = 像素个数 x 场景中所有三角面个数 x 反射次数,超级慢
  • 加速方法:用简单的体积包住复杂对象
    场景的物体都被一个个包围盒包住,先用光线跟盒子求交,光线如果连包围盒都碰不到,里面的三角形那是必然碰不到的。
    我们认为,一个盒子其实就是6个平面围住的一块公共区域,下图只画了2个平面
  • 后面所有包围盒都遵循Axis-Aligned-Bounding-Box(AABB)轴对齐包围盒
    轴对齐即面跟xoyxozyoz任一平面平行

AABB(轴对齐),如何快速求交点(以与x轴对齐的面为例)?
光线方程: p = o ⃗ + t ⋅ d ⃗ \large p=\vec{o}+t·\vec{d} p=o +td
与x轴对齐的面: x = x 0 \large x=x_0 x=x0
两者求交的方式很简单,明确目的:求 t
仅看光线方程的x轴分量,公式可以变成 p x = o x + t ⋅ d x \large p_x=o_x+t·d_x px=ox+tdx,三个已知量,t很容易得出
o x \large o_x ox:光线起点的x分量
d x \large d_x dx:光线的方向的x分量
p x \large p_x px:目标平面的x值


稍微整理一下

  • 求光线与面 x = x 0 \large x = x_0 x=x0的交点: t = x 0 − o x d x \displaystyle \large t=\frac{x_0-o_x}{d_x} t=dxx0ox
  • 求光线与面 x = x 1 \large x = x_1 x=x1的交点: t = x 1 − o x d x \displaystyle \large t=\frac{x_1-o_x}{d_x} t=dxx1ox

2D中的轴对齐盒子的求交
(1)首先第一幅图是跟X0,X1两个无限大的平面求交点,得到两个相交时间tmin、tmax
(2)然后跟y0、y1面求交点得到另一对时间tmin、tmax
(3)实际上这两次求交分别得到了两个线段,再对两个线段求个交集 即得到最后一张图的跟盒子的实际交点了


  • 再看3D中做法
    提醒:一个盒子是3对无限大的面围的一块区域
    判断进入包围盒:光线进入所有对面才算进入包围盒
    判断离开包围盒:光线出任意一个对面就算离开包围盒
  • 小的里面找大的,大的里面找小的,就是进入和离开盒子的时间
    • 进入3D盒子的时间:tenter = max{tmin}
    • 离开3D盒子的时间:texit = min{tmax}
      (如果texit < 0:盒子在光线发射方向的后面,不可能有交点)
      (如果texit >= 0 && tenter < 0:光源在盒子内部,必然有交点)
  • 总结:有交点的判定条件 (tenter < texit && texit >= 0 )

4.1 均匀空间分割(Uniform Spatial Partitions)

空间被划分为规则的、大小相等的单元格或体素。这些单元格可以是二维的(在2D图形中使用)或三维的(在3D图形中使用)。每个单元格都可以通过其在网格内的位置来标识,通常使用整数坐标表示。适用场景:物体均匀分布在场景的各个位置

格子数量经验公式:格子数 = 对象数量 x 27

光线 - 物体相交计算,首先光线跟格子求交,如果遇到"内部有物体的"格子则需要做光线-物体求交,判断光线是否跟该物体相交,从而可以找到光线跟所有物体的交点。

整个算法的流程大概如下:

  • 确定光线的起点和方向: 获取光线的起点(通常是相机位置)和光线的方向(通常是从相机指向屏幕上的像素)。
  • 找到光线起点所在的网格单元: 根据光线的起点坐标,确定它所在的初始网格单元。这可以通过将坐标除以单元格大小并取整数部分来实现。
  • 光线与初始网格单元的相交检测: 首先,检查光线是否与初始网格单元相交。如果不相交,可以忽略该单元格并将光线沿其方向移动到下一个相邻的单元格。
  • 光线与单元格内的物体相交检测: 如果光线与初始网格单元相交,接下来,需要在该单元格内进行物体相交检测。这包括检查光线是否与单元格内的任何物体(例如三角形、球体等)相交。这可以使用光线与物体的相交算法(如光线与三角形的相交检测算法)来实现。
  • 记录相交结果: 如果光线与单元格内的物体相交,需要记录相交的信息,如相交点的坐标、相交物体的属性等。
  • 继续光线追踪: 一旦相交处理完成,根据光线的方向继续光线追踪。根据之前的相交点和物体属性,可以计算下一个相交点。
  • 循环迭代: 重复上述步骤,直到光线到达场景的边界或达到最大反射/折射次数,或者直到达到其他终止条件。

4.2 非均匀空间分割

物体分布在大部分情况下还是非均匀的,自然空间分割也该在物体少的地方格子少一些,物体分布密集的地方格子多一些。依然是以2D为例

  • Oct-Tree:类似八叉树结构,注意下面省略了一些格子的后续划分,格子内没有物体或物体足够少时,停止继续划分。
  • KD-Tree:每次划分只沿着某个轴砍一刀,XYZ交替砍,不一定砍正中间,每次分出两块,类似二叉树结构。
  • BSP-Tree:空间二分的方法,每次选一个方向砍一刀,不是横平竖直,所以不好求交,维度越高越难算。

其中,KD-Tree需要掌握


KD-Tree 预处理

  • 内部节点(如ABCD)存放
    • 对齐的轴:x、y、z的其中一个
    • 分割的位置:平面沿着轴的坐标
    • 节点指针:指向两个孩子节点的指针
  • 叶子节点(如12345)存放:物体对象的列表

KD-Tree

  • 内部节点(如ABCD)存放
    (1)对齐的轴:x、y、z的其中一个
    (2)分割的位置:平面沿着轴的坐标
    (3)节点指针:指向两个孩子节点的指针
  • 叶子节点(如12345)存放:物体对象的列表

求交逻辑

  • 光线进入一个节点,判断其是否为叶子结点,

    • 不是叶子,判断光线与其两个子节点是否有交点
    • 是叶子结点,光线与其存放的所有物体求交点
  • 递归,直到遍历完所有的相交的叶子结点为止。

KD-Tree的缺陷:判定物体和包围盒的交集、物体是否属于某包围盒、同一物体可能存放于多个包围盒的问题,这些问题导致KD-Tree并不受欢迎。很难去建立一个很好的KD-Tree

4.3 对象划分 Bounding Volume Hierarchy(BVH)

不划分空间,而是划分物体 (目前最常用的求交加速划分策略)

根节点依然是最外层大包围盒

BVH数据的存储结构

  • 中间节点存放
    1.包围盒
    2.孩子指针
  • 叶子节点存放
    1.包围盒
    2.对象列表


逐步对物体进行分区:所有物体分成两组,对两组物体分别求一个包围盒(xyz的最值作为包围盒边界)

递归,对每个包围盒内部物体重复(1)中步骤,直到包围盒中的物体足够少


BVH方法的包围盒有相交,但应当尽可能减少相交区域

几种节点划分的策略

  • 轴对齐划分:选择某一个坐标轴(通常是X、Y或Z轴)进行划分。这意味着所有的分割都沿着选定的轴线进行,产生了与轴对齐边界框相关的节点。
  • 最长轴划分:在每个节点中,选择最长的轴(即边界框的最大尺寸所在的轴)进行划分。这可以确保在每个节点中都能有效地划分空间。
  • 中位数划分:这是一种常见的划分策略。沿着选定的轴,将场景中的物体按照该轴上的坐标进行排序,然后选择中间位置的物体作为划分点。这可以通过排序或者选择中位数的快速选择算法(性能更好)来实现。
  • 节点大小:在进行节点划分时,可以设置一个阈值,当节点中的物体数量小于某个数值(如5个)时停止划分,将该节点标记为叶子节点。这可以减少BVH的深度,提高遍历性能,但可能会牺牲一些精确性。

遍历BVH的算法
遍历每个盒子的算法跟KD-Tree遍历的算法没有区别,两者只是划分盒子的角度不同
递归

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.net 版权所有 湘ICP备2024080961号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务