#pragma comment(lib, "d3d11")
#pragma comment(lib, "d3dcompiler")
#include <d3d11.h>
#include <d3dcompiler.h>
#include <DirectXColors.h>
using namespace DirectX;
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
struct CustomVertex {
XMFLOAT2 Position;
XMFLOAT2 UV;
};
// 外部変数
ID3D11Device *pDevice;
ID3D11DeviceContext *pDeviceContext;
IDXGISwapChain *pSwapChain;
ID3D11RenderTargetView *pRenderTargetView;
ID3D11InputLayout *pVertexLayout;
ID3D11Buffer *pVertexBuffer;
ID3D11Buffer *pConstBuffer;
ID3D11VertexShader *pVertexShader;
ID3D11PixelShader *pPixelShader;
double offsetx;
double offsety;
double zoom = 4;
XMFLOAT4 center;
// 関数プロトタイプ宣言
HWND InitWnd(HINSTANCE hInstance, int nCmdShow);
HRESULT InitDevice(HWND hWnd);
HRESULT CompileShaderFromFile(LPCTSTR pFileName, LPCSTR pEntrypoint, LPCSTR pTarget, ID3DBlob **ppCode);
HRESULT CreateShaderBuffer();
void Render();
void CleanupDevice();
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//==============================================================================
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
// ウィンドウ初期化
HWND hWnd = InitWnd(hInstance, nCmdShow);
if (hWnd == NULL) {
return 0;
}
// デバイス初期化
if (FAILED(InitDevice(hWnd))) {
return 0;
}
// メインループ
MSG msg = { 0 };
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
// 描画
Render();
}
}
// デバイスのクリーンアップ
CleanupDevice();
return msg.wParam;
}
HWND InitWnd(HINSTANCE hInstance, int nCmdShow)
{
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof wc);
wc.cbSize = sizeof wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"DxMandelbrot";
RegisterClassEx(&wc);
DWORD dwStyle = WS_OVERLAPPEDWINDOW ^ WS_MAXIMIZEBOX ^ WS_THICKFRAME;
RECT rc = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
AdjustWindowRect(&rc, dwStyle, FALSE);
HWND hWnd = CreateWindow(
wc.lpszClassName, L"DxMandelbrot", dwStyle,
CW_USEDEFAULT, 0, rc.right - rc.left, rc.bottom - rc.top,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return hWnd;
}
HRESULT InitDevice(HWND hWnd)
{
// ドライバータイプ
D3D_DRIVER_TYPE DriverType[] = {
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
// 機能レベル
D3D_FEATURE_LEVEL FeatureLevels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
// D3D_FEATURE_LEVEL_10_1,
// D3D_FEATURE_LEVEL_10_0,
};
// スワップチェインの設定
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof sd);
sd.BufferCount = 1;
sd.BufferDesc.Width = WINDOW_WIDTH;
sd.BufferDesc.Height = WINDOW_HEIGHT;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
// デバイスとスワップチェインを作成
HRESULT hr;
for (int i = 0; i < ARRAYSIZE(DriverType); i++) {
D3D_FEATURE_LEVEL FeatureLevel;
hr = D3D11CreateDeviceAndSwapChain(NULL, DriverType[i], NULL, 0,
FeatureLevels, ARRAYSIZE(FeatureLevels), D3D11_SDK_VERSION, &sd,
&pSwapChain, &pDevice, &FeatureLevel, &pDeviceContext);
if (SUCCEEDED(hr)) break;
}
if (FAILED(hr)) {
return hr;
}
// バックバッファを取得
ID3D11Texture2D *pBackBuffer;
hr = pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
// レンダーターゲットを生成
hr = pDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView);
pBackBuffer->Release();
// 出力マネージャにレンダーターゲットを設定
pDeviceContext->OMSetRenderTargets(1, &pRenderTargetView, NULL);
// ビューポートの設定
D3D11_VIEWPORT vp;
vp.Width = WINDOW_WIDTH;
vp.Height = WINDOW_HEIGHT;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
// デバイスコンテキストにビューポートを設定
pDeviceContext->RSSetViewports(1, &vp);
// 頂点シェーダをコンパイル
ID3DBlob *pCode;
if (FAILED(CompileShaderFromFile(L"shader.fx", "VS", "vs_5_0", &pCode))) {
return E_FAIL;
}
// 頂点シェーダ生成
hr = pDevice->CreateVertexShader(
pCode->GetBufferPointer(), pCode->GetBufferSize(), NULL, &pVertexShader);
if (FAILED(hr)) {
return hr;
}
// 入力レイアウトの定義
D3D11_INPUT_ELEMENT_DESC layout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
// 入力レイアウトを生成
hr = pDevice->CreateInputLayout(layout, ARRAYSIZE(layout),
pCode->GetBufferPointer(), pCode->GetBufferSize(), &pVertexLayout);
SAFE_RELEASE(pCode);
if (FAILED(hr)) {
return hr;
}
// 入力アセンブラ(input-assembler)に入力レイアウトを設定
pDeviceContext->IASetInputLayout(pVertexLayout);
// ピクセルシェーダをコンパイル
hr = CompileShaderFromFile(L"shader.fx", "PS", "ps_5_0", &pCode);
if (FAILED(hr)) {
return hr;
}
// ピクセルシェーダ生成
hr = pDevice->CreatePixelShader(
pCode->GetBufferPointer(), pCode->GetBufferSize(), NULL, &pPixelShader);
SAFE_RELEASE(pCode);
if (FAILED(hr)) {
return hr;
}
// 頂点の定義
CustomVertex vertices[] = {
{ XMFLOAT2(-1, 1), XMFLOAT2(0, 1) },// LU
{ XMFLOAT2(1, 1), XMFLOAT2(1, 1) }, // RU
{ XMFLOAT2(-1, -1), XMFLOAT2(0, 0) }, // LD
{ XMFLOAT2(1, -1), XMFLOAT2(1, 0) }, // RD
};
// 頂点バッファの設定
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof bd);
bd.ByteWidth = sizeof vertices;
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
// サブリソースの設定
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof InitData);
InitData.pSysMem = vertices;
// 頂点バッファの生成
hr = pDevice->CreateBuffer(&bd, &InitData, &pVertexBuffer);
if (FAILED(hr)) {
return hr;
}
// 入力アセンブラに頂点バッファを設定
UINT stride = sizeof CustomVertex;
UINT offset = 0;
pDeviceContext->IASetVertexBuffers(0, 1, &pVertexBuffer, &stride, &offset);
// ピクセルシェーダのシェーダ変数
CreateShaderBuffer();
// プリミティブの種類を設定
pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
return S_OK;
}
HRESULT CompileShaderFromFile(LPCTSTR pFileName, LPCSTR pEntrypoint, LPCSTR pTarget, ID3DBlob **ppCode)
{
ID3DBlob *pErrorMsgs;
HRESULT hr = D3DCompileFromFile(
pFileName, NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, pEntrypoint, pTarget,
0, 0, ppCode, &pErrorMsgs);
return hr;
}
HRESULT CreateShaderBuffer()
{
center = { (float)offsetx, (float)offsety, (float)zoom, (float)WINDOW_HEIGHT / WINDOW_WIDTH };
D3D11_BUFFER_DESC bd;
bd.ByteWidth = sizeof center;
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
bd.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = ¢er;
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;
HRESULT hr = pDevice->CreateBuffer(&bd, &InitData, &pConstBuffer);
pDeviceContext->PSSetConstantBuffers(0, 1, &pConstBuffer);
return hr;
}
void Render()
{
// ビューをクリアする
pDeviceContext->ClearRenderTargetView(pRenderTargetView, Colors::CornflowerBlue);
// シェーダを設定して描画
pDeviceContext->VSSetShader(pVertexShader, NULL, 0);
pDeviceContext->PSSetShader(pPixelShader, NULL, 0);
center = { (float)offsetx, (float)offsety, (float)zoom, (float)WINDOW_HEIGHT / WINDOW_WIDTH };
pDeviceContext->UpdateSubresource(pConstBuffer, 0, NULL, ¢er, 0, 0);
// 描画
pDeviceContext->Draw(4, 0);
// 描画結果を表示
pSwapChain->Present(0, 0);
}
void CleanupDevice()
{
// ステートをクリアし、デフォルト状態にする
if (pDeviceContext) {
pDeviceContext->ClearState();
pDeviceContext->Flush();
}
SAFE_RELEASE(pVertexShader);
SAFE_RELEASE(pPixelShader);
SAFE_RELEASE(pConstBuffer);
SAFE_RELEASE(pVertexBuffer);
SAFE_RELEASE(pVertexLayout);
SAFE_RELEASE(pRenderTargetView);
SAFE_RELEASE(pSwapChain);
SAFE_RELEASE(pDeviceContext);
SAFE_RELEASE(pDevice);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
DestroyWindow(hWnd);
break;
case VK_UP:
zoom *= 0.9;
break;
case VK_DOWN:
zoom *= 1.1;
break;
case 'W':
offsety += zoom * 0.1;
break;
case 'S':
offsety -= zoom * 0.1;
break;
case 'A':
offsetx -= zoom * 0.1;
break;
case 'D':
offsetx += zoom * 0.1;
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}