●固定機能テセレータ
DomainShader で、Tessellator の出力 uv のみを WIRE 描画したものです。
TessFactor = 1, 1, 1, 1
InsideTessFactor = 1, 1
TessFactor = 3, 3, 3, 3
InsideTessFactor = 3, 3
TessFactor = 3, 3, 3, 3
InsideTessFactor = 8, 8
パッチ毎に分割数が異なる可能性があるため内部とエッジの係数を個別に指定します。
上の 3333-33 と 3333-88 は繋がります。
固定機能であるテセレータは、TessFactor に応じて上記の uv パターンを生成し、
必要分の頂点を出力しているだけだと考えられます。
実際の曲面を作るのは HullShader と DomainShader の仕事です。
struct DS_OUTPUT {
float4 vPos : SV_Position;
};
[domain("quad")]
DS_OUTPUT main(
HS_POINT_DATA ip,
float2 uv : SV_DomainLocation,
const OutputPatch bpatch )
{
DS_OUTPUT dout;
uv+= float2( -0.5, -0.5 );
uv*= float2( 0.5, 0.5 );
dout.vPos= float4( uv.x, -uv.y, 0, 1 );
return dout;
}
●ベジェ
実際にシンプルなベジェ曲面を生成してみます。
単純化するため入力はすべてコントロールポイントとします。
VertexShader は 16個のコントロールポイント定義のみ。
// vs.hlsl
float4 main( uint vid : SV_VertexID ) : VSPOS
{
const float4 v[]= {
{ -2, 2, 2, 1, },
{ -1, 2, 0, 1, },
{ 1, 2, 0, 1, },
{ 2, 2, 2, 1, },
{ -2, 1, 0, 1, },
{ -1, 1, 0, 1, },
{ 1, 1, 0, 1, },
{ 2, 1, 0, 1, },
{ -2, -1, 0, 1, },
{ -1, -1, 0, 1, },
{ 1, -1, 0, 1, },
{ 2, -1, 0, 1, },
{ -2, -2, 2, 1, },
{ -1, -2, 0, 1, },
{ 1, -2, 0, 1, },
{ 2, -2, 2, 1, },
};
return v[vid];
}
HullShader は TessFactor 設定以外に特にやることがありません。
VS からの出力をスルーし、もう 1つは TessFactor を定義します。
// hs.hlsl
struct VS_OUTPUT {
float4 vPos : VSPOS;
};
struct HS_OUTPUT {
float3 vPos : TSPOS;
};
struct HS_DATA {
float Edge[4] : SV_TessFactor;
float Inside[2] : SV_InsideTessFactor;
};
HS_DATA func(
InputPatch ip,
uint patchID : SV_PrimitiveID )
{
HS_DATA pdata;
pdata.Edge[0]= 32.0;
pdata.Edge[1]= 32.0;
pdata.Edge[2]= 32.0;
pdata.Edge[3]= 32.0;
pdata.Inside[0]= 32.0;
pdata.Inside[1]= 32.0;
return pdata;
}
[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(16)]
[patchconstantfunc("func")]
HS_OUTPUT main(
InputPatch ip,
uint id : SV_OutputControlPointID,
uint patchID : SV_PrimitiveID )
{
HS_OUTPUT odata;
odata.vPos= ip[id].vPos.xyz; // そのまま渡すだけ
return odata;
}
DomainShader で、生成された頂点の実際の座標を求めます。
同時に View Projection の変換もします。
// ds.hlsl
struct HS_DATA {
float Edge[4] : SV_TessFactor;
float Inside[2] : SV_InsideTessFactor;
};
struct HS_OUTPUT {
float3 vPos : TSPOS;
};
struct DS_OUTPUT {
float4 vPos : SV_Position;
};
cbuffer perFrame {
float4x4 WVP; // View Projection
};
float4 Bezier0( float t )
{
float t2= 1.0-t;
return float4( t2*t2*t2, t2*t2* t*3, t2* t* t*3, t* t* t );
}
float3 UVtoPosition( const OutputPatch p, float2 uv )
{
float4 u= Bezier0( uv.x );
float4 v= Bezier0( uv.y );
float3 r;
r = v.x*(u.x*p[ 0].vPos+u.y*p[ 1].vPos+u.z*p[ 2].vPos+u.w*p[ 3].vPos);
r+= v.y*(u.x*p[ 4].vPos+u.y*p[ 5].vPos+u.z*p[ 6].vPos+u.w*p[ 7].vPos);
r+= v.z*(u.x*p[ 8].vPos+u.y*p[ 9].vPos+u.z*p[10].vPos+u.w*p[11].vPos);
r+= v.w*(u.x*p[12].vPos+u.y*p[13].vPos+u.z*p[14].vPos+u.w*p[15].vPos);
return r;
}
[domain("quad")]
DS_OUTPUT main(
HS_DATA ip,
float2 uv : SV_DomainLocation,
const OutputPatch bpatch )
{
DS_OUTPUT dout;
float3 pos= UVtoPosition( bpatch, uv );
dout.vPos= mul( float4( pos.xyz, 1 ), WVP );
return dout;
}
呼び出し側。GeometryShader は使用していません。
iContext->RSSetState( iRS_WIREFRAME );
iContext->VSSetShader( iVS, NULL, 0 );
iContext->HSSetShader( iHS, NULL, 0 );
iContext->DSSetConstantBuffers( 0, 1, &iPerFrame );
iContext->DSSetShader( iDS, NULL, 0 );
iContext->PSSetShader( iPS, NULL, 0 );
iContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_16_CONTROL_POINT_PATCHLIST );
iContext->Draw( 16, 0 );
関連エントリ
・Direct3D11/DirectX11 (7) テセレータの流れの基本部分
・Direct3D11/DirectX11 (6) D3D11 の ComputeShader を使ってみる
・Direct3D11/DirectX11 (5) WARP の試し方、Dynamic Shader Linkage
・Direct3D11/DirectX11 (4) FeatureLevel と旧 GPU の互換性、テクスチャ形式など
・Direct3D11 (DirectX11) シェーダーの書き込み RWBuffer 他
・Direct3D11 の遅延描画、スレッド対応機能、シェーダー命令
・Direct3D11 Technical Preview D3D11の互換性、WARP Driver