日別アーカイブ: 2008年11月14日

Direct3D11/DirectX11 (8) テセレータの動作

●固定機能テセレータ

DomainShader で、Tessellator の出力 uv のみを WIRE 描画したものです。

TessFactor = 1, 1, 1, 1
InsideTessFactor = 1, 1
Tessellator1111_11

TessFactor = 3, 3, 3, 3
InsideTessFactor = 3, 3
Tessellator3333_33

TessFactor = 3, 3, 3, 3
InsideTessFactor = 8, 8
Tessellator3333_88

パッチ毎に分割数が異なる可能性があるため内部とエッジの係数を個別に指定します。
上の 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;
}

●ベジェ

実際にシンプルなベジェ曲面を生成してみます。
単純化するため入力はすべてコントロールポイントとします。

Tessellator32

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