• DDA, Bresenham line's algorithm and Voxel Traversal used in the Grid-Accelerator in PBRT


     
     
    - DDA(Digital Differential Analyzer, 数值微分法) - 
     
    计算机图形学中,经常会遇到一些计算机中”经典“的问题。例如,如何利用计算机”离散“的特质,模拟现实中”连续“的概念?关于这个问题的一个具体应用,就是如何利用计算机”画直线“的问题。我们知道在纯粹抽象的数学中,一条直线用y=mx+b就可以表达(其中m是斜率);但如果要画在计算机屏幕上,就没那么容易:因为计算机屏幕是由许多分散的像素格子组成的,你需要决定在什么时候换行或者换列。这种模拟的本质,还是”插值“问题。直线是最简单的情况,往复杂点说,就会有考虑如何插值三角形甚至多边形的情况。不过它们都是”直线“插值的复杂应用,下面就不多说了。
     
    DDA是一种处理此种问题的经典算法。算法思想倒也不难:
     
    准备阶段
     
    1) 给定一条线段的起点(x1, y1)和终点(x2, y2),分别计算在两轴上的差值 △y = y2 - y1 和 △x = x1 - x2。
     
    2) 比较△y和△x二者谁比较大,大的那一个就作为遍历的总步数。steps = max(△x, △y)。
     
    3) 分别计算在x和y方向上单步的步距。 dx = △x/steps,dy = △y/steps。注意此时dx和dy必然一个等于1,一个介于0-1之间(浮点数)

     

    【遍历阶段

    4)△x和△y谁大,谁就作为插值的主序方向。意味着每次插值都是朝这个方向行进一步。比如△x大,那么假设当前在格子(xi, yi)处,决定下一个格子时,由于在x方向是主序方向,所以朝x方向行进一步得到xi+1。剩下的就是决定y方向上到底是yi呢还是yi+1呢。

     

    5)到底是yi还是yi+1,要看根据y = m(xi+1) + b计算出真实的y等于多少。这时的y计算出来的是浮点数,根据float2Int的一些规则,转换成对应的整形然后找到对应的格子。float2Int的转换规则要看具体情况,四舍五入还是直接截取,看效果吧。

     

    6)重复4)-5)步

     

    DDA算法直接了当,简单易懂。不过它的计算过程中涉及到许多浮点数运算,这在当今的CPU设计架构中运算成本较大,也可视为其运算复杂度较高。下面的Bresenham line's algorithm就是对其的一种改进。

     

     

    - Bresenham line's algorithm - 

    Bresenham line's algorithm 是DDA的一种改进算法。它与DDA相比有质量和效率的两点改进:

    1)质量方面的改进。 比如还是以△x为主序方向行进时,决定下一个点是落在yi还是yi+1,不仅仅考虑真实计算出来的y等于多少,还要考虑这个y值是离yi更近还是离yi+1更近。谁近就画在谁的格子里。这一点显然比DDA中思考问题的方式更加make sense。

    2)效率方面的改进。根据上一点的判断准则,再加上一系列的递归推导,最后简化到只存在整形的加减运算以及比较操作。这在当今CPU的架构中运算成本相对来说非常便宜,因此效率更高。具体的推导细节,参见这篇 DERIVATION OF THE BRESENHAM’S LINE ALGORITHM

     

      

    - Voxel Traversal used in the Grid-Accelerator in PBRT -

    我正在看pbrt的第4章。这章主要讲了有关场景管理组织的一些技术,目的是为了提高射线相交测试(ray intersection tests)的效率。主要的手段有三种,Grid-Based, BVH  KD-Tree。目前正看到第一种,Grid-Based,基于网格的场景管理技术 

    基于网格的场景管理方法,相当于把整个场景看作是一个大的bouding box,然后对这个box所在的空间分别在三个维度上划分成一格一格的。然后将场景中的primitives与各个发生overlap的格子”关联“起来。接着,从射线的方位出发,遍历(traverse)这个网格系统,对于与射线发生相交的格子,再在里面遍历与之关联的所有primitives,对遍历到的每个primitive都做一次相交测试。当然这当中的细节还有很多,就不赘述了。总之这种算法的思想可以描述为”分而治之“。通过每个格子的空间管理与之有关的primitives,测试某个格子的时候就不必考虑其他格子里的primitives,从而可以有效降解其遍历场景的复杂度。

    这里,我想强调下射线是如何遍历这个网格系统的。其方法也是DDA算法的一种改进,书中说非常像Bresenham line's algorithm。但我仔细观察了一下,和Bresenham的算法还是不一样的。这里的关键是利用的是射线的参数(parametric)方程,那个参数t(作为沿着射线方向偏离起点的距离)是很重要的。当决定下一点究竟是朝x方向,y方向还是z方向步进时,要看这条射线”最先打到哪个方向的下一格子的边界“。哪个方向的边界先被打到,就朝哪个方向步进一次。这样一来,这种算法就不是依靠决定”哪个方向是主序“的思路进行的了,每次判断步进时三个方向皆有可能步进。这点与经典的DDA(包括Bresenham)算法都非常不同。

    我知道我没说清楚,刚才在网上粗略找了找,发现这篇应该才是pbrt中使用的算法:A Fast Voxel Traversal Algorithm for Ray Tracing。(我没细看,大体觉得差不了)。欢迎指正校对勘误拍砖头,谢谢。

     

     

  • 相关阅读:
    转:用两个栈实现一个队列——我作为面试官的小结
    Android屏幕重力感应旋转
    简单的单例模板,继承使用即可
    cocos2dx windows 音量设置无效解决办法
    lua lu
    OpenGL ES API with no current context
    git更新某个目录
    sh ndk-build clean Cannot run program "sh": Launching failed Error: Program "sh" not found in PATH PATH=......
    sublime Text快捷键
    转:解决windows下eclipse中android项目关联android library project失败问题
  • 原文地址:https://www.cnblogs.com/lookof/p/3528805.html
Copyright © 2020-2023  润新知