几何着色器
几何着色器(Geometry Shader)是DirectX渲染管线中的一个可选阶段,位于顶点着色器和像素着色器之间。它允许开发者动态创建或修改几何图元,例如将点扩展为四边形,三角形细分,或实现像billboards这样的特效。本章将详细介绍几何着色器的原理、应用及实现方法。
12.1 编写几何着色器
几何着色器是渲染流水线中唯一可以创建或销毁图元的阶段,也是唯一能改变图元类型的阶段。
12.1.1 几何着色器的基本结构
几何着色器的基本结构包含输入、输出以及最大顶点数的声明:
hlsl
// 输入:三角形图元(3个顶点)
[maxvertexcount(3)] // 每个图元最多输出3个顶点
void GS_PassThrough(triangle VertexOut input[3], inout TriangleStream<VertexOut> outStream)
{
// 简单将输入图元的3个顶点传递给输出流
outStream.Append(input[0]);
outStream.Append(input[1]);
outStream.Append(input[2]);
outStream.RestartStrip();
}
几何着色器输入参数说明:
- 前缀(如
triangle
)指定输入图元类型 - 输入结构体数组,大小由输入图元类型决定
maxvertexcount
属性指定单个图元可输出的最大顶点数
输出参数通常是包装了顶点结构体的几何流:
PointStream<T>
- 点流LineStream<T>
- 线流TriangleStream<T>
- 三角形流
12.1.2 输入图元类型
几何着色器可以接收不同类型的输入图元:
输入类型 | 描述 | 输入数组大小 |
---|---|---|
point |
单个顶点 | 1 |
line |
线段,两个顶点 | 2 |
triangle |
三角形,三个顶点 | 3 |
lineadj |
带邻接信息的线,四个顶点 | 4 |
triangleadj |
带邻接信息的三角形,六个顶点 | 6 |
使用带邻接信息的图元的示例:
hlsl
[maxvertexcount(6)]
void GS_TriangleWithAdjacency(triangleadj VertexOut input[6], inout LineStream<VertexOut> outStream)
{
// input[0], input[2], input[4] 是主三角形的顶点
// input[1], input[3], input[5] 是邻接顶点
// 绘制三角形的轮廓线
outStream.Append(input[0]);
outStream.Append(input[2]);
outStream.RestartStrip();
outStream.Append(input[2]);
outStream.Append(input[4]);
outStream.RestartStrip();
outStream.Append(input[4]);
outStream.Append(input[0]);
outStream.RestartStrip();
}
12.1.3 几何着色器输出流
几何着色器使用流对象来输出顶点。关键方法包括:
Append(vertex)
: 向当前图元添加顶点RestartStrip()
: 结束当前图元并开始一个新的图元
一个产生线段的几何着色器示例:
hlsl
[maxvertexcount(6)]
void GS_GenerateLines(triangle VertexOut input[3], inout LineStream<VertexOut> outStream)
{
// 生成三角形的三条边
outStream.Append(input[0]);
outStream.Append(input[1]);
outStream.RestartStrip(); // 结束第一条线
outStream.Append(input[1]);
outStream.Append(input[2]);
outStream.RestartStrip(); // 结束第二条线
outStream.Append(input[2]);
outStream.Append(input[0]);
outStream.RestartStrip(); // 结束第三条线
}
12.1.4 系统值语义
几何着色器可以输出额外的系统值语义,用于特殊目的:
SV_PrimitiveID
: 当前处理的图元IDSV_RenderTargetArrayIndex
: 用于渲染到纹理数组的特定层SV_ViewportArrayIndex
: 指定使用哪个视口
使用系统值语义的示例:
hlsl
struct GSOutput
{
float4 pos : SV_POSITION;
float2 tex : TEXCOORD0;
uint RTIndex : SV_RenderTargetArrayIndex; // 渲染目标数组索引
};
[maxvertexcount(3)]
void GS_LayeredRendering(triangle VertexOut input[3], uint primID : SV_PrimitiveID, inout TriangleStream<GSOutput> outStream)
{
// 根据图元ID决定渲染到哪一层
uint layerIndex = primID % 6; // 假设有6个层
// 输出每个顶点到特定层
for(int i = 0; i < 3; ++i)
{
GSOutput output;
output.pos = input[i].pos;
output.tex = input[i].tex;
output.RTIndex = layerIndex; // 设置渲染目标数组索引
outStream.Append(output);
}
outStream.RestartStrip();
}