11の機能はほとんど使ってなくてインチキくさいので今回は11の機能についてです。
プログラマブルシェーダは自由度が高く色々なことが出来ますので、
処理がどんどん増えていきます。
縦にコードが長くなるのはそれほど困らないのですが、
場合分けで横に増えていくと困りものです。
例えば、ポリゴンをそのまま表示する場合とテクスチャを張る場合。
ライトを当てる場合と当てない場合。
これだけでもう2 × 2 = 4のシェーダが必要になります。
他にも描画要素はたくさんあるので、
掛け算でシェーダ数が増えて行ってとんでもない数になります。
「100万パワー+100万パワーで200万パワー!」
「いつもの2倍のジャンプが加わって200×2の400万パワー!」
「そしていつもの3倍の回転を加えれば400×3の・・・!
バッファローマン!お前を上回る1200万パワーだーーーーっ!」
みたいなもんです。
if文を使った条件分岐でそれなりに対応は出来そうですが、難しい時もあるでしょう。
そういった状況のためにDirect3D 11からはDynamic Shader Linkageが追加されました。
OOPでおなじみのinterfaceとclassの概念をHLSLで使うことが出来ます。
先ほども出た、ポリゴンそのままとテクスチャ有りの場合分けを例にします。
今回のソース
HSLS側の準備
どちらの場合でも色データがとれればいいので、interfaceは次のように定義しました。1 | interface BaseColor |
---|---|
2 | { |
3 | float4 GetColor(float2 uv); |
4 | }; |
このinterfaceに対するclassとして次のように実装しました。
1 | class RawPolygonColor : BaseColor |
---|---|
2 | { |
3 | float4 GetColor(float2 uv) |
4 | { |
5 | // 固定で白を返す |
6 | return float4(1, 1, 1, alpha); |
7 | } |
8 | }; |
9 | |
10 | class TexturedColor : BaseColor |
11 | { |
12 | float4 GetColor(float2 uv) |
13 | { |
14 | // 与えられたuv座標でテクスチャの色を返す |
15 | float4 color = tex.Sample(wrapSampler, uv); |
16 | color.a *- alpha; |
17 | return color; |
18 | } |
19 | }; |
interfaceのインスタンス変数も用意します。
1 | BaseColor baseColor; |
---|
インスタンス変数はこのように使えます。
1 | float4 oc = baseColor.GetColor(input.uv); |
---|
Direct3D側の準備
Direct3Dから、HLSLのインスタンス変数に実装クラスのインスタンスをセットします。
ID3D11Device::CreateClassLinkageを使って、HLSL上interfaceのDirect3Dインターフェース(ややこしい)を作成します。
1 | device_.CreateClassLinkage(&pixelShaderClassLinkage_) |
---|
作成したインターフェースはID3D11Device::CreateXXXShaderに渡して関連付けます。
1 | device_.CreatePixelShader(compiled.GetBufferPointer(), compiled.GetBufferSize(), pixelShaderClassLinkage_, &pixelShader_) |
---|
この後、リフレクションを使って、interfaceが何番目のスロットに対応するかチェックするのですが、
今回はinterfaceのインスタンスが1個のため確実に0番になるので省略します。
classのインスタンスはID3D11ClassLinkage::CreateClassInstanceを使って、HLSL上のクラス名から取得します。
1 | pixelShaderClassLinkage_.CreateClassInstance("RawPolygonColor", 0, 0, 0, 0, &pixelShaderRawPolygonColorClass_) |
---|---|
2 | pixelShaderClassLinkage_.CreateClassInstance("TexturedColor", 0, 0, 0, 0, &pixelShaderTexturedColorClass_) |
(class内にメンバ変数がある場合は、GetClassInstanceを使います)
あとは表示条件に従ってinterfaceのインスタンス変数にclassインスタンスのどちらかをセットして使います。
1 | immediateContext_.PSSetShader(pixelShader_, &pixelShaderRawPolygonColorClass_, 1); |
---|