DirectX 12支持tessellation,我们可以在vertex shader后加入hull shader和domain shader,来对顶点进行动态细分,以实现不同的效果。在CPU层面上,我们传入的顶点信息需要设置为patchlist,DirectX 12最多支持32个控制点的patchlist:

object->mPrimitiveTopology = D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST;

mCommandList->IASetPrimitiveTopology(object->mPrimitiveTopology);


另外,在创建pipeline state object时,也需要进行一下调整:

psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;


hull shader的定义如下:

struct HullOut
{
float3 PosL : POSITION;
};

[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("ConstantHS")]
[maxtessfactor(64.0f)]
HullOut HS(InputPatch<VertexOut, 4> p,
uint i : SV_OutputControlPointID,
uint patchId : SV_PrimitiveID)
{
HullOut hout;

hout.PosL = p[i].PosL;

return hout;
}


domain表示输入到hull shader的primitive格式,partitioning表示硬件tessellation的方式,outputtopology表示hull shader输出的primitive格式和绕序,outputcontrolpoints表示输出控制点的数量,这个数量决定了hull shader执行的次数。patchconstantfunc表示具体如何对一个primitive进行partition的函数,maxtessfactor表示最多tessellation的数量。

patchconstantfunc的定义如下:

struct PatchTess
{
float EdgeTess[4] : SV_TessFactor;
float InsideTess[2] : SV_InsideTessFactor;
};

PatchTess ConstantHS(InputPatch<VertexOut, 4> patch, uint patchID : SV_PrimitiveID)
{
PatchTess pt;

float3 centerL = 0.25f*(patch[0].PosL + patch[1].PosL + patch[2].PosL + patch[3].PosL);
float3 centerW = mul(float4(centerL, 1.0f), gWorld).xyz;

float d = distance(centerW, gEyePosW);

// Tessellate the patch based on distance from the eye such that
// the tessellation is 0 if d >= d1 and 64 if d <= d0. The interval
// [d0, d1] defines the range we tessellate in.

const float d0 = 20.0f;
const float d1 = 100.0f;
float tess = 64.0f*saturate( (d1-d)/(d1-d0) );

// Uniformly tessellate the patch.

pt.EdgeTess[0] = tess;
pt.EdgeTess[1] = tess;
pt.EdgeTess[2] = tess;
pt.EdgeTess[3] = tess;

pt.InsideTess[0] = tess;
pt.InsideTess[1] = tess;

return pt;
}


EdgeTess控制边tessellation的数量,InsideTess控制primitive内部tessellation的数量。

tessellation的结果会以uv的方式传递给domain shader。domain shader的定义如下:

struct DomainOut
{
float4 PosH : SV_POSITION;
};

// The domain shader is called for every vertex created by the tessellator.
// It is like the vertex shader after tessellation.
[domain("quad")]
DomainOut DS(PatchTess patchTess,
float2 uv : SV_DomainLocation,
const OutputPatch<HullOut, 4> quad)
{
DomainOut dout;

// Bilinear interpolation.
float3 v1 = lerp(quad[0].PosL, quad[1].PosL, uv.x);
float3 v2 = lerp(quad[2].PosL, quad[3].PosL, uv.x);
float3 p = lerp(v1, v2, uv.y);

// Displacement mapping
p.y = 0.3f*( p.z*sin(p.x) + p.x*cos(p.z) );

float4 posW = mul(float4(p, 1.0f), gWorld);
dout.PosH = mul(posW, gViewProj);

return dout;
}


最后运行的结果如下:

在DirectX 12中使用tessellation_DirectX

如果你觉得我的文章有帮助,欢迎关注我的微信公众号(大龄社畜的游戏开发之路-

在DirectX 12中使用tessellation_d3_02