代码工程地址:
https://github.com/jiabaodan/Direct12BookReadingNotes
假设我们没有使用曲面细分阶段,几何着色器阶段就是在顶点着色器和像素着色器之间的一个可选的阶段。几何着色器输入的是基元,输出的是一个基元列表;假如我们绘制的是三角形列表,那么几何着色器就是对每个三角形进行运算:
for(UINT i = 0; i < numTriangles; ++i)
OutputPrimitiveList = GeometryShader(T[i].vertexList );
几何着色器的主要好处就是,它可以创建或删除几何体,所以它可以基于GPU实现一些很有意思的效果。比如可以将输入的基元扩展为更多的基元输出,或者根据某些条件不输出部分基元。
值得注意的是几何着色器输出的基元类型不能和输入的类型相同,所以常见的程序就是将一个顶点扩展成一个方块。
输出基元通过顶点列表来定义,顶点位置必须变换到其次裁切坐标系中。
学习目标
- 学习如何编写几何种色器程序;
- 学习公告牌效果如何被几何着色器高效的实现;
- 识别自动输出的基元ID和一些其它的应用;
- 学习如何创建和使用纹理数组,理解为什它们有用;
- 学习为什么alpha-to-coverage可以帮助解决透明切口的抗锯齿问题。
1 几何着色器编程
几何着色器编程很像顶点和像素着色器,但是有一些不同的地方。下面的代码展示了它的基本形式:
[maxvertexcount(N)]
void ShaderName (PrimitiveType InputVertexType InputName [NumElements],
inout StreamOutputObject<OutputVertexType> OutputName)
{
// Geometry shader body…
}
首先要声明单词调用时,输出的顶点的最大数量;通过在函数声明前添加下面语句:
[maxvertexcount(N)]
输出的顶点数量在每次调用时是可变的,但是不能超过最大值。以优化为目标,最大值应该越小越好。[NVIDIA08]写明当最大值在1 ~ 20时,运行效率最高;当值为27~40时,运行效率会降低50%。在实践中基于上述限制条件来应用是比较困难的,但是[NVIDIA08]是在2008年发表的,所以现在情况会好一些。
几何着色器有2个参数:输入和输出。输入参数是一个用来定义基元的顶点列表。顶点类型是顶点着色器返回的顶点类型。输入参数必须要有一个基元类型前缀,它可以是下面的值:
- point:输入的是点;
- line:输入的是线(lists or strips);
- triangle:三角形(lists or strips);
- lineadj:邻接线(lists or strips);
- triangleadj:邻接三角形(lists or strips)。
输入到几何着色器中的基元是完整的基元,所以不用关心是lists or strips。如果是strip代表顶点会被多个三角形共用,也就会被几何着色器执行多次。
输出参数会有inout修饰符。并且它是流类型,它保存了输出的顶点列表。几何着色器程序通过内置的Append方法添加顶点到输出流列表中:
void StreamOutputObject<OutputVertexType>::Append(OutputVertexType v);
流类型是一个模板类型,模板参数用来指定输出顶点的顶点类型,有三种可能的流类型:
- PointStream:顶点列表定义点列表;
- LineStream:顶点列表定义线strip;
- TriangleStream:顶点列表定义三角形strip。
对于线和三角形 它们是strip类型。对于线和三角形列表,可以通过调用内置的RestartStrip函数来模拟:
void StreamOutputObject<OutputVertexType>::RestartStrip();
比如如果你想输出三角形列表,可以在每次添加3个顶点后,调用这个函数。
下面是一些定义几何着色器签名的例子:
// EXAMPLE 1: GS ouputs at most 4 vertices. The input primitive is a
// line.
// The output is a triangle strip.
//
[maxvertexcount(4)]
void GS(line VertexOut gin[2],
inout TriangleStream<GeoOut> triStream)
{
// Geometry shader body…
} //
// EXAMPLE 2: GS outputs at most 32 vertices. The input primitive is
// a triangle. The output is a triangle strip.
//
[maxvertexcount(32)]
void GS(triangle VertexOut gin[3],
inout TriangleStream<GeoOut> triStream)
{>>>>>>>>>>>>>>>>>>
// Geometry shader body…
} //
// EXAMPLE 3: GS outputs at most 4 vertices. The input primitive
// is a point. The output is a triangle strip.
//
[maxvertexcount(4)]
void GS(point VertexOut gin[1],
inout TriangleStream<GeoOut> triStream)
{
// Geometry shader body…
}
下面的几何着色器举例说明了Append和RestartStrip函数;它输入三角形,细分和输出了4个细分的三角形:
struct VertexOut
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 Tex : TEXCOORD;
};
struct GeoOut
{
float4 PosH : SV_POSITION;
float3 PosW : POSITION;
float3