Table of Contents

シェーダーステージ入出力の自動管理

上級 プログラマー

HLSL シェーダーを書く際には、頂点属性を正確に定義し、最終シェーダーのステージに慎重に渡す必要があります。

ここでは、頂点の色を使用するシンプルな HLSL シェーダーの例を示します。

struct VS_IN
{
	float4 pos : POSITION;
	float4 col : COLOR;
};

struct PS_IN
{
	float4 pos : SV_POSITION;
	float4 col : COLOR;
};

PS_IN VS( VS_IN input )
{
	PS_IN output = (PS_IN)0;

	output.pos = input.pos;
	output.col = input.col;

	return output;
}

float4 PS( PS_IN input ) : SV_Target
{
	return input.col;
}

technique10 Render
{
	pass P0
	{
		SetGeometryShader( 0 );
		SetVertexShader( CompileShader( vs_4_0, VS() ) );
		SetPixelShader( CompileShader( ps_4_0, PS() ) );
	}
}

モデルに法線を追加し、法線に応じて結果の色を変えたくなったとします。色を計算するコードを修正し、頂点シェーダーからピクセルシェーダーに属性を渡すために中間構造の調整をしなければなりません。また、使用するセマンティクスにも注意しなければなりません。

コード: 修正後の HLSL シェーダー

struct VS_IN
{
	float4 pos : POSITION;
	float4 col : COLOR;
	float3 normal : NORMAL;
};

struct PS_IN
{
	float4 pos : SV_POSITION;
	float4 col : COLOR;
	float3 normal : TEXCOORD0;
};

PS_IN VS( VS_IN input )
{
	PS_IN output = (PS_IN)0;

	output.pos = input.pos;
	output.col = input.col;
	output.normal = input.normal;

	return output;
}

float4 PS( PS_IN input ) : SV_Target
{
	return input.col * max(input.normal.z, 0.0);
}

technique10 Render
{
	pass P0
	{
		SetGeometryShader( 0 );
		SetVertexShader( CompileShader( vs_4_0, VS() ) );
		SetPixelShader( CompileShader( ps_4_0, PS() ) );
	}
}

この例はシンプルです。実際のプロジェクトにはもっとたくさんのシェーダーがあるので、1回の変更でたくさんのシェーダーや構造などを書き換えることになるかもしれません。

図式的には、新しい属性を追加する際には、頂点入力から、属性を使用する最後のステージまでのすべてのステージと中間構造を修正する必要があります。

media/hlsl_add_normal.png

SDSL (Stride Shading Language)

SDSL には、シェーダーのさまざまなステージで横断的にパラメーターを渡す便利な方法があります。ストリーム(stream)変数の概要は以下の通りです。

  • 変数です。
  • 他のシェーダーメンバと同じように定義され、stream というキーワードを持ちます。
  • stream プレフィックスと一緒に使用します(省略するとコンパイルエラーになります)。ストリームが曖昧な(同じ名前である)場合は、変数の前にシェーダー名を指定する必要があります(例: streams.<my_shader>.<my_variable>)。

ストリームは、属性、変化(varying)、出力を1つの概念にまとめています。

  • 属性とは、頂点シェーダーで書き込まれる前に読み込まれるストリームのことです。
  • 変化とは、シェーダーステージ全体で横断的に存在するストリームのことです。
  • 出力とは、読み込まれる前に割り当てられるストリームのことです。

ストリームは、関数のパラメーターとして指定しなくてもどこからでもアクセスできるグローバルオブジェクトだと考えてください。

Note

これらの変数のセマンティックを作成する必要はありません。コンパイラが自動的に作成します。ただし、同じセマンティックを共有する変数は最後のシェーダーでマージされることに注意してください。この仕様は、ストリーム変数を宣言したシェーダーを継承せずに、ローカルで使用したい場合に便利です。

ストリームを宣言した後は、シェーダーのどの段階からでもアクセスすることができます。シェーダーコンパイラがすべてを処理してくれます。ストリーム変数は、他の変数と同じように、呼び出し元のコード(つまり継承階層の中)から見えるようにする必要があります。

コード: ストリームの定義と使用

shader BaseShader
{
	stream float3 myVar;
 
	float3 Compute()
	{
		return streams.myVar;
	}
};

コード: ストリームの仕様

shader StreamShader
{
	stream float3 myVar;
};

shader ShaderA : BaseShader, StreamShader
{
	float3 Test()
	{
		return streams.BaseShader.myVar + streams.StreamShader.myVar;
	}
}

SDSL シェーダーの例

最初の例と同じ HLSL シェーダーを、SDSL で見てみましょう。

コード: SDSL でのシェーダー

shader MyShader : ShaderBase
{
	stream float4 pos : POSITION;
	stream float4 col : COLOR;

	override void VSMain()
	{
		streams.ShadingPosition = streams.pos;
	}

	override void PSMain()
	{
		streams.ColorTarget = streams.col;
	}
};

では、法線の計算を追加してみましょう。

コード: 修正後の SDSL シェーダー

shader MyShader : ShaderBase
{
	stream float4 pos : POSITION;
	stream float4 col : COLOR;
	stream float3 normal : NORMAL;

	override void VSMain()
	{
		streams.ShadingPosition = streams.pos;
	}

	override void PSMain()
	{
		streams.ColorTarget = streams.col * max(streams.normal.z, 0.0);
	}
};

SDSL では、新しい属性の追加は、ストリームのプールに追加して必要な場所で使用するだけという簡単さです。

media/sdsl_add_normal.png

関連項目