前回の#4で四角形のポリゴンを表示することができましたが、単色の板を出しただけでは、あまり楽しいものではないですよね。今回は画像ファイルを読み込み、このポリゴンに画像の色を付けてみましょう。
まずは、いろいろと準備をしておきましょう
まず最初に、今回の講座で必要になるファイルの準備やソースコードの改良をしておきましょう。今回のプログラム集中できるようにするためです。
新規ソースファイルの作成
DirectX.hとTexture.cppとTexture.hファイルを作成しよう
プロジェクトへの追加もお忘れなく
後ほど、画像の取り扱いをするためのTextureクラスを、このファイルに書いていきます。
DirectXのまとめ役ヘッダー(DirectX.h)の記述
今現在はDirect3D.hとTexture.hの2種類のファイルが出来ています。今後まだまだ増えていく予定なので、今回はわかりやすさを重視し、お手軽にDirectX関係のヘッダーを一気にインクルードできるように、まとめ役のヘッダーを作りたいと思います。
いままでDirect3D.hに書いていたDirectX関係のヘッダーを、ここに移植してきましょう。
DirectX.hで、DirectX関係のヘッダーをまとめてインクルード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#pragma once // Direct3Dのライブラリを使用できるようにする #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "dxgi.lib") #pragma comment(lib, "d3dcompiler.lib") // Direct3Dの型・クラス・関数などを呼べるようにする #include <d3d11.h> #include <d3dcompiler.h> // DirectXMath(数学ライブラリ)を使用できるようにする #include <DirectXMath.h> // ComPtrを使用できるようにする #include <wrl/client.h> using Microsoft::WRL::ComPtr; // 自作のDirectX関係のヘッダーをインクルード #include "Direct3D.h" #include "Texture.h" |
今後、DirectX関係のヘッダーを作ったら、DirectX.hにincludeを追記していきます。
各ソースがDirectXを使うように修正
背景が黄色い行が追加や変更した部分です。コメントにしている箇所は削除しても大丈夫です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#pragma once /* // Direct3Dのライブラリを使用できるようにする #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "dxgi.lib") #pragma comment(lib, "d3dcompiler.lib") // Direct3Dの型・クラス・関数などを呼べるようにする #include <d3d11.h> #include <d3dcompiler.h> // DirectXMath(数学ライブラリ)を使用できるようにする #include <DirectXMath.h> // ComPtrを使用できるようにする #include <wrl/client.h> using Microsoft::WRL::ComPtr; // 自作のDirect3D関係のヘッダーをインクルードする #include "Texture.h" */ #include "DirectX.h" //========================================= // Direct3Dクラス //========================================= class Direct3D ~以下省略~ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include "framework.h" // DirectXクラスを使えるようにする #include "Source/DirectX/DirectX.h" // GameSystemクラスを使えるようにする #include "GameSystem.h" /* // Direct3Dクラスを使えるようにする #include "Source/DirectX/Direct3D.h" */ // ゲームの初期設定を行う void GameSystem::Initialize() ~以下省略~ |
インクルードのイメージ
結構横着しましたが、最初はこの方法が楽だと思います。
DirectX.hをincludeするだけで、DirectX関係の機能が全て使えるようになりますね。
画像を読み込むには?
画像ファイルを読み込むには、どうすればよいでしょうか?画像ファイルといっても、いろんな形式があります。
例えば、jpg, bmp, gif, png, tif, tga, dds, hdr, psdなどなど・・・
これらの画像ファイルは、お互いほとんど共通点がなく、中身は全然違う形式になっています。それらの画像形式の内部を全て理解し、自由に扱えるようになるには、大変な時間と努力が必要になってしまいます。これではゲームを作るのはいつのことやらって感じですよね。
でも安心してください。様々な画像ファイルを読み込むプログラムは、世の中のすごい人たちが既に作ってくれており、しかも無料で使用できるように提供してくれています。今回はそのような便利なライブラリ(プログラム)を使用して、簡単に画像を読み込んじゃいましょう。
DirectXTexの導入
DirectXTexとは、DirectXを開発しているMicrosoftが、DirectXで楽に画像を扱えるようにしてくれるライブラリです。githubにソースコードと説明書がありますが、今回はもっと簡単に導入できる「NuGet」で導入したいと思います。
興味のある人のために、DirectXTexライブラリ(github)のURLを記載しておきます。特に説明書の存在は知っておいた方が良いので、そのURLも記載しておきます。
・github
https://github.com/microsoft/DirectXTex
・説明書(英語)
https://github.com/microsoft/DirectXTex/wiki/DirectXTex
NuGetでDirectXTexを導入する方法
まずは下図のように「ソリューションのNuGetパッケージの管理」を選択してください。
下図のように、DirectXTexを検索し、導入先プロジェクトをチェックを入れて、インストールを押す
これで導入できました。簡単ですね!
必要なヘッダーをインクルードしよう
DirectXTex.hをインクルードすると、使用可能になります。DirectX.hでインクルードしておこう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#pragma once // Direct3Dのライブラリを使用できるようにする #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "dxgi.lib") #pragma comment(lib, "d3dcompiler.lib") // Direct3Dの型・クラス・関数などを呼べるようにする #include <d3d11.h> #include <d3dcompiler.h> // DirectXMath(数学ライブラリ)を使用できるようにする #include <DirectXMath.h> // DirectXテクスチャライブラリを使用できるようにする #include <DirectXTex.h> // ComPtrを使用できるようにする #include <wrl/client.h> using Microsoft::WRL::ComPtr; // 自作のDirectX関係のヘッダーをインクルード #include "Direct3D.h" #include "Texture.h" |
画像を読み込む(Textureクラスの作成)
では実際にDirectXTexの機能を使って画像を読み込み、DirectXで使えるようにしましょう。
画像のことをテクスチャと読んだりします。「表面の質感」とかいう意味ですね。
Textureクラスの作成
画像を読み込んだり、読み込んだ画像を管理したりするためのクラスを作成しましょう。冒頭で作成したファイル(Texture.hとTexture.cpp)に書き込んでいきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#pragma once #include "DirectX.h" //========================================= // テクスチャクラス //========================================= class Texture { public: // シェーダーリソースビュー(画像データ読み取りハンドル) ComPtr<ID3D11ShaderResourceView> m_srv = nullptr; // 画像情報 DirectX::TexMetadata m_info = {}; // 画像ファイルを読み込む bool Load(const std::string& filename); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include "framework.h" #include <locale.h> #include "Direct3D.h" #include "Texture.h" bool Texture::Load(const std::string& filename) { // マルチバイト文字列からワイド文字列へ変換 setlocale(LC_CTYPE, "jpn"); wchar_t wFilename[256]; size_t ret; mbstowcs_s(&ret, wFilename, filename.c_str(), 256); // WIC画像を読み込む auto image = std::make_unique<DirectX::ScratchImage>(); if (FAILED(DirectX::LoadFromWICFile(wFilename, DirectX::WIC_FLAGS_NONE, &m_info, *image))) { // 失敗 m_info = {}; return false; } // ミップマップの生成 if (m_info.mipLevels == 1) { auto mipChain = std::make_unique<DirectX::ScratchImage>(); if (SUCCEEDED(DirectX::GenerateMipMaps(image->GetImages(), image->GetImageCount(), image->GetMetadata(), DirectX::TEX_FILTER_DEFAULT, 0, *mipChain))) { image = std::move(mipChain); } } // リソースとシェーダーリソースビューを作成 if (FAILED(DirectX::CreateShaderResourceView(D3D.m_device.Get(), image->GetImages(), image->GetImageCount(), m_info, &m_srv))) { // 失敗 m_info = {}; return false; } // 成功! return true; } |
説明
・DirectX::LoadFromWICFile関数
この関数がWIC画像を読み込む命令です。WICとは「Windows Imaging Component」の略で、簡単に言うとWindowsが対応している画像形式です。主にbmp, png, gif, tiff, jpegなどが読み込めます。
また、これら以外の画像ファイル(DDS、HDR)を読み込む関数も用意されています。この講座では主にpngファイルを使用していきます。
GenerateMipMaps関数
画像を読み込んだあとは、ミップマップ画像を生成しています。これは主に3Dの描画で活躍するものなので、今回は説明を省略します。
CreateShaderResourceView関数
最後に「リソースとシェーダーリソースビューを作成」をしています。読み込んだ画像データを、実際にDirectXの描画で使用できるようにビデオメモリに書き込んで、そのハンドルを作成しています。
- リソース
・・・VRAMに置かれた画像データそのもの。 - ビュー
・・・画像本体であるリソースにアクセスするためのハンドル。
描画で使用するのはビューの方です。今回作成したビューはシェーダーリソースビュー、略してSRVと言います。
ビューを使用することで、画像本体にアクセスできるんですね。
Textureクラスを使用して、実際に画像を読み込む
作ったTextureクラスを使用して、画像を読み込んでみましょう。今回使用する画像は以下の画像です。右クリックで保存しましょう。保存場所はApplicationフォルダ以下にDataフォルダを作ってそこに保存しておきます。
こんな感じに保存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#pragma once //========================================= // GameSystemクラス // ・このゲームの土台となるもの //========================================= class GameSystem { public: // このゲームの初期設定を行う void Initialize(); // このゲーム世界の時間を進める(処理を実行する) void Execute(); // その他、ゲーム用のデータなどをココに書く Texture m_tex; // テクスチャ変数 //========================================= // 今回このクラスも、どこからでもアクセスできるように ~以下省略~ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include "framework.h" // DirectXクラスを使えるようにする #include "Source/DirectX/DirectX.h" // GameSystemクラスを使えるようにする #include "GameSystem.h" // ゲームの初期設定を行う void GameSystem::Initialize() { // 画像の読み込み m_tex.Load("Data/Logo.png"); } // このゲームの時間を進める(処理を実行する) void GameSystem::Execute() { ~以下省略~ |
テクスチャをシェーダーに送る
では次は読み込んだテクスチャを、デバイスコンテキストさんにお願いしてシェーダーにセットして、描画で使えるようにしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
~省略~ // プロミティブ・トポロジーをセット D3D.m_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); //----------------------------- // シェーダーをセット //----------------------------- D3D.m_deviceContext->VSSetShader(D3D.m_spriteVS.Get(), 0, 0); D3D.m_deviceContext->PSSetShader(D3D.m_spritePS.Get(), 0, 0); D3D.m_deviceContext->IASetInputLayout(D3D.m_spriteInputLayout.Get()); // こんな感じで、ひたすらデバイスコンテキストに情報を渡す // テクスチャを、ピクセルシェーダーのスロット0にセット D3D.m_deviceContext->PSSetShaderResources(0, 1, m_tex.m_srv.GetAddressOf()); //----------------------------- // 描画実行 //----------------------------- // デバイスコンテキストくん、上記のセットした内容で描画してください D3D.m_deviceContext->Draw(4, 0); } // バックバッファの内容を画面に表示 D3D.m_swapChain->Present(1, 0); } |
PSSetShadeResources関数で、ピクセルシェーダーのテクスチャスロットに画像をセットできます。テクスチャスロットとは簡単に言うと、テクスチャが入るロッカーみたいなものです。0~127の128個あります。合計128枚テクスチャを同時にロッカーに入れておけるのです。
このロッカーにテクスチャを入れておけば、シェーダーから簡単にテクスチャを使用することができるのです。
シェーダーでテクスチャから色を取得する
テクスチャスロットと言うロッカーにテクスチャを入れたので、実際にシェーダーからテクスチャを使用してみましょう。久々ですので覚えていますか?シェーダーのソースコードはSpriteShader.hlslというファイルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// 0番のテクスチャスロットを使用する Texture2D g_texture : register(t0); // 0番のサンプラスロットを使用する SamplerState g_sampler : register(s0); // 頂点シェーダーから出力するデータ struct VSOutput { float4 Pos : SV_Position; // 頂点の座標(射影座標系) }; //======================================== // 頂点シェーダー //======================================== VSOutput VS(float4 pos : POSITION) { VSOutput Out; // 頂点座標を、何も加工せずそのまま出力 Out.Pos = pos; return Out; } //======================================== // ピクセルシェーダー //======================================== float4 PS(VSOutput In) : SV_Target0 { // テクスチャから色を取得 float4 texColor = g_texture.Sample(g_sampler, float2(0.6, 0.6)); // テクスチャの色を出力 return texColor; } |
説明
// 0番のテクスチャスロットを使用する
Texture2D g_texture : register(t0);
ここで0番のロッカーに入っているテクスチャをg_textureという名前で使用します!って言ってます。
// 0番のサンプラスロットを使用する
SamplerState g_sampler : register(s0);
これはサンプラというものを使用するように言ってるとこです。サンプラはテクスチャから色を取得する道具のようなものです。この道具(サンプラ)の性能によって、色の取り方も変えることが出来ます。実は現在サンプラは作ってもないしセットもしてないのですが、一応動作するので今回はとりあえずこれで行きます。後ほど正式にサンプラも作っていきます。
// テクスチャから色を取得
float4 texColor = g_texture.Sample(g_sampler, float2(0.6, 0.6));
ここがテクスチャから色を取得している場所です。「g_textureからg_samplerの道具を使って(0.6, 0.6)の位置の色を取ってこい!」って命令です。この(0.6, 0.6)という座標は「UV座標」と言い、射影座標とはまた違った2Dの座標なんです。
射影座標とかUV座標とか、種類が多くて最初は覚えるのが大変ですね。
UV座標
UV座標とは、画像の左上を(0, 0)とし、画像の右下が(1, 1)の座標系です。画像のサイズは関係なしに0~1なのです。
ちなみにUnityでは、左下が(0,0)で右上が(1,1)です。混乱しそうですね。
実行結果
(0.6, 0.6)、つまり画像の真ん中ちょい右下あたりの、青っぽい色を取得して出力しています。
なんか違う
なんか思った結果と違いますね。ちゃんと画像全体をポリゴンに張り付けたいですよね。
次回は、きちんとポリゴンに画像が表示されるようにしていきたいと思います。
プログラムも増えてきたので、今回までのプロジェクトを下記からダウンロードできるようにしました。