细分控制着色器执行模式与大多数其他着色器阶段不同;它与计算着色器最相似。与几何着色器每个调用都可以输出多个图元不同,每个细分控制着色器调用只负责生成输出面片中的单个顶点。
对于渲染期间提供的每个面片,将执行n次细分控制着色器调用,其中n是输出面片中的顶点数。因此,如果一个绘制命令绘制了20个面片,并且每个输出有4个顶点,那么总共将有80个单独的细分控制着色器调用。
为同一面片提供数据的不同调用是相互连接的。这些调用都共享它们的输出值。它们可以读取同一面片的其他调用写入的输出值。但是为了做到这一点,他们必须使用同步机制(barrier)来确保细分控制着色器的所有其他调用至少已经执行了写入。
因此,细分控制着色器不同调用可以共享数据并彼此通信。
输出面片的顶点数量可以通过布局限定符(layout)来设置,也即设置了控制着色器执行的次数:
layout(vertices = patch_size) out;
patch_size 的大小不是必须与输入面片顶点的大小(glPatchParameteri(GL_PATCH_VERTICES, n))匹配。
细分控制着色器的输出变量直接传递到细分计算着色器,而不需要任何形式的插值(这是细分计算着色器的主要工作)。这些可以是逐顶点输出或逐面片输出。
下面来测试下执行的次数:
// 创建原子计数器缓存
glGenBuffers(1, &atomic_counter_buffer);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomic_counter_buffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_DYNAMIC_READ);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomic_counter_buffer);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomic_counter_buffer);
GLuint* data = (GLuint *)glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_WRITE_ONLY);
data[0] = 0;
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
//设置输入面片的顶点数
glPatchParameteri(GL_PATCH_VERTICES, 3);
//绘制六个顶点
glDrawArrays(GL_PATCHES, 0, 6);
//输出执行次数
data = (GLuint *)glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_READ_ONLY);
cout << "invocations:" << *data << endl;
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
逐顶点输出:
//顶点着色器
#version 440 core
layout(location = 0) in vec4 VsInPos;
layout(location = 1) in vec4 VsInColor;
out vec4 color;
out vec4 pos;
void main()
{
gl_Position = VsInPos;
color = VsInColor;
}
//细分控制着色器
#version 440 core
//输出顶点数为3
layout(vertices = 3) out;
layout (binding = 0, offset = 0) uniform atomic_uint TessCoord;
in vec4 color[];
out vec4 TCcolor[];
void main()
{
atomicCounterIncrement(TessCoord);
gl_TessLevelInner[0] = 2.0;
gl_TessLevelOuter[0] = 2.0;
gl_TessLevelOuter[1] = 2.0;
gl_TessLevelOuter[2] = 2.0;
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
TCcolor[gl_InvocationID] = color[gl_InvocationID];
}
//细分计算着色器
#version 440 core
layout(triangles, equal_spacing, cw) in;
in vec4 TCcolor[];
flat out vec4 color;
void main()
{
float u = gl_TessCoord.x;
float v = gl_TessCoord.y;
float w = gl_TessCoord.z;
gl_Position = u * gl_in[0].gl_Position + v * gl_in[1].gl_Position + w * gl_in[2].gl_Position;
color = TCcolor[0]*u + TCcolor[1]*v +TCcolor[2]*w;
}
逐面片输出:
//顶点着色器
#version 440 core
layout(location = 0) in vec4 VsInPos;
layout(location = 1) in vec4 VsInColor;
out vec4 color;
out vec4 pos;
void main()
{
pos = VsInPos;
color = VsInColor;
}
//细分控制着色器
#version 440 core
//输出顶点数为1
layout(vertices = 1) out;
layout (binding = 0, offset = 0) uniform atomic_uint TessCoord;
in vec4 color[];
in vec4 pos[];
struct OutputPatch
{
//必须指定数组大小
vec4 TCpos[3];
vec4 TCcolor[3];
};
out patch OutputPatch oPatch;
void main()
{
atomicCounterIncrement(TessCoord);
gl_TessLevelInner[0] = 2.0;
gl_TessLevelOuter[0] = 2.0;
gl_TessLevelOuter[1] = 2.0;
gl_TessLevelOuter[2] = 2.0;
for(int i=0;i<3;i++)
{
oPatch.TCpos[i] = pos[i];
oPatch.TCcolor[i] = color[i];
}
}
//细分计算着色器
#version 440 core
layout(triangles, equal_spacing, cw) in;
struct OutputPatch
{
vec4 TCpos[3];
vec4 TCcolor[3];
};
in patch OutputPatch oPatch;
flat out vec4 color;
void main()
{
float u = gl_TessCoord.x;
float v = gl_TessCoord.y;
float w = gl_TessCoord.z;
gl_Position = u * oPatch.TCpos[0] + v * oPatch.TCpos[1] + w * oPatch.TCpos[2];
color = oPatch.TCcolor[0]*u + oPatch.TCcolor[1]*v +oPatch.TCcolor[2]*w;
}