17.5.3轮廓勾勒
要完成卡通效果,我们还需要勾勒(outline)轮廓边(silhouette edge),这比卡通着色稍微复杂一点。
17.5.3.1 边的表示法
我们将一个网格的一条边表示为一个四元组(构建自2个三角形)——参见图17.5。

我们选择四元组有两个原因:我们可以通过调整四元组的尺寸容易地改变边的厚度,并且我们可以渲染退化的四元组来隐藏某些边,也即非轮廓边。在Direct3D中,我们从两个三角形来构建一个四元组。退化四元组(degenerate quad)是从两个退化三角形构建而来的四元组。退化三角形(degenerate triangle)是一个面积为零的三角形,或者换句话说,是一个三点位于一线上的三角形。如果我们传入一个退化三角形到渲染管线,则该三角形显示为空。这是很有用的,因为如果我们希望隐藏特定三角形,我们可以简单的退化它而不需要实际的从三角形列表(顶点缓冲)移除它。回想一下,我们只需要显示轮廓边——而不是网格的每一条边。
当我们首先创建一条边的时候,我们指定其四个顶点,并使其退化,这意味着边将会被隐藏(渲染时不显示)。

注意图17.6中的两个顶点v0和v1,我们设置其顶点法线向量为零向量。然后当我们将边的顶点送入顶点着色器的时候,顶点着色器将会检测顶点是否位于轮廓边上;如果是,则顶点着色器将沿顶点法线的方向偏移顶点位置的标量。观察法线向量为零的顶点,它不会被偏移。
因此,我们最终以一个非退化四元组(non-degenerate quad)来表示轮廓边,如图17.7所示。

备注:如果我们没有设置顶点v0和v1的顶点法线为零向量,那么那些顶点就同样会被偏移。但是如果偏移描述轮廓边的所有四个顶点,那么我们仅是平移了该退化四元组。通过保持顶点v0和v1固定并仅仅偏移顶点v2和v3,我们重新生成了四元组。
17.5.3.2 轮廓边测试
若两个三角面face0和face1在视图方向上与两个不同方向的面共享同一条边,则该边为轮廓边。也就是说,如果一个面是前面(front facing)而另一个面是后面(back facing),那么这条边就是一条轮廓边。图17.8给出了一个轮廓边和一个非轮廓边的例子。

接下来,为了检测一个顶点是否在轮廓边上,我们必须以每个顶点为基础了解face0和 face1的法线向量。我们的边的顶点数据结构反映如下:
| 以下是引用片段: struct VS_INPUT { vector position : POSITION; vector normal : NORMAL0; vector faceNormal1 : NORMAL1; vector faceNormal2 : NORMAL2; }; |
实际检测顶点是否在共享边上的数学原理如下。假设我们在视图空间中,令v为一原点指向检测顶点的向量——图17.8,令n0为face0的面法线且n1为face0的面法线,若下面的不等式为真,则顶点位于轮廓边上:
(1)(v·n0)(v·n1)<0
若两点积符号相异,则不等式为真,使得不等式左边为负。回想一下点积的性质:两个点积的符号相异,这意味着一个三角面是前面而另一个是后面。
现在,考虑一条边只有一个三角形共享它的情况,如图17.9,其法线将会被存储在faceNormal1中。

我们定义这种边总为轮廓边。要确保顶点着色器将这种边作为轮廓边处理,我们要让faceNormal2 = -faceNormal1。因此,反向的面法线和不等式(1)为真,表示该边为一轮廓边。
关注此文的读者还看过: