やっと画面に物を表示することができる。
とりあえず、ただの四角いポリゴンを表示してみよう。
今から作る機能は最初からクラスに分けておこう。
ポリゴンは1個だけということはないのでnamespaceではなくクラスにする。
Polygonというクラスはすでに存在してるのでQuadという名前にしよう。
とりあえず、いつもどおりこんな感じ。
<Quad.h>
#pragma once
class Quad
{
public:
Quad();
~Quad();
void Initialize();
void Draw();
void Release();
};
<Quad.cpp>
#include "Quad.h"
Quad::Quad()
{
}
Quad::~Quad()
{
}
void Quad::Initialize()
{
}
void Quad::Draw()
{
}
void Quad::Release()
{
}
DirectXでは基本的に3角形のポリゴンしか表示できない。(Mayaでもかならず三角化したよね?)
四角形を表示したいなら三角形を2つ表示しなければならない。
その為に用意するべき情報は以下の2つ。
その名の通り各頂点の情報。
頂点インプットレイアウトで「今回は位置情報だけ持たせる」と決めたのであれば、
頂点情報は「1番の頂点は(10,5,0)、2番の頂点は(20,10,5)・・・」のような情報になる。
もし頂点レイアウトで「今回は位置情報と色情報を持たせる」と決めたのであれば、
頂点情報は「1番の頂点は(10,5,0)で赤、2番の頂点は(20,10,5)で青・・・」のような情報になる。
実際は構造体の配列になる。
頂点情報は最終的に「頂点バッファ」という専用のメモリ領域に入れる。
頂点情報のなかの、何番と何番と何番のデータで一つの3角形になるかという情報。
ただのint型の配列。
最終的に「インデックスバッファ」という専用のメモリ領域に入れる。
では、頂点情報を準備しよう。
四角形を作るので、頂点は4つ必要。
#pragma once
#include "Direct3D.h"
#include <DirectXMath.h>
using namespace DirectX;
class Quad
{
void Quad::Initialize()
{
// 頂点情報
XMVECTOR vertices[] =
{
XMVectorSet(-1.0f, 1.0f, 0.0f, 0.0f), // 四角形の頂点(左上)
XMVectorSet(1.0f, 1.0f, 0.0f, 0.0f), // 四角形の頂点(右上)
XMVectorSet(1.0f, -1.0f, 0.0f, 0.0f), // 四角形の頂点(右下)
XMVectorSet(-1.0f, -1.0f, 0.0f, 0.0f), // 四角形の頂点(左下)
};
}
XMVECTOR型を使うためには<DirectXMath.h>をインクルードしなければならない。
また、本来はDirectX::XMVECTORと書かなきゃいけないのだが、面倒なのでusingを使う。
頂点の順番はどうでもいい。
次にこの情報を頂点バッファというところに置いてやる必要がある。
図にするとこんな感じかな…?
まずレンダリングパイプラインという言葉を覚えておいてほしい。
描画に関する一連の処理のことを指す言葉で、その名の通りパイプをイメージすればよい。
途中に穴がいくつかあって、そこから必要な情報を入れてやると終端から最終的なレンダリング画像が出てくるイメージ。
で、頂点情報を置く場所を用意してやって、そこにデータを置けばパイプラインに流れていくようにしてやる。
この場所が頂点バッファ。
後ほど、インデックス情報も同様にバッファを用意して、そこに置くことになる。
※他にも必要な情報はある。
ということで、頂点バッファを準備する。
まずは変数を用意。
#pragma once
#include "Direct3D.h"
class Quad
{
ID3D11Buffer *pVertexBuffer_; //頂点バッファ
いちいち書かないけど、メンバ変数を作ったらコンストラクタで初期化しとくように。
void Quad::Initialize()
{
:
:
:
// 頂点データ用バッファの設定
D3D11_BUFFER_DESC bd_vertex;
bd_vertex.ByteWidth = sizeof(vertices);
bd_vertex.Usage = D3D11_USAGE_DEFAULT;
bd_vertex.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd_vertex.CPUAccessFlags = 0;
bd_vertex.MiscFlags = 0;
bd_vertex.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA data_vertex;
data_vertex.pSysMem = vertices;
Direct3D::pDevice->CreateBuffer(&bd_vertex, &data_vertex, &pVertexBuffer_);
}
各設定項目はここを見よう。 ⇒ ここ
ところで、これだとエラーになる。
Direct3D::pDeviceはDirect3D.cppで作ってる変数なので、ここからは見えない。(.hはインクルードしてるけどね)
そういう時はDirect3D.hにこのように書く。
namespace Direct3D
{
extern ID3D11Device* pDevice;
externの使い方は……きっと誰かが説明してくれてるでしょう。
次に、何番と何番と何番の頂点で三角形になるのかという情報を用意する。
インデックス情報は順番が大事!
頂点の並びが時計回りになるように指定しなければならない。
そうじゃないとポリゴンが裏になって表示されなくなる。(カメラを裏に回せば見えるが)
順番が決まってしまえば書くのは簡単。
void Quad::Initialize()
{
:
:
:
//インデックス情報
int index[] = { 0,2,3, 0,1,2 };
}
このように、int型の配列に番号を順番に入れればいいだけ。
class Quad
{
ID3D11Buffer *pVertexBuffer_;
ID3D11Buffer *pIndexBuffer_;
public:
//インデックス情報
int index[] = { 0,2,3, 0,1,2 };
// インデックスバッファを生成する
D3D11_BUFFER_DESC bd;
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(index);
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = index;
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;
Direct3D::pDevice->CreateBuffer(&bd, &InitData, &pIndexBuffer_);
}
やり方は頂点バッファと同じ。
表示したいモデルに関する情報は揃ったが、カメラやライトの情報が無いと描画できない。
これらの情報は「コンスタントバッファ」という領域を用意してパイプラインに入れてやる。
まずは、渡してやる情報をまとめた構造体を用意する。
今回は、最低限必要なカメラの情報のみにする。
//コンスタントバッファー
struct CONSTANT_BUFFER
{
XMMATRIX matWVP;
};
class Quad
{
ID3D11Buffer *pVertexBuffer_;
ID3D11Buffer *pIndexBuffer_;
次に、コンスタントバッファ(データを置く場所)を作成する。
変数を用意して、Initialize関数の続きに記入。
class Quad
{
ID3D11Buffer *pVertexBuffer_;
ID3D11Buffer *pIndexBuffer_;
ID3D11Buffer *pConstantBuffer_; //コンスタントバッファ
public:
void Quad::Initialize()
{
:
:
:
//コンスタントバッファ作成
D3D11_BUFFER_DESC cb;
cb.ByteWidth = sizeof(CONSTANT_BUFFER);
cb.Usage = D3D11_USAGE_DYNAMIC;
cb.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cb.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
cb.MiscFlags = 0;
cb.StructureByteStride = 0;
// コンスタントバッファの作成
Direct3D::pDevice->CreateBuffer(&cb, nullptr, &pConstantBuffer_);
}
頂点バッファやインデックスバッファは、データを用意してからバッファを用意し、それと同時にバッファにデータを置いていた。
しかし、カメラの位置とかはゲーム中に変わる可能性があるので、この時点では決まっていない。
そこで、この時点では空の状態のバッファだけ用意しておいて、Draw関数でデータを用意してやる。
ということでDraw関数でカメラの情報を作成しよう。
あとでカメラクラスを別に作るので、説明はその時にする。
void Quad::Draw()
{
//コンスタントバッファに渡す情報
XMVECTOR position = { 0, 3, -10, 0 }; //カメラの位置
XMVECTOR target = { 0, 0, 0, 0 }; //カメラの焦点
XMMATRIX view = XMMatrixLookAtLH(position, target, XMVectorSet(0, 1, 0, 0)); //ビュー行列
XMMATRIX proj = XMMatrixPerspectiveFovLH(XM_PIDIV4, 800.0f / 600.0f, 0.1f, 100.0f);//射影行列
CONSTANT_BUFFER cb;
cb.matWVP = XMMatrixTranspose(view * proj);
}
ビュー行列(カメラの位置や向き)と射影行列(レンズの画角など)の情報を準備。
それを掛け算したものが今回必要になるデータ。
次はこれをバッファに格納する。
void Quad::Draw()
{
:
:
:
D3D11_MAPPED_SUBRESOURCE pdata;
Direct3D::pContext->Map(pConstantBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &pdata); // GPUからのデータアクセスを止める
memcpy_s(pdata.pData, pdata.RowPitch, (void*)(&cb), sizeof(cb)); // データを値を送る
Direct3D::pContext->Unmap(pConstantBuffer_, 0); //再開
}
Mapという関数で動きをいったん止めて書き込み可能状態にし、memcpy_sでデータをぶっこんで、Unmapで動きを再開させる。
これだと、pContextのところにエラーが出るので……どうすればいいんだっけ?(Direct3D.hにpContextが無いのが原因)
やっと描画できる。
まず、頂点バッファ、インデックスバッファ、コンスタントバッファにあるデータをパイプラインに流す。
(バッファは複数用意しておいて、この時にどれを使うか決めることもできる)
void Quad::Draw()
{
:
:
:
//頂点バッファ
UINT stride = sizeof(XMVECTOR);
UINT offset = 0;
Direct3D::pContext->IASetVertexBuffers(0, 1, &pVertexBuffer_, &stride, &offset);
// インデックスバッファーをセット
stride = sizeof(int);
offset = 0;
Direct3D::pContext->IASetIndexBuffer(pIndexBuffer_, DXGI_FORMAT_R32_UINT, 0);
//コンスタントバッファ
Direct3D::pContext->VSSetConstantBuffers(0, 1, &pConstantBuffer_); //頂点シェーダー用
Direct3D::pContext->PSSetConstantBuffers(0, 1, &pConstantBuffer_); //ピクセルシェーダー用
}
そして描画!
Direct3D::pContext->DrawIndexed(6, 0, 0);
第一引数の「6」はインデックス情報の数。
配列の要素数が自動的に指定されるように書いておいた方がいいね。
これでQuadクラスは一通りできたので、このクラスを使ってみよう。
Main.cppにQuad型のポインタ変数を作り、しかるべき位置で「new」して「Initialize」「Draw」「Release」関数を呼ぼう。
実行すると、白い正方形が表示されるはず。
作ったものを解放しよう。
もう自分で書けますね?
ヘッダで作った変数は、コンストラクタで初期化し(ポインタなら)最後に解放する。
解放の順番は「作った時の逆」。