今回はいよいよ3Dモデルデータを使った3Dモデルの表示、をしていきましょう。
3Dゲーム感が一気に増してくる頃合いです、がんばっていきましょう。
前回の記事:
【3Dゲーム開発#08】3Dから2Dに落とす「ビュー変換・透視投影変換」
いよいよ(やっと)今回から3D描画のほうにはいっていきます。3D描画では奥行き(z)要素が加わりますが、人間の視覚と同様の描画をするには、単純に奥行きをいれればいいというだけのことではありません。また、3Dゲームではカメラ操作ができるのが一...
3Dゲーム開発記事のまとめ:「3Dゲーム開発」
モデルデータの用意
まずは、モデルデータがないと話になりませんね。モデルデータを用意しましょう。
今回はシンプルなボックスを表示しましょう。
以下の画像のようにBlenderで初期表示されるボックスを、FBX形式で出力したデータを以下のリンクに配置してあります。こちらをダウンロードして利用しましょう。
box.fbxのDownload
もちろん自分でBlenderで出力して用意してもかまいません。
Blenderを少しでも使えると、こういった簡単なテストモデルデータを用意したりできるので、使ってみるのも良いと思います。
参考:なぜFBX形式を使うか
Blenderのエクスポートメニューには様々な形式のファイルがありますが、今回はこの中から.fbxを使うとしました。
FBX形式を使うのは以下のような特徴があるためです。
- メッシュ以外にも骨・アニメーション・マテリアルなど、多くのデータを格納可能
- ゲーム制作の中間ファイルフォーマットとして最も一般的な形式の1つ
- ゲームエンジン(Unity、Unreal Engine)での推奨もFBX
- AutoDeskが管理しているフォーマットなので安心
ざっというと、3Dゲーム向けのデータとしてなんでも含めることができて汎用性が高い、その上業界的にも標準的に利用されているから、というところです。
特に、3Dゲームを作るなら、すぐにアニメーションデータも必要となりますので、FBXを使っておくのが今後のためにも無難です。
これだけ汎用的な分、ファイルサイズとしては大きくなりがちですが、中間ファイル(様々なソフトで扱える形式)として必要なのは汎用性です、こだわりがなければFBXで間違いないかと思います。
モデルデータのインポート:assimpの導入
モデルデータをプログラム上で扱うには、
- データをロード
- データをフォーマットに沿って解釈(パース)
- 頂点データをメモリ上に作成
といったことが必要になります。
特に「データをフォーマットに沿って解釈(パース)」は、自前でやるのは大変ですので、こういったデータを利用するためにはライブラリを使うことが多いです。
今回は、「assimp」という様々な形式のデータのインポートが可能なライブラリを利用させてもらいましょう。
ちなみにfbx形式のデータなら、AutoDesk公式のFBXSDKを使うことでもfbxデータのロード、解釈が可能です。
ただ今後、FBX形式以外のデータも利用できたほうがなにかと便利ですので(フリーサイトでFBX以外のデータを拾うなど)、多くの形式に対応したassimpを使っていきます。
NuGetでAssimpを使えるように
Assimpは、Visual Studioのパッケージ管理機能「NuGet」で公開されているので、NuGetを使うことで簡単に導入できます。
Visual Studioのメニューから
[ツール] → [NuGet パッケージマネージャー] → [ソリューションのNuGetパッケージの管理]
を選択することでNuGetのパッケージ管理画面が開きます。
ここで右上のパッケージソース(↓画像の赤枠)から nuget.org を選びましょう。(nuget.orgがない場合は後述参照)
検索フィルタで「assimp」といれて、今回は最初にでてくる「Assimp」ではなく「AssimpCpp」↓をインストールしましょう。
「Assimp」の方はバージョンが低いためか、こちらで試す限りfbxのロードはうまくいかないため、バージョンも新しくfbxにも問題なく使えるAssimpCppを使っていきます。
これだけで、そのプロジェクトでAssimpを使用する準備が整いました。
NuGetでインストールしたパッケージはソリューションのあるディレクトリの「package」というディレクトリ以下で管理されています。
パッケージソースに nuget.org がない場合の追加方法
パッケージソースにnuget.orgがない場合もあるので、ない場合はその右にある歯車(設定)から、自分で追加しましょう。
右上「+」ボタンから追加します。
名前は自由ですが「nuget.org」が無難です。
ソースは「https://api.nuget.org/v3/index.json」となります。(参考)
Assimpの使い方概要
Assimpの詳細な使い方は公式マニュアル「Assimp C++インターフェース」を参照しましょう。
基本的には、このマニュアルにあるとおり
- Assimp::Importerを使って、モデルをロード・解釈
- モデルデータは aiScene 構造体で取得
- aiSceneに含まれる aiMesh でメッシュデータを取得
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(pFile, Flag);
const aiMesh* mesh = scene->mMeshes[0];
// meshから頂点データなどを取得
今回実際に使ったコードは以降で見ていきます。
モデルのロード・解釈
さっそく、実際に用意したデータをロードして表示まで組み込んでみましょう。
Modelクラスの追加
今まではTriangleクラスで表示してましたが、これからはModelを表示していくのでModelクラスを追加します。
Modelは「複数のメッシュで構成される1つの表示物」という定義とします。
ですので、Meshを複数持てるように構成します。
Meshはもちろん1つでも構わず、今回のBoxのようなシンプルなモデルはMesh1つとなります。
class Mesh;
class Model
{
public:
Model();
~Model();
bool Setup(Renderer& renderer, const char* filePath);
void Terminate();
void Draw(Renderer& renderer);
private:
void setupTransform(Renderer& renderer);
private:
Mesh* meshes_ = nullptr;
u_int meshNum_ = 0;
};
#include "Model.h"
#include "Mesh.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
Model::Model()
{
}
Model::~Model()
{
SAFE_DELETE_ARRAY(meshes_);
}
bool Model::Setup(Renderer& renderer, const char* filePath)
{
// load処理
Assimp::Importer importer;
u_int flag = aiProcess_Triangulate;
auto pScene = importer.ReadFile(filePath, flag);
if (pScene == nullptr) return false;
meshNum_ = pScene->mNumMeshes;
if (meshNum_ > 0) {
meshes_ = new Mesh[meshNum_];
for (u_int meshIdx = 0; meshIdx < pScene->mNumMeshes; ++meshIdx) {
auto pMeshData = pScene->mMeshes[meshIdx];
if (meshes_[meshIdx].Setup(renderer, pMeshData) == false) {
return false;
}
}
}
return true;
}
void Model::Terminate()
{
for (u_int meshIdx = 0; meshIdx < meshNum_; ++meshIdx) {
meshes_[meshIdx].Terminate();
}
SAFE_DELETE_ARRAY(meshes_);
meshNum_ = 0;
}
void Model::Draw(Renderer& renderer)
{
for (u_int meshIdx = 0; meshIdx < meshNum_; ++meshIdx) {
meshes_[meshIdx].Draw(renderer);
}
}
void Model::setupTransform(Renderer& renderer)
{
// Triangle同様の処理
...
}
Meshクラスの追加
そして、Modelがもつ各メッシュを管理・描画するMeshクラスを追加します。
このMeshがこれまで描画で使っていた、頂点データの構築や、描画APIを叩くといった、3Dオブジェクト描画のコアの部分となります。
また描画処理は、これまでにやっていた頂点バッファのみを指定する方法から、頂点インデックスバッファも指定する形に変わっています。
そういった点を踏まえながら、次項でMeshの処理の中身とともに見ていきます。
struct aiMesh;
class Mesh
{
public:
Mesh();
~Mesh();
bool Setup(Renderer& renderer, aiMesh* pMeshData);
void Terminate();
void Draw(Renderer& renderer);
private:
bool createVertexBuffer(Renderer& renderer);
bool createIndexBuffer(Renderer& renderer);
void destroyVertexBuffer();
void destroyIndexBuffer();
private:
// データの解釈ワーク
Vertex* vertices_ = nullptr;
u_int* indices_ = nullptr;
u_int vertexNum_ = 0;
u_int indexNum_ = 0;
// バッファリソース
ID3D11Buffer* vertexBuffer_ = nullptr;
ID3D11Buffer* indexBuffer_ = nullptr;
};
インデックスバッファを使った描画処理にする
頂点インデックスを使う意味
通常メッシュデータは、頂点データとは別に、頂点インデックスデータも持ちます。
頂点インデックスデータは、頂点を使う組み合わせを頂点データのインデックスで指定したデータ、です。
実際の例を見たほうがわかりやすいので、以下の図の例で見てみましょう。
この図では、①と②の三角形2つを描画しています。
このとき、①はA,B,Cの頂点データ、②はB,C,Dの頂点データを使っています。
このとき、頂点データリストのみで描画しようとすると、頂点データ配列としては
[A, B, C, B, C, D]
という配列が必要になりますね。
こうなると、全く同じB,Cの頂点データが、重複して利用されることになります。
重複すると、重複した分頂点データ用のメモリ(頂点バッファなど)が大きくなりますし、頂点シェーダの計算も同じ計算を複数行うことになります。
こういった2つの三角形のみなら、大した問題ではないですが、複雑なメッシュになるとこのコストは非常に無駄なコストとなります。
そこで、頂点データとは別に、頂点インデックスデータを持たせて解決します。
この例なら、
- 頂点データは、[A, B, C, D]の4つのみ
- インデックスは前から順に0, 1, 2, 3
- 頂点インデックスデータは、[0, 1, 2, 1, 2, 3]
とすることで、頂点データは4つで済ませるということになります。
頂点データ・頂点インデックスデータの解釈
ということで、改めてMeshの処理の中の、頂点データ、頂点インデックスデータを取得しているところが以下となります。
aiMeshから頂点データ(mVertices)の取得、頂点インデックスデータ(mFaces の mIndices)の取得、をします。
bool Mesh::Setup(Renderer& renderer, aiMesh* pMeshData)
{
// 頂点データ取得
vertexNum_ = pMeshData->mNumVertices;
vertices_ = new Vertex[vertexNum_];
for (u_int vertexIdx = 0; vertexIdx < vertexNum_; ++vertexIdx) {
auto& pos = pMeshData->mVertices[vertexIdx];
vertices_[vertexIdx].Position = XMFLOAT3(pos.x, pos.y, pos.z);
constexpr float COLOR = 0.5f;
vertices_[vertexIdx].Color = XMFLOAT4(COLOR, COLOR, COLOR, 1.f);
}
if (createVertexBuffer(renderer) == false) {
return false;
}
// 頂点インデックスデータ取得(TriangleList前提)
indexNum_ = pMeshData->mNumFaces * 3;
indices_ = new u_int[indexNum_];
for (u_int faceIdx = 0; faceIdx < pMeshData->mNumFaces; ++faceIdx) {
auto& face = pMeshData->mFaces[faceIdx];
assert(face.mNumIndices == 3);
for (u_int idx = 0; idx < 3; ++idx) {
indices_[faceIdx * 3 + idx] = face.mIndices[idx];
}
}
if (createIndexBuffer(renderer) == false) {
return false;
}
return true;
}
インデックスバッファを使った描画処理で描画
DirectX描画処理をIndexBufferを使った形式で、リソースバッファ作成、描画していきます。
VertexBuffer、IndexBufferの作成
インデックスバッファを使って描画するので、これまではVertexBuffer作成のみでしたが、加えて同様にIndexBufferを作成します。
インデックスバッファは、ただのunsigned intの配列なので、4Byteのindex数分のサイズでリソース確保します。
BindFlagsだけ D3D11_BIND_INDEX_BUFFER
にするのを忘れないようにしましょう。
頂点バッファ作成時よりも単純な処理ですね。
bool Mesh::createVertexBuffer(Renderer& renderer)
{
D3D11_BUFFER_DESC vertexBufferDesc = {};
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(Vertex) * vertexNum_;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA vertexSubData;
vertexSubData.pSysMem = vertices_;
auto hr = renderer.GetDevice()->CreateBuffer(
&vertexBufferDesc,
&vertexSubData,
&vertexBuffer_
);
if (FAILED(hr)) return false;
return true;
}
bool Mesh::createIndexBuffer(Renderer& renderer)
{
D3D11_BUFFER_DESC indexBufferDesc = {};
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = indexNum_ * 4;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
D3D11_SUBRESOURCE_DATA indexSubData;
indexSubData.pSysMem = indices_;
auto hr = renderer.GetDevice()->CreateBuffer(
&indexBufferDesc,
&indexSubData,
&indexBuffer_
);
if (FAILED(hr)) return false;
return true;
}
IndexBufferを設定した描画
あとは今までのVertexBufferのみでの描画処理に
- IASetIndexBuffer() でインデックスバッファをセット
- 描画は DrawIndexed() をコール
とするだけです。
void Mesh::Draw(Renderer& renderer)
{
auto pDeviceContext = renderer.GetDeviceContext();
size_t strides[1] = { sizeof(Vertex) };
size_t offsets[1] = { 0 };
pDeviceContext->IASetVertexBuffers(0, 1, &vertexBuffer_, strides, offsets);
pDeviceContext->IASetIndexBuffer(indexBuffer_, DXGI_FORMAT_R32_UINT, 0);
pDeviceContext->DrawIndexed(indexNum_, 0, 0);
}
その他の変更点
今回は、Modelクラスの作成がほぼすべてで、あとは使うだけです。以下のようにSceneManagerで使いましょう。
また、ソースコード全体の量も多くなってきたので、細かい点すべてを挙げることは難しいです。
記事末にはこのプロジェクトの全ソースコードがあるので、うまくいかない場合などはそちらと見比べる、Diffをとるなどしてみましょう。
SceneManagerで使うモデルをSetup
ModelクラスでModelデータを扱えるようになっているので、あとは実際に使うモデルデータのパスを指定して、ModelのSetup、描画を呼んであげればよいです。
SceneManagerに以下のようにmodelの処理を追加しましょう。また、前回までのsampleTriangleの処理はもう使わないので消してしまいます。
モデルのパスは、今回はわかりやすい&間違いないということで絶対パスで指定していますので、こちらは自分の環境に合わせて変えましょう。
void SceneManager::Initialize(Renderer& renderer)
{
...
const char* MODEL_PATH = "C:/Users/Keita/projects/GameDev/Assets/box.fbx";
sampleModel_.Setup(renderer, MODEL_PATH);
}
void SceneManager::Terminate()
{
sampleModel_.Terminate();
}
void SceneManager::Draw()
{
...
sampleModel_.Draw(*pRenderer_);
}
ボックスの描画結果
前回同様にCameraを上に移動だけさせて、Boxを描画した結果が以下となります。
ソースコード
以下が今回の実装があるコードです。
GameDev/06-DrawModel at master · prog-log/GameDev
Contribute to prog-log/GameDev development by creating an account on GitHub.
#pragma once
#include "Window.h"
#include "Renderer.h"
#include "SceneManager.h"
class Application
{
public:
void Initialize(HINSTANCE hInst);
void Loop();
void Terminate();
private:
// アプリのメインループ
bool gameLoop();
private:
Window window_;
Renderer renderer_;
SceneManager sceneManager_;
};
#pragma once
class Camera
{
public:
Camera();
~Camera();
void Update();
XMMATRIX GetViewMatrix() const;
private:
XMFLOAT3 eyePos_ = { 0, 0, 0};
XMFLOAT3 focutPos_ = { 0, 0, 0 };
XMFLOAT3 up_ = { 0, 1.f, 0 };
};
#pragma once
/*
* ワールド変換コンスタントバッファ
*/
struct CbTransform
{
XMFLOAT4X4 Transform;
};
struct CbTransformSet
{
CbTransform Data;
ID3D11Buffer* pBuffer = nullptr;
};
/*
* View変換用コンスタントバッファ
*/
struct CbView
{
XMFLOAT4X4 View;
};
struct CbViewSet
{
CbView Data;
ID3D11Buffer* pBuffer = nullptr;
};
/*
* 投影行列用コンスタントバッファ
*/
struct CbProjection
{
XMFLOAT4X4 Projection;
};
struct CbProjectionSet
{
CbProjection Data;
ID3D11Buffer* pBuffer = nullptr;
};
struct PS_INPUT {
float4 Pos : SV_POSITION; // 頂点座標(モデル座標系)
float4 Col : COLOR; // 頂点色
};
float4 main(PS_INPUT input) : SV_TARGET
{
return input.Col;
}
#pragma once
#include "ConstantBuffer.h"
/*
* 描画パラメータ
* コンスタントバッファの値などを保持
*/
struct RenderParam
{
CbTransformSet CbTransformSet;
CbViewSet CbViewSet;
CbProjectionSet CbProjectionSet;
bool Initialize(Renderer& renderer);
void Terminate(Renderer& renderer);
private:
bool initConstantBuffer(Renderer& renderer);
};
#pragma once
#include "Shader.h"
#include "RenderParam.h"
class Renderer
{
public:
Renderer();
~Renderer();
bool Initialize(HWND hWindow);
void Terminate();
void Draw();
void Swap();
ID3D11Device* GetDevice() { return pD3DDevice_; }
ID3D11DeviceContext* GetDeviceContext() { return pImmediateContext_; }
RenderParam& GetRenderParam() { return renderParam_; }
bool CompileShader(const WCHAR* vsPath, const WCHAR* psPath, Shader& outShader);
bool SetupViewTransform(const XMMATRIX& viewMat);
private:
bool initDeviceAndSwapChain(HWND hWindow);
bool initBackBuffer();
bool setupProjectionTransform();
private:
//! 機能レベルの配列
static const UINT FEATURE_LEVELS_NUM = 4;
D3D_FEATURE_LEVEL pFeatureLevels_[FEATURE_LEVELS_NUM] = {};
//! デバイス作成時に返される機能レベル
D3D_FEATURE_LEVEL featureLevelsSupported_;
//! デバイス
ID3D11Device* pD3DDevice_ = nullptr;
//! デバイスコンテキスト
ID3D11DeviceContext* pImmediateContext_ = nullptr;
//! スワップ・チェイン
IDXGISwapChain* pSwapChain_ = nullptr;
//! 描画ターゲット・ビュー
ID3D11RenderTargetView* pRenderTargetView_ = nullptr;
//! ビューポート
D3D11_VIEWPORT viewPort_[1];
//! ブレンド・ステート・オブジェクト
ID3D11BlendState* pBlendState_ = nullptr;
UINT backBufferNum_ = 3;
UINT screenWidth_ = 0;
UINT screenHeight_ = 0;
float nearClipDist_ = 0.f;
float farClipDist_ = 0.f;
float fov_ = 0.f;
Shader defaultShader_;
RenderParam renderParam_;
};
#pragma once
struct Shader
{
Shader() {}
~Shader() {}
void Terminate() {
DX_SAFE_RELEASE(pVertexShader);
DX_SAFE_RELEASE(pPixelShader);
DX_SAFE_RELEASE(pInputLayout);
}
ID3D11VertexShader* pVertexShader = nullptr;
ID3D11PixelShader* pPixelShader = nullptr;
ID3D11InputLayout* pInputLayout = nullptr;
};
#pragma once
struct Triangle
{
static constexpr size_t VERTEX_NUM = 3;
Vertex Vertices[VERTEX_NUM];
ID3D11Buffer* VertexBuffer = nullptr;
Triangle();
~Triangle();
bool CreateVertexBuffer(Renderer& renderer);
void DestroyVertexBuffer();
void Draw(Renderer& renderer);
private:
void updateTransform();
void setupTransform(Renderer& renderer);
private:
float translateX_ = 0.f;
float translateY_ = 0.f;
float angle_ = 0.f;
float scale_ = 1.f;
};
#pragma once
struct Vertex
{
DirectX::XMFLOAT3 Position = {};
DirectX::XMFLOAT4 Color = {};
Vertex() {}
Vertex(float x, float y, float z) {
Position.x = x;
Position.y = y;
Position.z = z;
}
};
#include
#include "Application.h"
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, int)
{
Application* application = new Application();
application->Initialize(hInst);
application->Loop();
application->Terminate();
delete(application);
return 0;
}
#include "Application.h"
void Application::Initialize(HINSTANCE hInst)
{
window_.Initialize(hInst);
renderer_.Initialize(window_.GetWindowHandle());
sceneManager_.Initialize(renderer_);
}
void Application::Loop()
{
while (true) {
bool result = window_.MessageLoop();
if (result == false) break;
bool ret = gameLoop();
if (ret == false) break;
}
}
void Application::Terminate()
{
sceneManager_.Terminate();
renderer_.Terminate();
window_.Terminate();
}
bool Application::gameLoop()
{
sceneManager_.Update();
renderer_.Draw();
sceneManager_.Draw();
renderer_.Swap();
Sleep(10);
return true;
}
#include "Camera.h"
Camera::Camera()
: eyePos_(0, 0, -5)
, focutPos_(0, 0, 0)
{
}
Camera::~Camera()
{
}
void Camera::Update()
{
//eyePos_.x += 0.0001f;
//if (eyePos_.x > 5.f) {
// eyePos_.x = -5.f;
//}
eyePos_.y += 0.03f;
if (eyePos_.y > 10.f) {
eyePos_.y = 0.f;
}
//eyePos_.z -= 0.0001f;
//if (eyePos_.z < -10.f) {
// eyePos_.z = -5.f;
//}
}
/*
* View変換行列取得
*/
DirectX::XMMATRIX Camera::GetViewMatrix() const
{
// ビュー変換行列を求める
XMMATRIX viewMat = XMMatrixLookAtLH(
XMLoadFloat3(&eyePos_), XMLoadFloat3(&focutPos_), XMLoadFloat3(&up_));
return viewMat;
}
#include "Mesh.h"
#include "Renderer.h"
#include
Mesh::Mesh()
{
}
Mesh::~Mesh()
{
Terminate();
}
bool Mesh::Setup(Renderer& renderer, aiMesh* pMeshData)
{
// 頂点データ取得
vertexNum_ = pMeshData->mNumVertices;
vertices_ = new Vertex[vertexNum_];
for (u_int vertexIdx = 0; vertexIdx < vertexNum_; ++vertexIdx) {
auto& pos = pMeshData->mVertices[vertexIdx];
vertices_[vertexIdx].Position = XMFLOAT3(pos.x, pos.y, pos.z);
constexpr float COLOR = 0.5f;
vertices_[vertexIdx].Color = XMFLOAT4(COLOR, COLOR, COLOR, 1.f);
}
if (createVertexBuffer(renderer) == false) {
return false;
}
// 頂点インデックスデータ取得(TriangleList前提)
indexNum_ = pMeshData->mNumFaces * 3;
indices_ = new u_int[indexNum_];
for (u_int faceIdx = 0; faceIdx < pMeshData->mNumFaces; ++faceIdx) {
auto& face = pMeshData->mFaces[faceIdx];
assert(face.mNumIndices == 3);
for (u_int idx = 0; idx < 3; ++idx) {
indices_[faceIdx * 3 + idx] = face.mIndices[idx];
}
}
if (createIndexBuffer(renderer) == false) {
return false;
}
return true;
}
void Mesh::Terminate()
{
destroyVertexBuffer();
destroyIndexBuffer();
SAFE_DELETE_ARRAY(vertices_);
vertexNum_ = 0;
SAFE_DELETE_ARRAY(indices_);
indexNum_ = 0;
}
bool Mesh::createVertexBuffer(Renderer& renderer)
{
D3D11_BUFFER_DESC vertexBufferDesc = {};
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(Vertex) * vertexNum_;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA vertexSubData;
vertexSubData.pSysMem = vertices_;
auto hr = renderer.GetDevice()->CreateBuffer(
&vertexBufferDesc,
&vertexSubData,
&vertexBuffer_
);
if (FAILED(hr)) return false;
return true;
}
bool Mesh::createIndexBuffer(Renderer& renderer)
{
D3D11_BUFFER_DESC indexBufferDesc = {};
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = indexNum_ * 4;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
D3D11_SUBRESOURCE_DATA indexSubData;
indexSubData.pSysMem = indices_;
auto hr = renderer.GetDevice()->CreateBuffer(
&indexBufferDesc,
&indexSubData,
&indexBuffer_
);
if (FAILED(hr)) return false;
return true;
}
void Mesh::destroyVertexBuffer()
{
DX_SAFE_RELEASE(vertexBuffer_);
}
void Mesh::destroyIndexBuffer()
{
DX_SAFE_RELEASE(indexBuffer_);
}
void Mesh::Draw(Renderer& renderer)
{
//setupTransform(renderer);
auto pDeviceContext = renderer.GetDeviceContext();
UINT strides[1] = { sizeof(Vertex) };
UINT offsets[1] = { 0 };
pDeviceContext->IASetVertexBuffers(0, 1, &vertexBuffer_, strides, offsets);
pDeviceContext->IASetIndexBuffer(indexBuffer_, DXGI_FORMAT_R32_UINT, 0);
pDeviceContext->DrawIndexed(indexNum_, 0, 0);
}
#pragma once
struct aiMesh;
class Mesh
{
public:
Mesh();
~Mesh();
bool Setup(Renderer& renderer, aiMesh* pMeshData);
void Terminate();
void Draw(Renderer& renderer);
private:
bool createVertexBuffer(Renderer& renderer);
bool createIndexBuffer(Renderer& renderer);
void destroyVertexBuffer();
void destroyIndexBuffer();
private:
// データの解釈ワーク
Vertex* vertices_ = nullptr;
u_int* indices_ = nullptr;
u_int vertexNum_ = 0;
u_int indexNum_ = 0;
// バッファリソース
ID3D11Buffer* vertexBuffer_ = nullptr;
ID3D11Buffer* indexBuffer_ = nullptr;
};
#include "Model.h"
#include "Mesh.h"
#include "Renderer.h"
#include
#include
#include
Model::Model()
{
}
Model::~Model()
{
SAFE_DELETE_ARRAY(meshes_);
}
bool Model::Setup(Renderer& renderer, const char* filePath)
{
// load処理
Assimp::Importer importer;
u_int flag = aiProcess_Triangulate;
auto pScene = importer.ReadFile(filePath, flag);
if (pScene == nullptr) {
auto error = importer.GetErrorString();
return false;
}
meshNum_ = pScene->mNumMeshes;
if (meshNum_ > 0) {
meshes_ = new Mesh[meshNum_];
for (u_int meshIdx = 0; meshIdx < pScene->mNumMeshes; ++meshIdx) {
auto pMeshData = pScene->mMeshes[meshIdx];
if (meshes_[meshIdx].Setup(renderer, pMeshData) == false) {
return false;
}
}
}
return true;
}
void Model::Terminate()
{
for (u_int meshIdx = 0; meshIdx < meshNum_; ++meshIdx) {
meshes_[meshIdx].Terminate();
}
SAFE_DELETE_ARRAY(meshes_);
meshNum_ = 0;
}
void Model::Draw(Renderer& renderer)
{
setupTransform(renderer);
for (u_int meshIdx = 0; meshIdx < meshNum_; ++meshIdx) {
meshes_[meshIdx].Draw(renderer);
}
}
void Model::setupTransform(Renderer& renderer)
{
// WorldMatrixを定数バッファに設定
auto& cb = renderer.GetRenderParam().CbTransformSet;
//auto mtxS = XMMatrixScaling(scale_, scale_, 1.f);
//auto mtxR = XMMatrixRotationX(angle_);
//auto mtxR = XMMatrixRotationY(angle_);
//auto mtxR = XMMatrixRotationZ(angle_);
//auto mtxT = XMMatrixTranslation(translateX_, translateY_, 0);
auto mtx = XMMatrixIdentity();
// 左から変換が適用される
//auto mtx = mtxT * mtxS;
//auto mtx = mtxS * mtxT;
//auto mtx = mtxT * mtxR;
//auto mtx = mtxR * mtxT;
XMStoreFloat4x4(&cb.Data.Transform, XMMatrixTranspose(mtx));
//DirectX::XMStoreFloat4x4(&cb.Data.Transform, translate);
D3D11_MAPPED_SUBRESOURCE mappedResource;
auto pDeviceContext = renderer.GetDeviceContext();
// CBufferにひもづくハードウェアリソースマップ取得(ロックして取得)
HRESULT hr = pDeviceContext->Map(
cb.pBuffer,
0,
D3D11_MAP_WRITE_DISCARD,
0,
&mappedResource);
if (FAILED(hr)) {
//DXTRACE_ERR(L"DrawSceneGraph failed", hr);
return;
}
CopyMemory(mappedResource.pData, &cb.Data, sizeof(cb.Data));
// マップ解除
pDeviceContext->Unmap(cb.pBuffer, 0);
pDeviceContext->VSSetConstantBuffers(0, 1, &cb.pBuffer);
}
#pragma once
class Mesh;
class Model
{
public:
Model();
~Model();
bool Setup(Renderer& renderer, const char* filePath);
void Terminate();
void Draw(Renderer& renderer);
private:
void setupTransform(Renderer& renderer);
private:
Mesh* meshes_ = nullptr;
u_int meshNum_ = 0;
};
#include "RenderParam.h"
#include "Renderer.h"
bool RenderParam::Initialize(Renderer& renderer)
{
auto result = initConstantBuffer(renderer);
if (result == false) return false;
return true;
}
void RenderParam::Terminate(Renderer& renderer)
{
DX_SAFE_RELEASE(CbTransformSet.pBuffer);
DX_SAFE_RELEASE(CbViewSet.pBuffer);
DX_SAFE_RELEASE(CbProjectionSet.pBuffer);
}
bool RenderParam::initConstantBuffer(Renderer& renderer)
{
// 定数バッファの定義
D3D11_BUFFER_DESC cBufferDesc;
cBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
cBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
cBufferDesc.MiscFlags = 0;
cBufferDesc.StructureByteStride = 0;
// Transform(ワールド変換)用定数バッファの作成
cBufferDesc.ByteWidth = sizeof(CbTransform); // バッファ・サイズ
auto hr = renderer.GetDevice()->CreateBuffer(&cBufferDesc, nullptr, &CbTransformSet.pBuffer);
if (FAILED(hr)) {
// DXTRACE_ERR(L"InitDirect3D g_pD3DDevice->CreateBuffer", hr);
return false;
}
// View変換用定数バッファの作成
cBufferDesc.ByteWidth = sizeof(CbView); // バッファ・サイズ
hr = renderer.GetDevice()->CreateBuffer(&cBufferDesc, nullptr, &CbViewSet.pBuffer);
if (FAILED(hr)) {
// DXTRACE_ERR(L"InitDirect3D g_pD3DDevice->CreateBuffer", hr);
return false;
}
// 投影変換用定数バッファの作成
cBufferDesc.ByteWidth = sizeof(CbProjection); // バッファ・サイズ
hr = renderer.GetDevice()->CreateBuffer(&cBufferDesc, nullptr, &CbProjectionSet.pBuffer);
if (FAILED(hr)) {
// DXTRACE_ERR(L"InitDirect3D g_pD3DDevice->CreateBuffer", hr);
return false;
}
return true;
}
#include "Renderer.h"
#include
#pragma comment(lib, "d3dcompiler.lib")
Renderer::Renderer()
: nearClipDist_(0.1f)
, farClipDist_(1000.f)
, fov_(XMConvertToRadians(30.f))
{
pFeatureLevels_[0] = D3D_FEATURE_LEVEL_11_1;
pFeatureLevels_[1] = D3D_FEATURE_LEVEL_11_0;
pFeatureLevels_[2] = D3D_FEATURE_LEVEL_10_1;
pFeatureLevels_[3] = D3D_FEATURE_LEVEL_10_0;
}
Renderer::~Renderer()
{
}
bool Renderer::Initialize(HWND hWindow)
{
// Windowに合わせてスクリーンサイズ初期化
RECT rc;
GetClientRect(hWindow, &rc);
screenWidth_ = rc.right - rc.left;
screenHeight_ = rc.bottom - rc.top;
initDeviceAndSwapChain(hWindow);
initBackBuffer();
CompileShader(L"VertexShader.hlsl", L"PixelShader.hlsl", defaultShader_);
renderParam_.Initialize(*this);
setupProjectionTransform();
return true;
}
void Renderer::Terminate()
{
renderParam_.Terminate(*this);
// デバイス・ステートのクリア
if(pImmediateContext_)
pImmediateContext_->ClearState();
// スワップ チェインをウインドウ モードにする
if (pSwapChain_)
pSwapChain_->SetFullscreenState(FALSE, nullptr);
DX_SAFE_RELEASE(pRenderTargetView_);
DX_SAFE_RELEASE(pBlendState_);
DX_SAFE_RELEASE(pSwapChain_);
DX_SAFE_RELEASE(pImmediateContext_);
DX_SAFE_RELEASE(pD3DDevice_);
defaultShader_.Terminate();
}
void Renderer::Draw()
{
if (!pImmediateContext_ || !pRenderTargetView_) return;
pImmediateContext_->OMSetRenderTargets(1, &pRenderTargetView_, nullptr);
// OMにブレンドステートオブジェクトを設定
FLOAT BlendFactor[4] = { 0.f, 0.f, 0.f, 0.f };
pImmediateContext_->OMSetBlendState(pBlendState_, nullptr, 1);
float color[] = { 0.f, 0.f, 0.f, 0.f };
pImmediateContext_->ClearRenderTargetView(pRenderTargetView_, color);
pImmediateContext_->IASetInputLayout(defaultShader_.pInputLayout);
pImmediateContext_->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
pImmediateContext_->VSSetShader(defaultShader_.pVertexShader, nullptr, 0);
pImmediateContext_->PSSetShader(defaultShader_.pPixelShader, nullptr, 0);
}
void Renderer::Swap()
{
// バックバッファの表示(画面をすぐに更新)
HRESULT hr = pSwapChain_->Present(0, 0);
if (FAILED(hr)) {
//TRACE("Swap failed(%0x08x)\n", hr);
return;
}
}
bool Renderer::CompileShader(const WCHAR* vsPath, const WCHAR* psPath, Shader& outShader)
{
ID3DBlob* vsBlob = nullptr;
ID3DBlob* errBlob = nullptr;
auto pDevice = GetDevice();
// シェーダーコンパイル
auto hr = D3DCompileFromFile(
vsPath,
nullptr,
D3D_COMPILE_STANDARD_FILE_INCLUDE,
"main",
"vs_4_0",
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
0,
&vsBlob,
&errBlob
);
if (FAILED(hr)) return false;
// 頂点シェーダ作成(シェーダオブジェクト作成)
ID3D11VertexShader* pVertexShader = nullptr;
hr = pDevice->CreateVertexShader(
vsBlob->GetBufferPointer(),
vsBlob->GetBufferSize(),
nullptr,
&pVertexShader
);
if (FAILED(hr)) return false;
// 入力レイアウトオブジェクト作成
ID3D11InputLayout* pInputLayout = nullptr;
D3D11_INPUT_ELEMENT_DESC layout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
hr = pDevice->CreateInputLayout(
layout,
_countof(layout),
vsBlob->GetBufferPointer(),
vsBlob->GetBufferSize(),
&pInputLayout
);
if (FAILED(hr)) return false;
// ピクセルシェーダー作成
ID3DBlob* psBlob = nullptr;
hr = D3DCompileFromFile(
psPath,
nullptr,
D3D_COMPILE_STANDARD_FILE_INCLUDE,
"main",
"ps_4_0",
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
0,
&psBlob,
&errBlob
);
ID3D11PixelShader* pPixelShader = nullptr;
hr = pDevice->CreatePixelShader(
psBlob->GetBufferPointer(),
psBlob->GetBufferSize(),
nullptr,
&pPixelShader
);
if (FAILED(hr)) return false;
outShader.pVertexShader = pVertexShader;
outShader.pPixelShader = pPixelShader;
outShader.pInputLayout = pInputLayout;
return true;
}
bool Renderer::SetupViewTransform(const XMMATRIX& viewMat)
{
auto& cb = GetRenderParam().CbViewSet;
XMStoreFloat4x4(&cb.Data.View, XMMatrixTranspose(viewMat));
D3D11_MAPPED_SUBRESOURCE mappedResource;
auto pDeviceContext = GetDeviceContext();
// CBufferにひもづくハードウェアリソースマップ取得(ロックして取得)
HRESULT hr = pDeviceContext->Map(
cb.pBuffer,
0,
D3D11_MAP_WRITE_DISCARD,
0,
&mappedResource);
if (FAILED(hr)) {
//DXTRACE_ERR(L"DrawSceneGraph failed", hr);
return false;
}
CopyMemory(mappedResource.pData, &cb.Data, sizeof(cb.Data));
// マップ解除
pDeviceContext->Unmap(cb.pBuffer, 0);
// VSにViewMatrixをセット
pDeviceContext->VSSetConstantBuffers(1, 1, &cb.pBuffer);
return true;
}
bool Renderer::initDeviceAndSwapChain(HWND hWindow)
{
// デバイスとスワップ・チェイン作成
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = backBufferNum_; // バックバッファの数
sd.BufferDesc.Width = screenWidth_; // バックバッファの幅
sd.BufferDesc.Height = screenHeight_; // バックバッファの高さ
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // フォーマット
sd.BufferDesc.RefreshRate.Numerator = 60; // リフレッシュレート(分母)
sd.BufferDesc.RefreshRate.Denominator = 1; // リフレッシュレート(分子)
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_CENTERED;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // バックバッファの使用法
sd.OutputWindow = hWindow; // 関連付けるウィンドウ
sd.SampleDesc.Count = 1; // マルチサンプル(アンチエイリアス)の数
sd.SampleDesc.Quality = 0; // マルチサンプル(アンチエイリアス)のクオリティ
sd.Windowed = TRUE; // ウィンドウモード(TRUEがウィンドウモード)
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // モード自動切り替え
#if defined(DEBUG) || defined(_DEBUG)
UINT createDeviceFlags = D3D11_CREATE_DEVICE_DEBUG;
#else
UINT createDeviceFlags = 0;
#endif
const D3D_DRIVER_TYPE DriverTypes[] = {
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
HRESULT hr;
for (auto type : DriverTypes) {
// ハードウェアデバイスを作成
hr = D3D11CreateDeviceAndSwapChain(
nullptr, type, nullptr, createDeviceFlags,
pFeatureLevels_, FEATURE_LEVELS_NUM, D3D11_SDK_VERSION, &sd,
&pSwapChain_, &pD3DDevice_, &featureLevelsSupported_, &pImmediateContext_);
if (SUCCEEDED(hr)) {
break;
}
}
if (FAILED(hr)) return false;
return true;
}
bool Renderer::initBackBuffer()
{
HRESULT hr;
// スワップ・チェインから最初のバック・バッファを取得する
ID3D11Texture2D *pBackBuffer; // バッファのアクセスに使うインターフェイス
hr = pSwapChain_->GetBuffer(
0, // バック・バッファの番号
__uuidof(ID3D11Texture2D), // バッファにアクセスするインターフェイス
(LPVOID*)&pBackBuffer); // バッファを受け取る変数
if (FAILED(hr)) {
//TRACE("InitBackBuffer g_pSwapChain->GetBuffer(%0x08x)\n", hr); // 失敗
return false;
}
// バック・バッファの情報
D3D11_TEXTURE2D_DESC descBackBuffer;
pBackBuffer->GetDesc(&descBackBuffer);
// バック・バッファの描画ターゲット・ビューを作る
hr = pD3DDevice_->CreateRenderTargetView(
pBackBuffer, // ビューでアクセスするリソース
nullptr, // 描画ターゲット・ビューの定義
&pRenderTargetView_); // 描画ターゲット・ビューを受け取る変数
DX_SAFE_RELEASE(pBackBuffer); // 以降、バック・バッファは直接使わないので解放
if (FAILED(hr)) {
//TRACE("InitBackBuffer g_pD3DDevice->CreateRenderTargetView(%0x08x)\n", hr); // 失敗
return false;
}
// RenderTarget0へのAlphaブレンド描画設定
D3D11_BLEND_DESC BlendState;
//ZeroMemory(&BlendState, sizeof(D3D11_BLEND_DESC));
BlendState.AlphaToCoverageEnable = FALSE;
BlendState.IndependentBlendEnable = FALSE;
BlendState.RenderTarget[0].BlendEnable = TRUE;
BlendState.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
BlendState.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
BlendState.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
BlendState.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
BlendState.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
BlendState.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
BlendState.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
hr = pD3DDevice_->CreateBlendState(&BlendState, &pBlendState_);
if (FAILED(hr)) {
//TRACE(L"InitDirect3D g_pD3DDevice->CreateBlendState", hr);
return false;
}
// ビューポートの設定
viewPort_[0].TopLeftX = 0.0f; // ビューポート領域の左上X座標。
viewPort_[0].TopLeftY = 0.0f; // ビューポート領域の左上Y座標。
viewPort_[0].Width = static_cast(screenWidth_); // ビューポート領域の幅
viewPort_[0].Height = static_cast(screenHeight_); // ビューポート領域の高さ
viewPort_[0].MinDepth = 0.0f; // ビューポート領域の深度値の最小値
viewPort_[0].MaxDepth = 1.0f; // ビューポート領域の深度値の最大値
pImmediateContext_->RSSetViewports(1, &viewPort_[0]);
return true;
}
/*
* 透視投影行列を設定
*/
bool Renderer::setupProjectionTransform()
{
XMMATRIX mat = XMMatrixPerspectiveFovLH(
fov_,
static_cast(screenWidth_) / static_cast(screenHeight_), // アスペクト比
nearClipDist_,
farClipDist_);
mat = XMMatrixTranspose(mat);
auto& cb = GetRenderParam().CbProjectionSet;
XMStoreFloat4x4(&cb.Data.Projection, mat);
D3D11_MAPPED_SUBRESOURCE mappedResource;
auto pDeviceContext = GetDeviceContext();
// CBufferにひもづくハードウェアリソースマップ取得(ロックして取得)
HRESULT hr = pDeviceContext->Map(
cb.pBuffer,
0,
D3D11_MAP_WRITE_DISCARD,
0,
&mappedResource);
if (FAILED(hr)) {
//DXTRACE_ERR(L"DrawSceneGraph failed", hr);
return false;
}
CopyMemory(mappedResource.pData, &cb.Data, sizeof(cb.Data));
// マップ解除
pDeviceContext->Unmap(cb.pBuffer, 0);
// VSにProjectionMatrixをセット(ここで1度セットして以後不変)
pDeviceContext->VSSetConstantBuffers(2, 1, &cb.pBuffer);
return true;
}
#include "SceneManager.h"
#include "Renderer.h"
SceneManager::SceneManager()
{
}
SceneManager::~SceneManager()
{
}
void SceneManager::Initialize(Renderer& renderer)
{
pRenderer_ = &renderer;
const char* MODEL_PATH = "C:/Users/Keita/projects/GameDev/Assets/box.fbx";
sampleModel_.Setup(renderer, MODEL_PATH);
}
void SceneManager::Terminate()
{
sampleModel_.Terminate();
}
void SceneManager::Update()
{
camera_.Update();
}
void SceneManager::Draw()
{
auto viewMatrix = camera_.GetViewMatrix();
pRenderer_->SetupViewTransform(viewMatrix);
sampleModel_.Draw(*pRenderer_);
}
#pragma once
#include "Camera.h"
#include "Model.h"
class SceneManager
{
public:
SceneManager();
~SceneManager();
void Initialize(Renderer& renderer);
void Terminate();
void Update();
void Draw();
private:
Renderer* pRenderer_ = nullptr;
Camera camera_;
Model sampleModel_;
};
#include "Triangle.h"
#include "Renderer.h"
Triangle::Triangle()
{
// √3
auto sqrt3 = sqrtf(3);
Vertices[0] = { 0.f, sqrt3 / 3.f, 0.f };
Vertices[1] = { 0.5f, -sqrt3 / 6.f, 0.f };
Vertices[2] = { -0.5f, -sqrt3 / 6.f, 0.f };
constexpr float ALPHA = 1.f;
Vertices[0].Color = { 1.f, 0.f, 0.f, ALPHA };
Vertices[1].Color = { 0.f, 1.f, 0.f, ALPHA };
Vertices[2].Color = { 0.f, 0.f, 1.f, ALPHA };
}
Triangle::~Triangle()
{
DestroyVertexBuffer();
}
bool Triangle::CreateVertexBuffer(Renderer& renderer)
{
D3D11_BUFFER_DESC vertexBufferDesc = {};
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(Vertex) * 3;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA vertexSubData;
vertexSubData.pSysMem = Vertices;
auto hr = renderer.GetDevice()->CreateBuffer(
&vertexBufferDesc,
&vertexSubData,
&VertexBuffer
);
if (FAILED(hr)) return false;
return true;
}
void Triangle::DestroyVertexBuffer()
{
DX_SAFE_RELEASE(VertexBuffer);
}
void Triangle::Draw(Renderer& renderer)
{
updateTransform();
setupTransform(renderer);
auto pDeviceContext = renderer.GetDeviceContext();
UINT strides[1] = { sizeof(Vertex) };
UINT offsets[1] = { 0 };
pDeviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, strides, offsets);
pDeviceContext->Draw(VERTEX_NUM, 0);
}
void Triangle::updateTransform()
{
// 平行移動
//translateX_ += 0.0001f;
//if (translateX_ >= 1.f) translateX_ = -1.f;
//translateY_ += 0.0001f;
//if (translateY_ >= 1.f) translateY_ = -1.f;
translateX_ = 0.5f;
// 回転
//angle_ += XM_PI / 60000.f;
angle_ = XM_PI / 2.f;
// スケール
//scale_ += 0.0001f;
//if (scale_ >= 2.0f) scale_ = 0.5f;
scale_ = 2.f;
}
void Triangle::setupTransform(Renderer& renderer)
{
// WorldMatrixを定数バッファに設定
auto& cb = renderer.GetRenderParam().CbTransformSet;
//auto mtxS = XMMatrixScaling(scale_, scale_, 1.f);
//auto mtxR = XMMatrixRotationX(angle_);
//auto mtxR = XMMatrixRotationY(angle_);
//auto mtxR = XMMatrixRotationZ(angle_);
//auto mtxT = XMMatrixTranslation(translateX_, translateY_, 0);
auto mtx = XMMatrixIdentity();
// 左から変換が適用される
//auto mtx = mtxT * mtxS;
//auto mtx = mtxS * mtxT;
//auto mtx = mtxT * mtxR;
//auto mtx = mtxR * mtxT;
XMStoreFloat4x4(&cb.Data.Transform, XMMatrixTranspose(mtx));
//DirectX::XMStoreFloat4x4(&cb.Data.Transform, translate);
D3D11_MAPPED_SUBRESOURCE mappedResource;
auto pDeviceContext = renderer.GetDeviceContext();
// CBufferにひもづくハードウェアリソースマップ取得(ロックして取得)
HRESULT hr = pDeviceContext->Map(
cb.pBuffer,
0,
D3D11_MAP_WRITE_DISCARD,
0,
&mappedResource);
if (FAILED(hr)) {
//DXTRACE_ERR(L"DrawSceneGraph failed", hr);
return;
}
CopyMemory(mappedResource.pData, &cb.Data, sizeof(cb.Data));
// マップ解除
pDeviceContext->Unmap(cb.pBuffer, 0);
pDeviceContext->VSSetConstantBuffers(0, 1, &cb.pBuffer);
}
#include "Window.h"
#include
Window::Window()
: sizeWindow_()
{
sizeWindow_.cx = 640;
sizeWindow_.cy = 480;
}
Window::~Window()
{
}
bool Window::Initialize(HINSTANCE hInst)
{
// ウインドウ クラスの登録
wc_.style = CS_HREDRAW | CS_VREDRAW;
wc_.lpfnWndProc = (WNDPROC)Window::MainWndProc;
wc_.cbClsExtra = 0;
wc_.cbWndExtra = 0;
wc_.hInstance = hInst;
wc_.hIcon = LoadIcon(hInst, IDI_APPLICATION);
wc_.hCursor = LoadCursor(hInst, IDC_ARROW);
wc_.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
wc_.lpszMenuName = nullptr;
wc_.lpszClassName = _T("DrawModel");
if (!RegisterClass(&wc_)) {
return false;
}
// メイン ウインドウ作成
RECT rect;
rect.top = 0;
rect.left = 0;
rect.right = sizeWindow_.cx;
rect.bottom = sizeWindow_.cy;
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
hWindow_ = CreateWindow(wc_.lpszClassName, _T("DrawModel"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
rect.right - rect.left, rect.bottom - rect.top,
nullptr, nullptr, hInst, nullptr);
if (hWindow_ == nullptr) {
return false;
}
// ウインドウ表示
ShowWindow(hWindow_, SW_SHOWNORMAL);
UpdateWindow(hWindow_);
return true;
}
bool Window::Terminate()
{
DestroyWindow(GetWindowHandle());
// ウインドウ クラスの登録解除
UnregisterClass(wc_.lpszClassName, wc_.hInstance);
hWindow_ = nullptr;
return true;
}
bool Window::MessageLoop()
{
// Windowsメッセージループ処理
if (PeekMessage(&msg_, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg_); DispatchMessage(&msg_);
}
// アプリケーションが終わるときにmessageがWM_QUITになる
if (msg_.message == WM_QUIT) {
return false;
}
return true;
}
/*static*/ LRESULT CALLBACK Window::MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_DESTROY:
// アプリ終了
PostQuitMessage(0);
return 0;
default:
break;
}
// デフォルト処理(必須)
return DefWindowProc(hWnd, msg, wParam, lParam);
}
void Window::OnResize(UINT width, UINT height)
{
sizeWindow_.cx = width;
sizeWindow_.cy = height;
}
#pragma once
#include
class Window
{
public:
Window();
virtual ~Window();
bool Initialize(HINSTANCE hInst);
bool Terminate();
bool MessageLoop();
static LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void OnResize(UINT width, UINT height);
HWND GetWindowHandle() { return hWindow_; }
private:
WNDCLASS wc_;
HWND hWindow_ = nullptr;
SIZE sizeWindow_;
MSG msg_ = {};
};
#pragma once
#include
#pragma comment(lib, "d3d11.lib")
#include
#define DX_SAFE_RELEASE(x) { if(x) { (x)->Release(); (x)=nullptr; } }
#define SAFE_DELETE(p) { if(p) { delete p; p = nullptr; }}
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] p; p = nullptr; }}
#include "Vertex.h"
using namespace DirectX;
class Renderer;
cbuffer cbTransform : register(b0) { // 常にスロット「0」を使う
matrix Transform;
};
// Slot1 View変換
cbuffer cbView : register(b1) {
matrix View;
};
// Slot2 投影変換
cbuffer cbProjection : register(b2) {
matrix Projection;
};
struct VS_INPUT {
float3 Pos : POSITION; // 頂点座標(モデル座標系)
float4 Col : COLOR; // 頂点色
};
struct VS_OUTPUT {
float4 Pos : SV_POSITION;
float4 Col : COLOR;
};
VS_OUTPUT main(VS_INPUT input)
{
VS_OUTPUT output;
// transのみ
//output.Pos = mul(input.Pos, Transform);
// ViewTrans
//float4 pos = mul(input.Pos, Transform);
//output.Pos = mul(pos, View);
// ViewProjection
float4 pos = float4(input.Pos, 1.0);
pos = mul(pos, Transform);
pos = mul(pos, View);
output.Pos = mul(pos, Projection);
output.Col = input.Col;
return output;
}
コメント