今(2017年5月)Windows&C++で3DプログラミングするならDirectXかOpenGLだと思いますが、最新バージョン(DirextX12など)は、性能重視のため手軽にプログラミングができなくなっています。
なので古いバージョンで使えるものを調べて、プログラミングができる環境を作ってみました。
作成した環境は、OpenGL1.1、OpenGL4.5、DirectX9、DirectX11、DirectX12で、初期化と画面クリア、簡単なポリゴンの描画まで行っています。
VSプロジェクト、ソースコードを公開していますので、3Dプログラミングの参考にしてください。
記事最後にあるダウンロードボタンから取得できます。
Visual Studio 2017インストール
プログラムの開発環境として、Visual Studio Community 2017が無料で利用できます。30日間試用期間があり、それ以降も使う場合は登録が必要になります。
ダウンロード
https://www.visualstudio.com/ja/downloads/
からインストーラーをダウンロード。
インストール
ダウンロードしたファイルを実行するとVisual Studio Installerが起動し、インストールしたい開発環境を選択します。3Dプログラム(DirectX、OpenGL)をするために「C++によるデスクトップ開発」をインストールします。
右側にはインストールする機能を細かく指定できるようになっています。今回はデフォルトのままでインストールします。右下に「Windows 10 SDK (10.xxxxxxx)」とありますが選択する必要はありません。古いバージョンのWindows SDKをインストールするときに使用します。
Windows 10 SDK
DirectXとOpenGLはこのSDKに含まれています。
Visual Studioをインストールすると最新版のSDKがインストールされます(のはず・・・、いつの間にかインストールされいてVisual Studio InstallerのWindowsSDKのリストにありませんでした)。
環境構築
ここで説明するものはすべてWindows10SDKに入っているためVisualStudioのインストールのみです。利用するヘッダやライブラリファイル、初期化の方法について説明していきます。
OpenGL1.1
簡単な3Dプログラムならこれで十分だと思います。Windows環境以外でも多くの環境で対応していて、本、ネットなどから簡単に情報を入手できます。古いAPIなのでシェーダや複雑な処理はできません。
ヘッダーとライブラリファイル
#include <gl/gl.h>
#pragma comment(lib,"OpenGL32.lib")
初期化と終了処理
簡単に初期化を行える外部ライブラリがありますが使用せずに、Windows用のOpenGL関数(wgl~)のみを使った方法を紹介します。
//デバイスコンテキスト取得
g_hDC = GetDC(hwnd);//hwnd:ウインドウハンドル
//OpenGLが使用可能なピクセルフォーマットを取得
PIXELFORMATDESCRIPTOR pfdesc;
ZeroMemory(&pfdesc, sizeof(pfdesc));
pfdesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfdesc.nVersion = 1;
pfdesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfdesc.iPixelType = PFD_TYPE_RGBA;
pfdesc.cColorBits = 32;
pfdesc.cDepthBits = 24;
pfdesc.cStencilBits = 8;
int format = ChoosePixelFormat(hDC, &pfdesc);
//format==0 失敗 対応したフォーマットがない
// 取得したピクセルフォーマットを設定
// これで指定したウインドウがOpenGLで使用可能に
SetPixelFormat(hDC, format, &pfdesc));
//OpenGLコンテキスト作成
hGL = wglCreateContext(hDC);
wglMakeCurrent(hDC, hGL)) {
//ここからOpenGL関数(gl~)が使用可能になる
//削除
//OpenGLコンテキストの削除
wglDeleteContext(hGL);
//デバイスコンテキスト解放
ReleaseDC(hwnd, hDC);
簡単な描画処理例
ポリゴンを描画するには、頂点や色などを1つずつ関数を呼び出して行います。簡単ですが効率は悪いです。ライティングは決められた計算方法のみで、シェーダーはありません。
//画面クリア
glClearColor(0.0f, 0.125f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//ポリゴン描画
glBegin(GL_TRIANGLES);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.5f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.5f, -0.5f, 0.0f);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(-0.5f, -0.5f, 0.0f);
glEnd();
//描画処理を実行
glFlush();
//ダブルバッファ(描画バッファと表示バッファ)入れ替え
//Windows用関数 g_hDC:デバイスコンテキスト
SwapBuffers(g_hDC);
DirectX9
高度な処理や計算性能の必要としない3Dプログラムやゲームであれば十分使えます。古いAPIですが、それなりに複雑な処理が可能で、シェーダーも使えます。固定のライティング処理が用意されているためシェーダーを書かなくてもポリゴンなどを描画できます。最新の機能がなく、シェーダーの制約も多いですが、その代わり高性能PCでなくても動作します。
他のバージョンと比べて、簡単に色々な情報が入手できます。
ヘッダーとライブラリファイル
#include <d3d9.h>
#pragma comment(lib,"d3d9.lib")
初期化と終了処理
今回紹介する中で一番簡単で短いソースコードです。構造体に必要な情報を入力しデバイスを作成します。
HWND hwnd = ウインドウハンドル;
LPDIRECT3D9 pD3D = nullptr;
LPDIRECT3DDEVICE9 pDevice = nullptr;
pD3D = Direct3DCreate9(D3D_SDK_VERSION);
// デバイス作成
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &pDevice);
//pDeviceを使って描画処理
//削除
pDevice->Release();
pD3D->Release();
簡単な描画処理例
頂点データを作成し、固定のライティングかシェーダーかどちらかを選んで描画処理を行います。
//データ構築
LPDIRECT3DVERTEXBUFFER9 pVBuffer = nullptr;
// 頂点データ作成
struct Vertex
{
FLOAT x,y,z;
D3DCOLOR color;
};
Vertex VertexData[3] = {
{ 0.0f, 0.5f, 0.0f, D3DCOLOR_ARGB(255, 0,0,255) },
{ 0.5f, -0.5f, 0.0f, D3DCOLOR_ARGB(255,0,255,0) },
{ -0.5f, -0.5f, 0.0f, D3DCOLOR_ARGB(255, 255,0,0) },
};
pDevice->CreateVertexBuffer(sizeof(VertexData), 0,
D3DFVF_XYZ | D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pVBuffer, NULL);
void* vb;
if (SUCCEEDED(pVBuffer->Lock(0, sizeof(VertexData), &vb, 0))) {
memcpy(vb, &VertexData, sizeof(VertexData));
pVBuffer->Unlock();
}
//描画処理
//画面クリア
pDevice->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 50, 100), 1.0f, 0);
pDevice->BeginScene();
pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
pDevice->SetStreamSource(0, pVBuffer, 0, sizeof(Vertex));
pDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE);
pDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
pDevice->EndScene();
//画面更新
pDevice->Present(NULL, NULL, NULL, NULL);
OpenGL4
シェーダーなど新しい機能に対応したバージョンです。描画処理もDirectXのような頂点バッファとシェーダーを使った方法に変更され、古い描画処理と固定のライティング機能は廃止されています。また使用するにはグラフィックボードのドライバーが対応している必要があり、利用できる環境が制限されます。
古いバージョンの情報が多く、新しいバージョンの情報を探すのに少し苦労します。
ヘッダーとライブラリファイル
glext.hというヘッダファイルが必要になります。
https://khronos.org/registry/OpenGL/index_gl.phpから入手できます。
新しいバージョンのlibファイルはなく、実行時に動的なリンク処理(関数ポインタの取得)が必要になります。
#include <gl/gl.h>
#include "exgl.h"
#pragma comment(lib,"OpenGL32.lib")
初期化と終了処理
ウインドウの初期化などはOpenGL1.1と同じです。新しい機能を使うために実行時に動的なリンク処理を行います。ここでは使用していませんが、この処理を行う外部ライブラリ(glewなど)があります。
// 関数ポインタ取得
// 未対応の場合NULLポインタ
PFNGLBINDVERTEXBUFFERPROC glBindVertexBuffer;
glBindVertexBuffer = (glBindVertexBuffer)wglGetProcAddress("glBindVertexBuffer");
//関数呼び出し
glBindVertexBuffer(GL_ARRAY_BUFFER, id)
簡単な描画処理例
頂点バッファやシェーダーの作成は長いため、描画処理の部分のみです。
//画面クリア
glClearColor(0.0f, 0.125f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//シェーダー
glUseProgram(ProgID);
//属性0に頂点の位置データを割り当て
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBuffID);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// 三角形描画
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(0);
//描画処理を実行
glFlush();
//ダブルバッファ(描画バッファと表示バッファ)入れ替え
SwapBuffers(hDC);
DirectX11
ジオメトリシェーダーなどの機能追加され、より高度で効率の良いプログラムが可能です。支援機能(フォント、画像からテクスチャ生成、シェーダーエフェクトなど)が削られているため、自分で作成するか外部のライブラリ(DirectXTKなど)を使用します。
Windows7以降、新機能に対応したGPUを搭載したPCが必要になります。
あと、情報がほとんどありません(英語ならあるかも)。中上級者向け。
ヘッダーとライブラリファイル
#include <d3d11.h>
#pragma comment(lib,"d3d11.lib")
初期化と終了処理
処理が複雑で長いため一部のみ、サンプルのソースコードを見てください。
D3D_DRIVER_TYPE g_DriverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_FeatureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pd3dDevice = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
// DirectX11&ハードウェア対応
D3D_FEATURE_LEVEL feature_level = D3D_FEATURE_LEVEL_11_0;
g_DriverType = D3D_DRIVER_TYPE_HARDWARE;
// スワップチェイン設定
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1; //1/60 = 60fps
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = g_hWndDX11;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
// DirectX11デバイスとスワップチェイン作成
D3D11CreateDeviceAndSwapChain(NULL, g_DriverType, NULL,
cdev_flags, &feature_level, 1, D3D11_SDK_VERSION, &sd,
&g_pSwapChain, &g_pd3dDevice, &g_FeatureLevel, &g_pImmediateContext);
簡単な描画処理例
頂点バッファやシェーダーの作成は長いため、描画処理の部分のみです。
// 指定色で画面クリア
float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; //red,green,blue,alpha
g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, ClearColor);
// 入力レイアウト
g_pImmediateContext->IASetInputLayout(g_pVertexLayout);
// 頂点バッファ
UINT stride = sizeof(VertexData);
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);
// プリミティブ形状
g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// シェーダ
g_pImmediateContext->VSSetShader(g_pVertexShader, NULL, 0);
g_pImmediateContext->PSSetShader(g_pPixelShader, NULL, 0);
//ポリゴン描画
g_pImmediateContext->Draw(3, 0);
//結果をウインドウに反映
g_pSwapChain->Present(0, 0);
DirectX12
機能的には11とほぼ同じですが、メモリなどのリソース管理、CPUとGPUの並列実行などすべて自分で行う必要があります。その代わり、かなり効率の良いプログラムが可能です。
グラフィックライブラリやゲームエンジンの開発者用です。通常の3Dプログラムで使用することはないと思います。DirectX11以上に情報の取得が困難です。
ヘッダーとライブラリファイル
#include <dxgi1_4.h>
#include <d3d12.h>
#pragma comment(lib,"dxgi.lib")
#pragma comment(lib,"d3d12.lib")
初期化と終了処理
処理が複雑で長いため省略。サンプルのソースコードを見てください。
簡単な描画処理例
画面クリアのみを行うソースコードです。
pCmdAllocator->Reset();
pCmdList->Reset(pCmdAllocator.Get(), pPLineState.Get());
pCmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(apRenTarget[uFrameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(pRTVHeap->GetCPUDescriptorHandleForHeapStart(), uFrameIndex, uRTVDescSize);
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
pCmdList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
pCmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(apRenTarget[uFrameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
hr = pCmdList->Close();
ID3D12CommandList* ppCommandLists[] = { pCmdList.Get() };
pCmdQ->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
hr = pSwapChain->Present(1, 0);
{//GPU実行待ち GPU命令実行後すぐに終了待ち
const UINT64 fence = uFenceValue;
pCmdQ->Signal(pFence.Get(), fence);
uFenceValue++;
if (pFence->GetCompletedValue() < fence)
{
hr = pFence->SetEventOnCompletion(fence, hFenceEvent);
WaitForSingleObject(hFenceEvent, INFINITE);
}
uFrameIndex = pSwapChain->GetCurrentBackBufferIndex();
}
サンプルソースコード
DirectX9、DirectX11、DirectX12、OpenGL1.1、OpenGL4.5各環境で初期化と画面クリア、簡単なポリゴンの描画まで行うプログラムです。WindowsSDK以外の外部ライブラリなどを使用していないため、VisualStudioのインストールだけでコンパイルできます。
コンパイル時の注意点
新しいバージョンのWindowsSDKでコンパイルする場合、「ソリューションの再ターゲット」を実行してください。
“Start 3DProgram VS2017” をダウンロード Start3DProgVS2017-1.zip – 974 回のダウンロード – 171 KB
「VisualStudio2017(Windows10 DirectX OpenGL)で始める3Dプログラミング」への1件のフィードバック