上篇文章介绍了本算法中需要用到的一些数学工具以及运算技巧,为后文打下了数学基础。本文从另一个方面着手,为后文提供存储数据的数据结构。
在所有的数据结构书籍中一定会有这样一个公式:程序 = 算法 + 数据结构,显然这二者是每个程序员的必修课。由于对它们的研究比较深入,各种经典结构及算法已经基本定型,所以无论是在.NET还是在Java中都被封装成非常好用的类,这样就大幅提高了开发效率,并且降低了编程门槛,这都是好事。但是万不可因为它们非常容易使用就只知其然不知其所以然,一定的了解还是必要的,毕竟经典的数据结构在某些时候未必会完全符合要求,我们需要进行包装甚至是重头搭建,还好本算法使用的数据结构不那么复杂,适当的包装一下就可以了。
一、 DirectX中的两个缓冲
如果你不是DirectX菜鸟,可以直接跳过这一段,本段是对DirectX中顶点缓冲(Vertex Buffer)和索引缓冲(Index Buffer)的概要介绍。
在DirectX中,一切都是三角形(当然如果你非拿线段来质问我就太矫情了),所有的物体都是由大量的三角形拟合而成,这也正是本专题存在的前提。
如果是我们来设计DirectX中的数据结构会怎样做呢?最直观的,把每个三角形的三个顶点按照顺序(DirectX中是逆时针)构成一个三元组,再把所有这些三元组组成一个大数组不就可以了。没错,DirectX正是这样做的,只不过并没有什么三元组,而是把所有的顶点一股脑的装进那个大数组,每相邻的三个为一组表示一个三角形罢了。
上面所说的方案是DirectX支持的一种,这个大数组就是顶点缓冲,它无可挑剔的直观,但是它的缺点也是致命的:浪费了大量的存储空间。浪费在什么地方了呢?很多三角形是共用顶点的!每个顶点所占用的空间可是不菲呀,重复的那几遍毫无意义。
很自然的,我们想到,如果把顶点缓冲当做一个没有顺序关系,没有重复的顶点池,把每个三角形顶点在这个池中的索引拿出来放到另一个数组中,像前面一样把索引三个一组的排列来表示一个三角形,会不会更好呢?
我们来算笔帐:描述空间三角形某个顶点需要三个分量,每个分量都是Double型的,在.NET中一个Double值占用8个字节,所以一个顶点需要24个字节。现假设渲染如图的一个立方体,总共有6个面,每个面需要两个三角形,总共是12个三角形,36个顶点,如果将这36个顶点一股脑的装进顶点缓冲需要6×2×3×24 = 864字节;现在换用第二种方法,顶点个数一共是8个,所以顶点缓冲需要8×24 = 192字节,三角形的个数还是那么多,所以需要有36个索引来分别代表上一种方法的36个顶点,这些索引都是其对应顶点在顶点缓冲中的索引,在这里是0~7,索引都是Integer型的,在.NET中一个Integer值占用4个字节,所以这里索引数组需要36×4 = 144字节,总共是192 + 144 = 336字节。结果很明显,采用第二种方法的空间占有量仅为第一种方法的336 / 864 = 38.9%,优势极其明显,当模型更为复杂时这种优势将更加强化,所以只有在模型极为简单的时候才使用第一种方法,毕竟直观嘛,如果模型稍微复杂一些,就要使用带有索引的方法了,索引的数组正是DirectX的索引缓冲。
这种顶点缓冲与索引缓冲的应用,与享元模式何其相似来尔!
二、 设计数据结构
由于该算法三角剖分出来的结果是要为DirectX提供数据的,所以我们的数据结构要能很好的与DirectX数据结构交互,因此参照DirectX数据结构设计是很自然的,况且根据前面的分析,该种设计方式也是很合理的。
顶点缓冲中的Point2OnLine是上篇文章Point2的子类,含有一些其它属性,在这里不必关心。由于我们面对的问题是平面多边形的三角剖分,所以所有顶点不必具有三个分量,可以认为z值始终为零。
三、 例说二缓冲
下面我们就从一个具体的例子来详细说明两个缓冲是如何配合的。
第一步:绘制一个多边形
第二步:利用扫描线在多边形上找到所有被扫描线扫到的点(整体思路请看《第二回:漫谈新思路,是我们自己干的时候了》)
第三步:上一步找到的点已经被组织到顶点缓冲,顺序是无所谓的,但是不可以重复,上文已经说过了
第四步:第一个三角形ABD的索引按照顺序(逆时针)添加到索引缓冲
第五步:所有五个三角形的顶点索引按照顺序添加到索引缓冲
上面的步骤中第二步和第三步是交替进行的,找到一个点后就将该点加入到顶点缓冲中,具体过程请看《第六回:寻找交点,离胜利就剩一步 之 开找》,第四第五步的详细过程请看《第七回:寻找三角形,夺取红旗》。
终于,一切准备工作已经完成,用于算法调试的步骤显示在《第三回:实现步骤显示,一步一步看得见》已经实现 ,数学基础在《第四回:掌握数学工具,没个好帮手怎么行》已经打下,本文又给数据存储做好了准备,下一篇文章我们将进入整个算法的最核心地带,讲述交点的寻找历程。
在所有的数据结构书籍中一定会有这样一个公式:程序 = 算法 + 数据结构,显然这二者是每个程序员的必修课。由于对它们的研究比较深入,各种经典结构及算法已经基本定型,所以无论是在.NET还是在Java中都被封装成非常好用的类,这样就大幅提高了开发效率,并且降低了编程门槛,这都是好事。但是万不可因为它们非常容易使用就只知其然不知其所以然,一定的了解还是必要的,毕竟经典的数据结构在某些时候未必会完全符合要求,我们需要进行包装甚至是重头搭建,还好本算法使用的数据结构不那么复杂,适当的包装一下就可以了。
一、 DirectX中的两个缓冲
如果你不是DirectX菜鸟,可以直接跳过这一段,本段是对DirectX中顶点缓冲(Vertex Buffer)和索引缓冲(Index Buffer)的概要介绍。
在DirectX中,一切都是三角形(当然如果你非拿线段来质问我就太矫情了),所有的物体都是由大量的三角形拟合而成,这也正是本专题存在的前提。
如果是我们来设计DirectX中的数据结构会怎样做呢?最直观的,把每个三角形的三个顶点按照顺序(DirectX中是逆时针)构成一个三元组,再把所有这些三元组组成一个大数组不就可以了。没错,DirectX正是这样做的,只不过并没有什么三元组,而是把所有的顶点一股脑的装进那个大数组,每相邻的三个为一组表示一个三角形罢了。
上面所说的方案是DirectX支持的一种,这个大数组就是顶点缓冲,它无可挑剔的直观,但是它的缺点也是致命的:浪费了大量的存储空间。浪费在什么地方了呢?很多三角形是共用顶点的!每个顶点所占用的空间可是不菲呀,重复的那几遍毫无意义。
很自然的,我们想到,如果把顶点缓冲当做一个没有顺序关系,没有重复的顶点池,把每个三角形顶点在这个池中的索引拿出来放到另一个数组中,像前面一样把索引三个一组的排列来表示一个三角形,会不会更好呢?
这种顶点缓冲与索引缓冲的应用,与享元模式何其相似来尔!
二、 设计数据结构
由于该算法三角剖分出来的结果是要为DirectX提供数据的,所以我们的数据结构要能很好的与DirectX数据结构交互,因此参照DirectX数据结构设计是很自然的,况且根据前面的分析,该种设计方式也是很合理的。
VertexBuffer
IndexBuffer
顶点缓冲中的Point2OnLine是上篇文章Point2的子类,含有一些其它属性,在这里不必关心。由于我们面对的问题是平面多边形的三角剖分,所以所有顶点不必具有三个分量,可以认为z值始终为零。
三、 例说二缓冲
下面我们就从一个具体的例子来详细说明两个缓冲是如何配合的。
第一步:绘制一个多边形
第二步:利用扫描线在多边形上找到所有被扫描线扫到的点(整体思路请看《第二回:漫谈新思路,是我们自己干的时候了》)
第三步:上一步找到的点已经被组织到顶点缓冲,顺序是无所谓的,但是不可以重复,上文已经说过了
第四步:第一个三角形ABD的索引按照顺序(逆时针)添加到索引缓冲
第五步:所有五个三角形的顶点索引按照顺序添加到索引缓冲
上面的步骤中第二步和第三步是交替进行的,找到一个点后就将该点加入到顶点缓冲中,具体过程请看《第六回:寻找交点,离胜利就剩一步 之 开找》,第四第五步的详细过程请看《第七回:寻找三角形,夺取红旗》。
终于,一切准备工作已经完成,用于算法调试的步骤显示在《第三回:实现步骤显示,一步一步看得见》已经实现 ,数学基础在《第四回:掌握数学工具,没个好帮手怎么行》已经打下,本文又给数据存储做好了准备,下一篇文章我们将进入整个算法的最核心地带,讲述交点的寻找历程。