本文所讲的内容已经开源,你可以在 这里 找到源代码。
前面我们介绍了三维图形的轴测投影画法,这里介绍一组三维图形,创作这些图形主页使用“斜二测”画法。
多面体
先看一类比较简单的三维图形,这类图形,完全由顶点、棱、面组成,称为多面体。他很像二维图形中的多边形。
长方体、棱台都是多面体。
在多面体作图中,顶点、棱是比较关键的元素,具体哪些顶点之间有棱连接,是由具体的图形决定的。多面体有一些共性,需要首先处理这些共性。
- 共性1:包围多边形
当给定了一组顶点,一个基础的问题是,这些顶点在二维平面映射后的点集的包围多边形是怎样的。包围多边形一般用于确定图形的可点击范围,在接下来的另一个问题中,也会用到包围多边形的算法。
计算最小包围多边形的算法,网上有很多文章,这里介绍一下简单步骤:
- 寻找最下面、最右边的点
- 以该点作为原点,调整点的坐标
- 将所有点按其极坐标角度排序
- 初始化一个栈,元素是点,将(0,0)点入栈
- 遍历所有排序后的点,做如下处理
- 循环从栈中拿出顶部两个点
- 与当前点组成一个有向角,计算夹脚变化方向
- 如果方向大于 0,则用当前点替换栈顶,退出循环
- 弹出这两个点,继续循环,直到栈中少于两个元素
- 当栈中只有一个元素时,直接入栈
- 循环从栈中拿出顶部两个点
- 将栈中所有点调整偏移量,回到原来的位置
- 最后,栈中所有点按顺序形成包围多边形
共性2:棱的可见性
我们知道,这些棱中有些是被其他部分遮挡,看不见的,看不见的棱需要用虚线表示。但是怎么确定一条棱的可见性呢?我们也大致说下思路:
- 将所有棱的中点补充到顶点集合中
- 将所有三维点,按 y 坐标排序,y 越大,越在屏幕后面,同时计算其二维映射坐标
- 遍历排序后的点
- 用一个多边形记录已经遍历过的点,在二维平面的包围多边形
- 判断当前正在遍历的点相对于包围多边形的位置
- 如果中包围多边形里面,则该点是不可见的(因为被前面的点组成的面挡住了)
- 如果一个棱的两个顶点、一个中点,有任何一个不可见,则该棱不可见。
有可能一个棱的两个顶点是可见的,但是棱不可见,通过加入中点可以基本覆盖这种场景的正确性。
有了上面的基础,接下来我们就来看看如何计算每种特殊的多面体的顶点。
长方体
相对来说,长方体的快速绘制要比正方体简单,正方体是作为长方体的特例来处理的。
输入的两点 A、B 中,都是长方体的一个顶点。根据矢量 AB 的方向所在的象限,可以分为四种情况,这四种情况,A 点分别对应长方体的 “左后上”、“右后上”、“右前下”、“左前下”顶点,B 点对应另一个相对的顶点。
快速绘制长方体(斜二测图),我们假定在三维 X 轴方向的长度为 w,在 Y、Z 轴方向的长度相等,都是 h。
为方便计算,我们还假定 A 点所在点长方体的前面或者后面(垂直于 Y 轴)与 X、Z 轴组成的平面重合,也就是起点 A 对应的 Y 轴坐标永远为 0,这并不影响后续计算以及图形绘制。
所以我们就有了下面的这个方程:
这里不考虑 A、B 的相对方向性, 所以 w、h 可能为负值。
求解方程可以等到:
其中:
有了上面的基础,我们可以列出长方体 8 个顶点的三维坐标了。
这里,前 4 个顶点同在长方体的前面或者后面(包含 A 点),所以 y 坐标都是 y0 = 0,后面 4 个顶点在对面,y 坐标都是 y0 + h = h 。
在实际绘图时,需要通过三维坐标到二维坐标的变换矩阵 A,将上面的坐标转化为屏幕坐标。
最后 12 个边(棱)分别为:
makeLine(0, 1);
makeLine(1, 2);
makeLine(2, 3);
makeLine(3, 0);
makeLine(0, 6);
makeLine(6, 5);
makeLine(5, 4);
makeLine(4, 7);
makeLine(7, 6);
makeLine(7, 3);
makeLine(4, 2);
makeLine(5, 1);
正方体
在输入的两点 A、B 中,我们以 A 点作为正方体的一个顶点,规定 B 点可以位于正方体前面(或者后面)的底边上,也可以位于不包含 A 的侧面上。
有了前面针对长方体的计算基础,要满足上面的规定,只需同样算出两个边长 w、h,然后取他们长度最大的一个,作为正方体的边长,后面的计算就是一模一样的了。
正棱台
在棱柱、棱锥、棱台中,我们从棱台开始,因为棱柱、棱锥其实是棱台的一种特例。
正棱台的上下两个面都是正多边形,可以沿用前面正多边形的算法来计算各个顶点的坐标。
这里,我们规定棱台上下两个面的半径(r,R)存在固定的比例,比如(r = R * 0.5),还规定多边形的最外侧棱平行于 x 轴,正多边形中心点的 y 坐标为 0。
在输入的两点 A、B 中,我们以 A 点作为小正多边形的中心,B 点作为大正多边形的最外侧棱上点一个顶点,假设棱台高 h,大正多边形中点对应的三维点坐标为 (x0, 0, z0),顶点 B 相对中心的方向角为 t,那么就有了下面的方程:
求解方程得到:
其中:
结合之前二维正多边形顶点的计算方法,可以确定各顶点的 x、y 坐标,另外 z 坐标就是 z0 和 z0 + h。对于正 n 棱台,总共有 2n 个顶点。
最后,正 n 棱台,有 3n 个棱,这是不难给出的。
正棱锥
当正棱台上下两个面的半径(r,R)的比例为 0 时,即 r = 0 时,就变成正棱锥。此时,小多边形退化为一个顶点,总顶点数变为 n + 1,棱数变为 2n。其他计算完成沿用正棱台的过程就行了。
正棱柱
正棱柱则更简单,将正棱台上下两个面的半径(r,R)的比例设置为 1 即可。