テクスチャを貼ったはずの四角形が、真っ黒です。モデルは正しい場所に出ていて、形も合っています。ただ、絵だけが出ません。CreateTexture2Dも、S_OKを返しています。
DirectX11では、テクスチャが読めなかったとき、エラーにならずに、黒く塗られることが、よくあります。原因は、1つではありません。そこで、テクスチャが、黒くなったり、色が変わったりする原因を、8通り再現して、画素の値と画像で比べました。デバッグレイヤーが、それぞれを教えてくれるかも、同時に確かめています。
画面は出さずに、128×128の描画先に、2×2のテクスチャを貼った四角形を描いて、画素を読み戻しました(MSVC 19.44、x64、Direct3D 11、ハードウェアドライバー)。
テクスチャが、画面に出るまでの流れ
シェーダーが、テクスチャの色を、読むまでには、
・テクスチャ(リソース) → シェーダーリソースビュー(SRV) → ピクセルシェーダーのスロットにセット → サンプラーで読み取る
という、段階があります。どこか1つでも抜けると、黒くなったり、色が変わったりします。ほとんどの原因は、この流れの、どこで切れているかを調べれば、見つかります。
実験の設定
テクスチャは、2×2で、左上が赤、右上が緑、左下が青、右下が黄です。ピクセルシェーダーは、tex.Sample(samp, uv)を返すだけです。ケースごとに、1つだけ、設定を壊しています。
▼描画前の設定(tx_exp.cppから抜粋。ケースによって、2・3行目のどちらかを飛ばす)
ctx->VSSetShader(vs, nullptr, 0); ctx->PSSetShader(ps, nullptr, 0);
if (cs != 1) ctx->PSSetShaderResources(0, 1, &srv);
if (cs != 2) ctx->PSSetSamplers(0, 1, &samp);
ctx->Draw(6, 0);描画後に、四角形の4つの領域と、(ミップのケースでは)中心の色を、読み戻しました。
結果:8通りの画像

CreateTexture2D: 0x00000000, CreateShaderResourceView: 0x00000000
ケース0: 正常(2x2のテクスチャ、ポイントサンプリング)
左上 RGB = (255, 0, 0) 右上 RGB = ( 0,255, 0)
左下 RGB = ( 0, 0,255) 右下 RGB = (255,255, 0)
デバッグレイヤーのメッセージ: 0 件
CreateTexture2D: 0x00000000, CreateShaderResourceView: 0x00000000
ケース1: シェーダーリソースビューを、セットし忘れた
左上 RGB = ( 0, 0, 0) 右上 RGB = ( 0, 0, 0)
左下 RGB = ( 0, 0, 0) 右下 RGB = ( 0, 0, 0)
デバッグレイヤーのメッセージ: 0 件
CreateTexture2D: 0x00000000, CreateShaderResourceView: 0x00000000
ケース2: サンプラーを、セットし忘れた
左上 RGB = (249, 3, 3) 右上 RGB = ( 3,255, 0)
左下 RGB = ( 3, 3,252) 右下 RGB = (255,255, 0)
デバッグレイヤーのメッセージ: 1 件
[WARNING] ID3D11DeviceContext::Draw: The Pixel Shader unit expects a Sampler to be set at Slot 0, but none is bound. This is perfectly valid, as a NULL Sampler maps to default Sampler state. However, the developer may not want to rely on th
CreateTexture2D: 0x00000000, CreateShaderResourceView: 0x80070057
ケース3: テクスチャ作成時に、BIND_SHADER_RESOURCEを付け忘れた
左上 RGB = ( 0, 0, 0) 右上 RGB = ( 0, 0, 0)
左下 RGB = ( 0, 0, 0) 右下 RGB = ( 0, 0, 0)
デバッグレイヤーのメッセージ: 2 件
[ERROR] ID3D11Device::CreateShaderResourceView: A ShaderResourceView cannot be created of a Resource that did not specify the D3D11_BIND_SHADER_RESOURCE BindFlag.
[ERROR] ID3D11Device::CreateShaderResourceView: Returning E_INVALIDARG, meaning invalid parameters were passed.
CreateTexture2D: 0x00000000, CreateShaderResourceView: 0x00000000
ケース4: ミップを全段確保したが、ミップを生成していない(小さく描く)
小さい四角形(約16画素)の中心 RGB = ( 0, 0, 0) (元のテクスチャは、全面(255,128,0))
デバッグレイヤーのメッセージ: 0 件
CreateTexture2D: 0x00000000, CreateShaderResourceView: 0x00000000
ケース5: ミップを全段確保して、GenerateMipsを呼んだ(小さく描く)
小さい四角形(約16画素)の中心 RGB = (255,128, 0) (元のテクスチャは、全面(255,128,0))
デバッグレイヤーのメッセージ: 0 件
CreateTexture2D: 0x00000000, CreateShaderResourceView: 0x00000000
ケース6: BGRAのデータを、RGBAのテクスチャとして読む
左上 RGB = ( 0, 0,255) 右上 RGB = ( 0,255, 0)
左下 RGB = (255, 0, 0) 右下 RGB = ( 0,255,255)
デバッグレイヤーのメッセージ: 0 件
CreateTexture2D: 0x00000000, CreateShaderResourceView: 0x00000000
ケース7: UV座標が、すべて(0, 0)
左上 RGB = (255, 0, 0) 右上 RGB = (255, 0, 0)
左下 RGB = (255, 0, 0) 右下 RGB = (255, 0, 0)
デバッグレイヤーのメッセージ: 0 件1つずつ見ていく
①正常
左上が赤、右上が緑、左下が青、右下が黄と、期待どおりでした。デバッグレイヤーのメッセージは、0件です。
②SRVを、セットし忘れた:真っ黒で、メッセージもなし
PSSetShaderResourcesを呼ばないと、4つの領域すべてが(0, 0, 0)の黒になりました。CreateTexture2DもCreateShaderResourceViewも、S_OKで、デバッグレイヤーのメッセージは、0件でした。「何も教えてくれない」ケースです。
③サンプラーを、セットし忘れた:色が、にじむ
サンプラーを、セットしなくても、描画はできました。ただし、画像のとおり、赤と緑の境目が、にじんでいます。サンプラーが未設定のときは、既定のサンプラーが使われて、フィルタリングが、効いてしまうからです。ポイントサンプリングのつもりが、線形補間になっています。デバッグレイヤーは、WARNINGで、「サンプラーが、設定されていませんが、NULLのサンプラーは、既定のサンプラー状態として扱われるので、有効です。ただし、その動作に頼らないほうがよいかもしれません」という趣旨のメッセージを、出しました。
④SRVの作成に、失敗している:真っ黒で、エラーは出る
テクスチャを作るとき、BindFlagsに、D3D11_BIND_SHADER_RESOURCEを付けないと、CreateShaderResourceViewが、0x80070057(E_INVALIDARG)で失敗します。失敗を見逃して、nullptrのSRVをセットすると、②と同じく、真っ黒になります。今回は、デバッグレイヤーが、「シェーダーリソースビューは、D3D11_BIND_SHADER_RESOURCEを指定していないリソースからは、作れません」と、ERRORで、原因を教えてくれました。
⑤【重要】ミップを、確保したのに、生成していない:小さく描くと、真っ黒
次は、ミップマップのケースです。256×256のテクスチャ(全面オレンジ)で、ミップを全段確保して、ミップ0にだけデータを書き込みました。それを、約16画素の、小さい四角形に貼ると、中心の色は、(0, 0, 0)の、黒でした(元の色は(255, 128, 0))。小さく描くと、縮小用のミップ(1以降)が、使われますが、そこには、何も書かれていなかったためです。
エラーも警告も、0件でした。しかも、テクスチャを大きく描くと、ミップ0が使われるので、正しく見えます。「近づくと見えるのに、遠くの物体だけが、真っ黒」という、気づきにくい症状になります。
⑥ミップを生成した:直る
同じ設定で、GenerateMipsを呼ぶと、中心の色は、元の(255, 128, 0)になりました。ミップを全段確保するなら、ミップ1以降を、生成するところまで、セットで行います。ミップマップの仕組みそのものは、ミップマップの記事で解説しています。
⑦BGRAのデータを、RGBAのテクスチャとして読む:色が、入れ替わる
画像データの、チャンネルの順番と、テクスチャのフォーマットが違うと、色が入れ替わります。今回は、B・G・R・Aの順に並べたデータを、R8G8B8A8のテクスチャとして読みました。左上が、赤ではなく青、左下が、青ではなく赤になりました。黒にはならないので、見た目で、すぐに気づけますが、画像ファイルのデコード結果をそのまま渡す場面では、起きやすいミスです。
⑧UV座標が、すべて(0, 0):1色になる
頂点のUV座標が、すべて(0, 0)だと、四角形の全体が、テクスチャの左上の1画素(赤)で塗られます。UVを、頂点バッファに書き込むときの、オフセットや、DXGI_FORMATの指定を間違えると、同じ見た目になります。
原因の切り分け:どこで、止まっているか
| 症状 | 疑うところ | デバッグレイヤー |
|---|---|---|
| 真っ黒(近くも遠くも) | SRVを、セットしたか。CreateShaderResourceViewの戻り値(②④) | SRV未設定は、0件。作成失敗は、ERRORで原因を教えてくれる |
| 遠くだけ真っ黒 | ミップを、生成したか(⑤) | 0件 |
| 色が、にじむ | サンプラーを、セットしたか(③) | WARNINGが出る |
| 赤と青が入れ替わる | データの、チャンネル順と、フォーマット(⑦) | 0件 |
| 1色になる | UV座標(⑧) | 0件 |
直し方の、チェックリスト
CreateTexture2DとCreateShaderResourceViewの、戻り値を確認する(FAILEDなら、その場で止める)- テクスチャに、
D3D11_BIND_SHADER_RESOURCEを付けたか - 描画の前に、
PSSetShaderResourcesとPSSetSamplersを、呼んでいるか - ミップを使うなら、ミップ1以降を、生成しているか(
GenerateMips、またはDirectXTexなど) - 画像データの、チャンネル順(RGBA・BGRA)と、
DXGI_FORMATが、合っているか - 頂点の、UV座標が、期待した範囲か(RenderDocなどのツールや、画素値の出力で確認)
テクスチャが黒くなるときの、よくある失敗例と対処法
①CreateShaderResourceViewの、戻り値を確認しない
実験④のとおり、失敗しても、プログラムは、そのまま進みます。FAILED(hr)なら、メッセージを出して、止めてください。デバッグレイヤーを有効にしておくと、失敗の理由が、文章で分かります。
②近くで確認して、安心する
実験⑤のとおり、ミップが生成されていなくても、近くでは、正しく見えます。テクスチャを、小さく描く場面(遠くの物体など)でも、確かめてください。
③デバッグレイヤーが静かなので、問題ないと思う
SRVのセット忘れ(②)と、ミップ未生成(⑤)は、メッセージが0件でした。三角形が出ない原因の記事の考え方と同じで、メッセージが無いことは、正しい証拠にはなりません。
注意点
- テクスチャが読めないとき、DirectX11は、エラーにせず、黒く塗ることが多い
- SRVの未設定と、ミップ未生成は、デバッグレイヤーが、何も言わなかった(0件)
BIND_SHADER_RESOURCEの付け忘れは、CreateShaderResourceViewがE_INVALIDARGで失敗し、デバッグレイヤーが原因を教えた- サンプラー未設定は、既定のサンプラーで、動いてしまう(フィルタリングが効き、色がにじむ)
- ミップは、確保しただけでは、中身が空。小さく描くと、真っ黒になった
- 今回の検証は、MSVC 19.44、Direct3D 11、ハードウェアドライバー、デバッグレイヤー有効、2×2と256×256のテクスチャで行った。結果は、ドライバーによって変わる可能性がある
この記事のポイント
- テクスチャの流れは、リソース → SRV → セット → サンプラー。どこで切れているかを探す
- 真っ黒の主な原因は、SRVのセット忘れ・作成失敗・ミップ未生成。戻り値と、セットの有無を、確認する
- 遠くだけ黒いなら、ミップを疑う
- デバッグレイヤーは、8通りのうち2つしか、教えてくれなかった。画素値で、確かめる
「黒い」という症状は、1つに見えて、原因は、いくつもあります。流れの順に、1段ずつ、確かめていくだけで、たいていは、見つかります。そのとき、画面の、特定の場所の画素値を出力して、数字で見る方法は、とても強力です。

