在DirectX 11增加tessellation管线阶段之前,一般是使用DirectX 10的geometry shader(注意:DirectX 10中增加了geometry阶段,DirectX 11又增加了tessellation阶段)处理表面细分,现在已经有了专门处理表面细分的tessellation阶段:hull-shader阶段,tessellation阶段,以及domain-shader阶段。如图21.3所示的图形管线,tessellation阶段介于vertex和geometry shader两个阶段之间。接下来每一节讲解tessellation的一个阶段,并提供相应的演示程序。
图21.3 The Direct3D 11 graphics pipeline.
列表21.5 A Hull Shader for Triangle Patches
struct VS_OUTPUT { float4 ObjectPosition : POSITION; }; struct HS_CONSTANT_OUTPUT { float EdgeFactors[3] : SV_TessFactor; float InsideFactor : SV_InsideTessFactor; }; struct HS_OUTPUT { float4 ObjectPosition : POSITION; }; [domain("tri")] [partitioning("integer")] [outputtopology("triangle_cw")] [outputcontrolpoints(3)] [patchconstantfunc("constant_hull_shader")] HS_OUTPUT hull_shader(InputPatch<VS_OUTPUT, 3> patch, uint controlPointID : SV_OutputControlPointID) { HS_OUTPUT OUT = (HS_OUTPUT)0; OUT.ObjectPosition = patch[controlPointID].ObjectPosition; return OUT; }
列表21.6 A Constant Hull Shader
cbuffer CBufferPerFrame { float TessellationEdgeFactors[3]; float TessellationInsideFactor; } struct HS_CONSTANT_OUTPUT { float EdgeFactors[3] : SV_TessFactor; float InsideFactor : SV_InsideTessFactor; }; HS_CONSTANT_OUTPUT constant_hull_shader(InputPatch<VS_OUTPUT, 3> patch) { HS_CONSTANT_OUTPUT OUT = (HS_CONSTANT_OUTPUT)0; [unroll] for (int i = 0; i < 3; i++) { OUT.EdgeFactors[i] = TessellationEdgeFactors[i]; } OUT.InsideFactor = TessellationInsideFactor; return OUT; }
接下来讨论hull shader的下一个阶段tessellation阶段,该阶段是不可编程阶段。在tessellation阶段根据hull shader阶段的输出数据,执行真正的表面细分操作。图21.4和21.5中分别显示了一个三角形和一个四边形使用各种不同的边缘和内部细分因子产生的输出结果。
图21.4 A tessellated triangle with various tessellation factors.
图21.5 A tessellated quad with various tessellation factors.
列表21.7 A Domain Shader
struct DS_OUTPUT { float4 Position : SV_Position; }; [domain("tri")] DS_OUTPUT domain_shader(HS_CONSTANT_OUTPUT IN, float3 uvw : SV_DomainLocation, const OutputPatch<HS_OUTPUT, 3> patch) { DS_OUTPUT OUT = (DS_OUTPUT)0; float3 objectPosition = uvw.x * patch[0].ObjectPosition.xyz + uvw.y * patch[1].ObjectPosition.xyz + uvw.z * patch[2].ObjectPosition.xyz; OUT.Position = mul(float4(objectPosition, 1.0f), WorldViewProjection); return OUT; }
对于四边形,domain shader的第二个参数是一个二维坐标,类似于纹理坐标空间(范围为[0, 1];u表示水平轴;v表示垂直轴)。计算该vertex的坐标位置需要进行线程插值。具体的公式为:
列表21.8 A Quad Tessellation Shader
/************* Resources *************/ static const float4 ColorWheat = { 0.961f, 0.871f, 0.702f, 1.0f }; cbuffer CBufferPerFrame { float TessellationEdgeFactors[4]; float TessellationInsideFactors[2]; } cbuffer CBufferPerObject { float4x4 WorldViewProjection; } /************* Data Structures *************/ struct VS_INPUT { float4 ObjectPosition : POSITION; }; struct VS_OUTPUT { float4 ObjectPosition : POSITION; }; struct HS_CONSTANT_OUTPUT { float EdgeFactors[4] : SV_TessFactor; float InsideFactors[2] : SV_InsideTessFactor; }; struct HS_OUTPUT { float4 ObjectPosition : POSITION; }; struct DS_OUTPUT { float4 Position : SV_Position; }; /************* Vertex Shader *************/ VS_OUTPUT vertex_shader(VS_INPUT IN) { VS_OUTPUT OUT = (VS_OUTPUT)0; OUT.ObjectPosition = IN.ObjectPosition; return OUT; } /************* Hull Shaders *************/ HS_CONSTANT_OUTPUT constant_hull_shader(InputPatch<VS_OUTPUT, 4> patch, uint patchID : SV_PrimitiveID) { HS_CONSTANT_OUTPUT OUT = (HS_CONSTANT_OUTPUT)0; [unroll] for (int i = 0; i < 4; i++) { OUT.EdgeFactors[i] = TessellationEdgeFactors[i]; } OUT.InsideFactors[0] = TessellationInsideFactors[0]; OUT.InsideFactors[1] = TessellationInsideFactors[1]; return OUT; } [domain("quad")] [partitioning("integer")] [outputtopology("triangle_cw")] [outputcontrolpoints(4)] [patchconstantfunc("constant_hull_shader")] HS_OUTPUT hull_shader(InputPatch<VS_OUTPUT, 4> patch, uint controlPointID : SV_OutputControlPointID) { HS_OUTPUT OUT = (HS_OUTPUT)0; OUT.ObjectPosition = patch[controlPointID].ObjectPosition; return OUT; } /************* Domain Shader *************/ [domain("quad")] DS_OUTPUT domain_shader(HS_CONSTANT_OUTPUT IN, float2 uv : SV_DomainLocation, const OutputPatch<HS_OUTPUT, 4> patch) { DS_OUTPUT OUT; float4 v0 = lerp(patch[0].ObjectPosition, patch[1].ObjectPosition, uv.x); float4 v1 = lerp(patch[2].ObjectPosition, patch[3].ObjectPosition, uv.x); float4 objectPosition = lerp(v0, v1, uv.y); OUT.Position = mul(float4(objectPosition.xyz, 1.0f), WorldViewProjection); return OUT; } /************* Pixel Shader *************/ float4 pixel_shader(DS_OUTPUT IN) : SV_Target { return ColorWheat; } /************* Techniques *************/ technique11 main11 { pass p0 { SetVertexShader(CompileShader(vs_5_0, vertex_shader())); SetHullShader(CompileShader(hs_5_0, hull_shader())); SetDomainShader(CompileShader(ds_5_0, domain_shader())); SetPixelShader(CompileShader(ps_5_0, pixel_shader())); } }