SlimDXでConstantBufferを使ってシェーダーに定数を渡す

DirectXで、例えば以下の頂点シェーダーのようにその頂点の変換行列を渡して、シェーダー内で変換を行いたいとする。

matrix ModelViewProjection;

struct VertexPositionColor
{
	float4 Position : SV_Position;
	float4 Color : COLOR;
};

VertexPositionColor VShader(VertexPositionColor input)
{
	input.Position = mul(input.Position, ModelViewProjection);
	return input;
}

DirectXのEffectsの場合、SlimDXではSlimDX.Direct3D11.Effect.GetVariableBy(Name|Index)メソッドを経由して、SlimDX.Direct3D11.EffectVariableを取得でき、このインスタンスを経由して、変数を設定したりできます。

effect.GetVariableByName("ModelViewProjection")
    .AsMatrix()
    .SetMatrix(modelMatrix * viewMatrix * projectionMatrix);

ただし、下記のように、各シェーダーを個々で生成し、DeviceContextにそれぞれのシェーダーを設定する方式をとっていたので、 その場合に、どのように変数を渡せばいいかわからなかったので、調べてみました。

using (var bytecode = ShaderBytecode.CompileFromFile("triangle.fx", "VShader", "vs_4_0", ShaderFlags.None, EffectFlags.None))
    vertexShader = new VertexShader(device, bytecode);

// load and compile the pixel shader
using (var bytecode = ShaderBytecode.CompileFromFile("triangle.fx", "PShader", "ps_4_0", ShaderFlags.None, EffectFlags.None))
    pixelShader = new PixelShader(device, bytecode);

ConstantBuffer

シェーダーに定数を渡してやるには、ConstantBufferを利用してやるとよさそう。 ConstantBufferは名前の通り定数を渡してやるバッファで、シェーダーに定数を渡す用途に利用できます。

使い方

基本は頂点バッファを作成するときと同じ要領で、まずはバッファに突っ込むデータSlimDX.DataStreamで用意します。

// 64は`SlimDX.Matrix`のサイズ
var stream = new SlimDX.DataStream(64, true, true);
// 事前に生成した、`SlimDX.Matrix`型の変数を渡す
stream.Write(matrix);
stream.Position = 0;

ConstantBufferとしてバッファを確保し、頂点シェーダーに渡してみます。Descriptionに、ConstantBufferを作成することを指定してやります。

using (var buffer = new SlimDX.Direct3D11.Buffer(
    deviceContext, // 書き込み対象の`SlimDX.Direct3D11.DeviceContext`
    stream,
    64,
    SlimDX.Direct3D11.ResourceUsage.Dynamic,
    SlimDX.Direct3D11.BindFlags.ConstantBuffer, // ConstantBufferであることを指定
    SlimDX.Direct3D11.CpuAccessFlags.Write,
    SlimDX.Direct3D11.ResourceOptionFlags.None, 4))
{
    deviceContext.VertexShader.SetConstantBuffer(buffer, 0);
}

頂点シェーダーは以下になります。

cbuffer c0
{
    matrix ModelViewProjection;
}

struct VertexPositionColor
{
	float4 Position : SV_Position;
	float4 Color : COLOR;
};

VertexPositionColor VShader(VertexPositionColor input)
{
	input.Position = mul(input.Position, ModelViewProjection);
	return input;
}

これで、頂点バッファに定数が渡せました。 余談ですが、cbufferはごっそり削除しても、$Globalsという名前のConstantBufferとして認識されるようです。

DirectX、まだまだ触り始めたばかりなので、間違っている箇所等、@yucchiy_までご連絡いただけると幸いです。

参考