概述

曲面细分阶段是一种将低多边形模型模型动态细分为更多的三角形或面片的技术,用于生成更平滑和精细的几何形状

上面左图为曲面细分的效果,右图是直接将面分为了平面,并没有保持面与面之间的平滑,而曲面细分阶段实现的细分会自行实现平面之间平滑

曲面细分阶段主要由三个阶段组成,

  • 曲面细分控制着色器(Tessellation Contorl Shader)
  • 曲面细分生成器(Tessellation Primitive Generator)
  • 曲面细分评估着色器(Tessellation Evalution Shader)


细分的流程:

  1. vertex shader 将topo发送给TCS
  2. tcs对patch中的点进行分析,然后根据硬件实现的算法进行细分,可增加或减少点
  3. tcs将TL发送给TPG,然后TPG生成增加或减少的点,然后将点转发给tes
  4. tes将生成的点融入到原始topo中,然后tes以point为单位发送给下一级shader

示例

  • vertex shader:
1
2
3
4
5
6
7
8
9
#version 450
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 in_color;
layout (location = 0) out vec4 out_color;
void main(void)
{
out_color = in_color;
gl_Position = position;
}
  • tessellation control shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#version 450
layout(location = 0) in vec4 vsColor[]; // 从 VS 接收颜色
layout(location = 0) out vec4 tcsColor[]; // 传递到 TES 的颜色值
layout(vertices = 4) out;
void main(void)
{
if(gl_InvocationID == 0)
{
gl_TessLevelInner[0] = 5.9;
gl_TessLevelOuter[0] = 5.9;
gl_TessLevelOuter[1] = 5.9;
gl_TessLevelOuter[2] = 5.9;
}
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
tcsColor[gl_InvocationID] = vsColor[gl_InvocationID];
}
  • tessellation evaluation shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#version 450
layout (triangles ,equal_spacing) in;
layout(location = 0) in vec4 tcsColor[]; // 从 TCS 接收颜色
layout(location = 0) out vec4 tesColor; // 传递到 FS 的颜色值
void main(void)
{
vec4 pos = gl_in[0].gl_Position * gl_TessCoord.x +
gl_in[1].gl_Position * gl_TessCoord.y +
gl_in[2].gl_Position * gl_TessCoord.z ;

// 计算插值后的颜色
tesColor = gl_TessCoord.x * tcsColor[0] +
gl_TessCoord.y * tcsColor[1] +
gl_TessCoord.z * tcsColor[2];

gl_Position = pos;
}
  • fragment shader
1
2
3
4
5
6
7
8
#version 450
layout (location = 0) in vec4 in_color;
layout (location = 0) out vec4 outColor;

void main(void)
{
outColor = in_color;
}


输出的图片如上,和例子相符。在tcs中设置了内部细分和外部细分都为5.9,在tes中指定equal_spacing会将细分数量向上取整,所以三角形内外部的细分都为6份

例子的输入每个顶点数据格式为:

1
2
3
// x y z w  r g b a
// 每个顶点为坐标xyzw,然后是rgba
// 每个顶点数据用8个float表示

关键字说明

1
2
3
4
gl_TessLevelInner[0] = 5.9;
gl_TessLevelOuter[0] = 5.9;
gl_TessLevelOuter[1] = 5.9;
gl_TessLevelOuter[2] = 5.9;

这部分代码设置了细分数,Inner为内部细分数 Outer为外部细分数
gl_InvocationID :表示的为instance的编号

layout (triangles ,equal_spacing) in :定义了输入tes的图元类型为trangle其他的图元类型还有quads和isolines,equal_spacing则表示顶点会均匀的分布在曲面上,细分的每条边被分割为相等的线段
分布方式一共有三种:

  • equal_spacing : 边界上的点按照等间距分布,分段长度相等,适合需要均匀网格的渲染场景
  • fractional_odd_spacing : 分边分割为奇数段,剩余部分用于平滑插值
  • fractional_even_spacing : 分边分割为偶数段,剩余部分用于平滑插值
1
2
3
gl_Position = baryCoord.x * gl_in[0].gl_Position 
+ baryCoord.y * gl_in[1].gl_Position
+ baryCoord.z * gl_in[2].gl_Position;

这部分代码通过重心坐标插值计算位置