常量外壳着色器会针对每个面片统一进行处理(即每处理一个面片就被调用一次)。它的任务是输出当前网格的曲面细分因子,而且必须要输出。曲面细分因子指示了在曲面细分阶段中将面片镶嵌处理后的份数,以及怎么进行细分。它由两个输出系统值所表示:SV_TessFactor和SV_InsideTessFactor,这两个系统值属于float或float数组的类型,具体取决于输入装配阶段定义的图元类型。常量外壳着色器的输出被限制在128个标量(如32个4D单精度浮点向量),这意味着除了系统值,你还可以额外添加输出信息供每个面片所使用。下面是一个具有3个控制点的四边形面片示例,我们通过常量缓冲区来为其设置各个方面的细分程度:
struct QuadPatchTess { float EdgeTess[4] : SV_TessFactor; float InsideTess[2] : SV_InsideTessFactor; // 可以在下面为每个面片附加所需的额外信息 }; QuadPatchTess QuadConstantHS(InputPatch<VertexOut, 4> patch, uint patchID : SV_PrimitiveID) { QuadPatchTess pt; pt.EdgeTess[0] = g_QuadEdgeTess[0]; // 四边形面片的左侧边缘 pt.EdgeTess[1] = g_QuadEdgeTess[1]; // 四边形面片的上侧边缘 pt.EdgeTess[2] = g_QuadEdgeTess[2]; // 四边形面片的右侧边缘 pt.EdgeTess[3] = g_QuadEdgeTess[3]; // 四边形面片的下册边缘 pt.InsideTess[0] = g_QuadInsideTess[0]; // u轴(四边形内部细分的列数) pt.InsideTess[1] = g_QuadInsideTess[1]; // v轴(四边形内部细分的行数) return pt; }其中InputPatch<VertexOut, 4>定义了控制点的数目和信息。前面提到,控制点首先会传至顶点着色器,因此它们的类型由顶点着色器的输出类型VertexOut来确定。在此例中,我们的面片拥有4个控制点,所以就将InputPatch模板第二个参数指定为4。系统还通过SV_PrimitiveID语义提供了面片的ID值,此ID唯一地标识了绘制调用过程中的各个面片,我们可以根据具体的需求来运用它。
但按左上右下的顺序来控制边缘细分是建立在使用下面的顶点摆放顺序而言的:
XMFLOAT3 quadVertices[4] = { XMFLOAT3(-0.54f, 0.72f, 0.0f), // 左上角 XMFLOAT3(0.54f, 0.72f, 0.0f), // 右上角 XMFLOAT3(-0.54f, -0.72f, 0.0f), // 左下角 XMFLOAT3(0.54f, -0.72f, 0.0f) // 右下角 };对四边形面片(quad)进行镶嵌化处理的过程由两个构成:
4个边缘曲面细分因子控制着对应边缘镶嵌后的份数
两个内部曲面细分因子指示了如何来对该四边形面片的内部进行镶嵌化处理(其中一个针对四边形的横向维度,另一个则作用于四边形的纵向维度)
对三角形面片(tri)进行镶嵌化处理的过程同样分为两部分:
3个边缘曲面细分因子控制着对应边缘镶嵌后的份数
一个内部曲面细分因子指示着三角形面片内部的镶嵌份数。
对等值线(isoline)进行镶嵌化处理的过程如下:
2个边缘细分因子控制着等值线如何进行镶嵌。第一个值暂时不知道作用(忽略),第二个用于控制两个相邻控制点之间分成多少段。
Direct3D 11硬件所支持的最大曲面细分因子为64(D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR).如果把所有的曲面细分因子都设置为0,则该面片会被后续的处理阶段所丢弃。这就使得我们能够以每个面片为基准来实现如视锥体剔除与背面剔除这类优化。
如果面片根本没有出现在视锥体范围内,那么就能将它从后续的处理中丢弃(倘若已经对该面片进行了镶嵌化处理,那么其细分后的各三角形将在三角形裁剪期间被抛弃)
如果面片是背面朝向的,那么就能将其从后面的处理过程中丢弃(如果该面片已经过了镶嵌化处理,则其细分后的所有三角形会在光栅化阶段的背面剔除过程中被抛弃)
一个问题自然而然地复现出来:到底应该执行几次镶嵌化处理才合适?前面提到,曲面细分的基本想法就是为了丰富网格的细节。但是,如果用户对此无感,我们就不需要对它增添细节了。以下是一些确定镶嵌次数的常用衡量标准。
根据与摄像机之间的距离:物体与摄像机的距离越远,能分辨的细节就越少。因此,我们在两者距离较远的时候渲染物体的低模版本,并随着两者逐渐接近而逐步对物体进行更加细致的镶嵌化细分。
根据占用屏幕的范围:可以先估算出物体覆盖屏幕的像素个数。如果数量比较少,则渲染物体的低模版本。随着物体占用屏幕范围的增加,我们便可以逐渐增大镶嵌化细分因子。