OpenGL ES 2.0 と Direct3D のシェーダー パイプラインの比較
重要な API
概念的には、Direct3D 11 のシェーダー パイプラインは OpenGL ES 2.0 のそれとよく似ています。 ただし、API の設計という点では、シェーダー ステージを作成、管理するための主要コンポーネントは、ID3D11Device1 と ID3D11DeviceContext1 という 2 つのプライマリ インターフェイスに含まれています。 このトピックでは、OpenGL ES 2.0 の一般的なシェーダー パイプライン API パターンが、Direct3D 11 におけるこれらのインターフェイスの何に対応するかを説明します。
Direct3D 11 のシェーダー パイプラインについて
シェーダー オブジェクトは ID3D11Device1::CreateVertexShader や ID3D11Device1::CreatePixelShader などの ID3D11Device1 インターフェイスのメソッドで作成されます。
Direct3D 11 グラフィックス パイプラインは、ID3D11DeviceContext1 インターフェイスのインスタンスで管理され、次のステージが含まれます。
- 入力アセンブラー ステージ: 入力アセンブラー ステージでは、パイプラインにデータ (三角形、線、点) を提供します。 このステージをサポートする ID3D11DeviceContext1 メソッドには、"IA" というプレフィックスが付きます。
- 頂点シェーダー ステージ: 頂点シェーダー ステージでは頂点を処理し、通常は、変換、スキンの適用、照明の適用などの操作を実行します。 頂点シェーダーは常に 1 つの入力頂点を受け取り、1 つの出力頂点を生成します。 このステージをサポートする ID3D11DeviceContext1 メソッドには、"VS" というプレフィックスが付きます。
- ストリーム出力ステージ: ストリーム出力ステージでは、パイプラインからラスタライザーへの途中でメモリにプリミティブ データをストリーミングします。 データはラスタライザーにストリーミングするか、渡すことができます。 メモリにストリーミングされたデータは、CPU からの入力データまたはリード バックとして、パイプラインに再循環させることができます。 このステージをサポートする ID3D11DeviceContext1 メソッドには、"SO" というプレフィックスが付きます。
- ラスタライザー ステージ: ラスタライザーは、プリミティブをトリミングし、ピクセル シェーダー用にプリミティブを準備して、ピクセル シェーダーを呼び出す方法を決定します。 ピクセル シェーダーがないことをパイプラインに示し (ID3D11DeviceContext::PSSetShader でピクセル シェーダー ステージを NULL に設定)、深度とステンシルのテストを無効にすることで (D3D11_DEPTH_STENCIL_DESC で DepthEnable と StencilEnable を FALSE に設定)、ラスター化を無効にすることができます。 無効になっている間、ラスター化関連のパイプライン カウンターは更新されません。
- ピクセル シェーダー ステージ: ピクセル シェーダー ステージがプリミティブの補間データを受信し、カラーなどのピクセル単位のデータを生成します。 このステージをサポートする ID3D11DeviceContext1 メソッドには、"PS" というプレフィックスが付きます。
- 出力マージャー ステージ: 出力マージャー ステージでは、各種出力データ (ピクセル シェーダー値、深度とステンシルの情報) をレンダー ターゲットおよび深度/ステンシル バッファーのコンテンツと結合し、最終的なパイプラインの結果を生成します。 このステージをサポートする ID3D11DeviceContext1 メソッドには、"OM" というプレフィックスが付きます。
(ジオメトリ シェーダー、ハル シェーダー、テッセレーター、ドメイン シェーダーのステージもありますが、OpenGL ES 2.0 には類似するものがないので、ここでは説明しません) これらのステージのメソッドの詳しい一覧については、ID3D11DeviceContext と ID3D11DeviceContext1 のリファレンス ページをご覧ください。 ID3D11DeviceContext1 は Direct3D 11 向けに ID3D11DeviceContext を拡張したものです。
シェーダーの作成
Direct3D では、シェーダー リソースは、コンパイルと読み込みの前に作成されません。このリソースは HLSL が読み込まれたときに作成されます。 そのため、GL_VERTEX_SHADER or GL_FRAGMENT_SHADER など、特定の型の初期化されたシェーダー リソースを作成する glCreateShader に相当する関数はありません。 Direct3D では、シェーダーは HLSL が ID3D11Device1::CreateVertexShader や ID3D11Device1::CreatePixelShader などの特定の関数で読み込まれた後で作成されます。これらの関数は、パラメーターとして型とコンパイルされた HLSL を受け取ります。
OpenGL ES 2.0 | Direct3D 11 |
---|---|
glCreateShader | コンパイルされたシェーダー オブジェクトが正常に読み込まれた後で ID3D11Device1::CreateVertexShader と ID3D11Device1::CreatePixelShader を呼び出し、それらに CSO をバッファーとして渡します。 |
シェーダーのコンパイル
Direct3D のシェーダーは、ユニバーサル Windows プラットフォーム (UWP) アプリのコンパイル済みシェーダー オブジェクト (.cso) ファイルとしてプリコンパイルされ、いずれかの Windows ランタイム ファイル API を使って読み込まれる必要があります (デスクトップ アプリは実行時にテキスト ファイルからのシェーダーや文字列をコンパイルできます)。CSO ファイルは、Microsoft Visual Studio プロジェクトに含まれる任意の .hlsl ファイルから作成され、同じ名前が保持されます。ただし、ファイル拡張子は .cso です。 出荷時に、これらがパッケージに含まれていることを確かめてください。
OpenGL ES 2.0 | Direct3D 11 |
---|---|
glCompileShader | 該当なし。 シェーダーを Visual Studio の .cso ファイルにコンパイルし、パッケージに含めます。 |
コンパイルの状態に glGetShaderiv を使用 | 該当なし。 コンパイルでエラーが発生した場合は、Visual Studio の FX Compiler (FXC) からのコンパイル出力を調べてください。 コンパイルが成功すると、対応する CSO ファイルが作成されます。 |
シェーダーの読み込み
シェーダーの作成に関するセクションで触れたように、Direct3D 11 では、対応する CSO ファイルがバッファーに読み込まれ、次の表のいずれかのメソッドに渡されたときに、シェーダーを作成します。
OpenGL ES 2.0 | Direct3D 11 |
---|---|
ShaderSource | コンパイルされたシェーダー オブジェクトが正常に読み込まれた後で ID3D11Device1::CreateVertexShader と ID3D11Device1::CreatePixelShader を呼び出します。 |
パイプラインの設定
OpenGL ES 2.0 には、実行のための複数のシェーダーを含む "シェーダー プログラム" オブジェクトがあります。 個々のシェーダーはシェーダー プログラム オブジェクトにアタッチされます。 ただし、Direct3D 11 では、レンダリング コンテキスト (ID3D11DeviceContext1) を直接操作して、そのコンテキストでシェーダーを作成します。
OpenGL ES 2.0 | Direct3D 11 |
---|---|
glCreateProgram | 該当なし。 Direct3D 11 では、シェーダー プログラム オブジェクト アブストラクションは使われません。 |
glLinkProgram | 該当なし。 Direct3D 11 では、シェーダー プログラム オブジェクト アブストラクションは使われません。 |
glUseProgram | 該当なし。 Direct3D 11 では、シェーダー プログラム オブジェクト アブストラクションは使われません。 |
glGetProgramiv | 作成した、ID3D11DeviceContext1 への参照を使います。 |
静的な D3D11CreateDevice メソッドを使って ID3D11DeviceContext1 と ID3D11Device1 のインスタンスを作成します。
Microsoft::WRL::ComPtr<ID3D11Device1> m_d3dDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1> m_d3dContext;
// ...
D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
creationFlags, // Set set debug and Direct2D compatibility flags.
featureLevels, // List of feature levels this app can support.
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for UWP apps.
&device, // Returns the Direct3D device created.
&m_featureLevel, // Returns feature level of device created.
&m_d3dContext // Returns the device's immediate context.
);
ビューポートの設定
Direct3D 11 のビューポートの設定は、OpenGL ES 2.0 でのビューポートの設定方法とよく似ています。 Direct3D 11 では、構成済みの CD3D11_VIEWPORT で、ID3D11DeviceContext::RSSetViewports を呼び出します。
Direct3D 11: ビューポートを設定します。
CD3D11_VIEWPORT viewport(
0.0f,
0.0f,
m_d3dRenderTargetSize.Width,
m_d3dRenderTargetSize.Height
);
m_d3dContext->RSSetViewports(1, &viewport);
OpenGL ES 2.0 | Direct3D 11 |
---|---|
glViewport | CD3D11_VIEWPORT、ID3D11DeviceContext::RSSetViewports |
頂点シェーダーの構成
Direct3D 11 の頂点シェーダーの構成は、シェーダーの読み込み時に行われます。 Uniform は ID3D11DeviceContext1::VSSetConstantBuffers1 を使って定数バッファーとして渡されます。
OpenGL ES 2.0 | Direct3D 11 |
---|---|
glAttachShader | ID3D11Device1::CreateVertexShader |
glGetShaderiv、glGetShaderSource | ID3D11DeviceContext1::VSGetShader |
glGetUniformfv、glGetUniformiv | ID3D11DeviceContext1::VSGetConstantBuffers1 |
ピクセル シェーダーの構成
Direct3D 11 のピクセル シェーダーの構成は、シェーダーの読み込み時に行われます。 Uniform は ID3D11DeviceContext1::PSSetConstantBuffers1 を使って定数バッファーとして渡されます。
OpenGL ES 2.0 | Direct3D 11 |
---|---|
glAttachShader | ID3D11Device1::CreatePixelShader |
glGetShaderiv、glGetShaderSource | ID3D11DeviceContext1::PSGetShader |
glGetUniformfv、glGetUniformiv | ID3D11DeviceContext1::PSGetConstantBuffers1 |
最終結果の生成
パイプラインが完了したら、バック バッファーにシェーダー ステージの結果を描画します。 Direct3D 11 では、Open GL ES 2.0 と同様に、描画コマンドを呼び出して結果をバック バッファーにカラー マップとして出力し、そのバック バッファーをディスプレイに送信する必要があります。
OpenGL ES 2.0 | Direct3D 11 |
---|---|
glDrawElements | ID3D11DeviceContext1::Draw、ID3D11DeviceContext1::DrawIndexed (または、ID3D11DeviceContext1 の他の Draw* メソッド)。 |
eglSwapBuffers | IDXGISwapChain1::Present1 |
HLSL への GLSL の移植
GLSL と HLSL は、複合型のサポートと全体的な構文以外はそれほど違いません。 最も簡単な移植方法は、一般的な OpenGL ES 2.0 の命令と定義を HLSL の対応する要素にエイリアシングすることです。 グラフィックス インターフェイスでサポートされる HLSL の機能セットを表現するために、Direct3D ではシェーダー モデル バージョンを使います。OpenGL には、HLSL 向けに異なるバージョン仕様があります。 次の表では、他のバージョンの観点から、Direct3D 11 と OpenGL ES 2.0 に対して定義されているシェーダー言語機能セットについて、簡単に示します。
シェーダー言語 | GLSL 機能バージョン | Direct3D シェーダー モデル |
---|---|---|
Direct3D 11 HLSL | ~ 4.30。 | SM 5.0 |
OpenGL ES 2.0 向けの GLSL ES | 1.40。 OpenGL ES 2.0 向けの GLSL ES の以前の実装では、1.10 から 1.30 までを使用できます。 元のコードは、glGetString(GL_SHADING_LANGUAGE_VERSION) または glGetString(SHADING_LANGUAGE_VERSION) を確認して、判断してください。 | ~ SM 2.0 |
2 つのシェーダー言語の違いと一般的な構文のマッピングについて詳しくは、「GLSL と HLSL の対応を示すリファレンス」をご覧ください。
HLSL セマンティクスへの OpenGL 組み込みメソッドの移植
Direct3D 11 HLSL セマンティクスは、uniform や属性名と同様に、アプリとシェーダー プログラムの間で渡される値を識別するために使われる文字列です。 使用できる文字列はさまざまですが、用途を示す文字列 (POSITION、COLOR など) を使うことをお勧めします。 これらのセマンティクスは、定数バッファーまたはバッファー入力レイアウトを構成する際に割り当てます。 類似する値に別のレジスタを使うために、セマンティクスに 0 ~ 7 の範囲の数値を追加できます。 COLOR0、COLOR1、COLOR2 などです。
"SV_" というプレフィックスが付いたセマンティクスは、シェーダー プログラムによって書き込みが行われるシステム値のセマンティクスです。CPU 上で実行されているアプリ自体で変更することはできません。 通常、これらには、グラフィックス パイプラインの別のシェーダー ステージからの入出力値のほか、GPU のみによって生成された値が含まれます。
また、SV_ セマンティクスは、シェーダー ステージに対する入出力を指定するために使用される場合は、さまざまな動作を示します。 たとえば、SV_POSITION (出力) には、頂点シェーダー ステージで変換された頂点データが含まれ、SV_POSITION (入力) には、ラスター化中に補間されたピクセル位置の値が含まれます。
一般的な OpenGL ES 2.0 シェーダー組み込みのマッピングをいくつか次に示します。
OpenGL のシステム値 | 使用する HLSL セマンティック |
---|---|
gl_Position | 頂点バッファー データを表す POSITION(n)。 SV_POSITION によって、ピクセル位置がピクセル シェーダーに指定されます。これをアプリで書き込むことはできません。 |
gl_Normal | 頂点バッファーによって提供される標準データを表す NORMAL(n)。 |
gl_TexCoord[n] | シェーダーに提供されるテクスチャ UV (一部の OpenGL のドキュメントでは ST) 座標データを表す TEXCOORD(n)。 |
gl_FragColor | シェーダーに提供される RGBA カラー データを表す COLOR(n)。 座標データと同様に処理されることに注意してください。このセマンティクスは、単に色データであることを示すために使用します。 |
gl_FragData[n] | ピクセル シェーダーからターゲット テクスチャまたは他のピクセル バッファーへの書き込みを表す SV_Target[n]。 |
セマンティクスのコーディングに使うメソッドは、OpenGL ES 2.0 での組み込みメソッドの使用と同じではありません。 OpenGL では、構成または宣言なしで組み込みメソッドの多くに直接アクセスできます。Direct3D では、特定のセマンティクスを使うために、特定の定数バッファーでフィールドを宣言するか、シェーダーの main() メソッドの戻り値として宣言する必要があります。
定数バッファーの定義で使われるセマンティックの例を次に示します。
struct VertexShaderInput
{
float3 pos : POSITION;
float3 color : COLOR0;
};
// The position is interpolated to the pixel value by the system. The per-vertex color data is also interpolated and passed through the pixel shader.
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 color : COLOR0;
};
このコードは、単純な定数バッファーのペアを定義します。
フラグメント シェーダーによって返される値を定義するために使われるセマンティックの例を次に示します。
// A pass-through for the (interpolated) color data.
float4 main(PixelShaderInput input) : SV_TARGET
{
return float4(input.color,1.0f);
}
この場合、SV_TARGET は、シェーダーの実行が完了するときに、ピクセルの色 (4 つの浮動小数点値を持つベクターとして定義) が書き込まれるレンダー ターゲットの場所です。
Direct3D でのセマンティクスの使用について詳しくは、「HLSL セマンティクス」をご覧ください。