|開発環境|Microsoft Visual Studio Community 2019| |実行環境|Microsoft Windows 10 Home (64bit)| |プロジェクト テンプレート|C++ 空のプロジェクト| |プロジェクト名|dx11domino| #image(dx11domino.jpg) dx11domino.cpp #highlight(c++){{ #pragma comment(lib, "d3d11") #pragma comment(lib, "d3dcompiler") #include <Windows.h> #include <wrl.h> #include <d3d11.h> #include <d3dcompiler.h> #include <DirectXMath.h> #define HLSL_FILE L"dx11domino.fx" #define NUM 500 #define STEP 4 #define CYCLE (NUM * STEP) using namespace Microsoft::WRL; using namespace DirectX; struct ConstantBuffer { XMMATRIX mView; XMMATRIX mProjection; float frame; float cycle; float f2, f3; // filler }; struct InstanceBuffer { XMFLOAT4 domino; }; struct Head { float x, y, z; float dir; float delta; }; // 外部変数 ComPtr<ID3D11Device> g_pDevice; ComPtr<ID3D11DeviceContext> g_pContext; ComPtr<IDXGISwapChain> g_pSwapChain; ComPtr<ID3D11RenderTargetView> g_pRenderTargetView; ComPtr<ID3D11VertexShader> g_pVertexShader; ComPtr<ID3D11PixelShader> g_pPixelShader; ComPtr<ID3D11InputLayout> g_pInstanceLayout; ComPtr<ID3D11Buffer> g_pInstanceBuffer; ComPtr<ID3D11Buffer> g_pConstantBuffer; ComPtr<ID3D11Texture2D> g_pDepthBuffer; ComPtr<ID3D11DepthStencilView> g_pDepthView; InstanceBuffer g_ib[NUM]; XMFLOAT4 g_rail[CYCLE]; Head g_head; XMFLOAT3 g_cam = { 0, 20, 0 }; int g_frame = 0; // 関数宣言 HWND InitWindow(HINSTANCE hInst); HRESULT InitDevice(HWND hWnd); void CleanupDevice(); LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void Render(); void CreateRail(); int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow) { HWND hWnd = InitWindow(hInstance); if (!hWnd) return -1; if (FAILED(InitDevice(hWnd))) { CleanupDevice(); return -1; } for (int i = 0; i < CYCLE; i++) { CreateRail(); } ShowWindow(hWnd, nCmdShow); MSG msg = { 0 }; while (msg.message != WM_QUIT) { if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { Render(); } } CleanupDevice(); return int(msg.wParam); } HWND InitWindow(HINSTANCE hInst) { WNDCLASSEX wc = { sizeof WNDCLASSEX }; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.hInstance = hInst; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.hbrBackground = HBRUSH(COLOR_WINDOW + 1); wc.lpszClassName = L"dx11domino"; if (!RegisterClassEx(&wc)) return nullptr; DWORD dwStyle = WS_OVERLAPPEDWINDOW; RECT rc = { 0, 0, 640, 400 }; AdjustWindowRect(&rc, dwStyle, FALSE); HWND hWnd = CreateWindow( wc.lpszClassName, L"dx11domino", dwStyle, CW_USEDEFAULT, 0, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, hInst, nullptr); return hWnd; } HRESULT CompileShaderFromFile( LPCWSTR szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut) { DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; ComPtr<ID3DBlob> pErrorBlob; HRESULT hr = D3DCompileFromFile(szFileName, nullptr, nullptr, szEntryPoint, szShaderModel, dwShaderFlags, 0, ppBlobOut, &pErrorBlob); if (FAILED(hr)) { if (pErrorBlob) { OutputDebugStringA((LPCSTR)pErrorBlob->GetBufferPointer()); } return hr; } return S_OK; } HRESULT InitDevice(HWND hWnd) { HRESULT hr; RECT rc; GetClientRect(hWnd, &rc); UINT width = rc.right - rc.left; UINT height = rc.bottom - rc.top; DXGI_SWAP_CHAIN_DESC sd = { 0 }; sd.BufferDesc.Width = width; sd.BufferDesc.Height = height; sd.BufferDesc.RefreshRate.Numerator = 60; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 符号なし正規化整数 sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.BufferCount = 1; sd.OutputWindow = hWnd; sd.Windowed = TRUE; sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; D3D_FEATURE_LEVEL featureLevel; hr = D3D11CreateDeviceAndSwapChain( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pDevice, &featureLevel, &g_pContext); if (FAILED(hr)) return hr; // レンダーターゲットビュー ComPtr<ID3D11Texture2D> pBackBuffer; hr = g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); if (FAILED(hr)) return hr; hr = g_pDevice->CreateRenderTargetView( pBackBuffer.Get(), nullptr, &g_pRenderTargetView); if (FAILED(hr)) return hr; // 深度バッファ D3D11_TEXTURE2D_DESC td = { 0 }; td.Width = width; td.Height = height; td.MipLevels = 1; td.ArraySize = 1; td.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; td.SampleDesc.Count = 1; td.SampleDesc.Quality = 0; td.Usage = D3D11_USAGE_DEFAULT; td.BindFlags = D3D11_BIND_DEPTH_STENCIL; hr = g_pDevice->CreateTexture2D(&td, nullptr, &g_pDepthBuffer); if (FAILED(hr)) return hr; // 深度ビュー D3D11_DEPTH_STENCIL_VIEW_DESC dsv = { td.Format, D3D11_DSV_DIMENSION_TEXTURE2D }; g_pDevice->CreateDepthStencilView(g_pDepthBuffer.Get(), &dsv, &g_pDepthView); if (FAILED(hr)) return hr; // ビューポート D3D11_VIEWPORT vp = { 0 }; vp.Width = (FLOAT)width; vp.Height = (FLOAT)height; vp.MinDepth = 0; vp.MaxDepth = 1; g_pContext->RSSetViewports(1, &vp); // 頂点シェーダ ComPtr<ID3DBlob> pVSBlob; hr = CompileShaderFromFile(HLSL_FILE, "VS", "vs_5_0", &pVSBlob); if (FAILED(hr)) { return hr; } hr = g_pDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &g_pVertexShader); if (FAILED(hr)) return hr; // ピクセルシェーダ ComPtr<ID3DBlob> pPSBlob; hr = CompileShaderFromFile(HLSL_FILE, "PS", "ps_5_0", &pPSBlob); if (FAILED(hr)) { return hr; } hr = g_pDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &g_pPixelShader); if (FAILED(hr)) return hr; // 入力レイアウト D3D11_INPUT_ELEMENT_DESC layout[] = { { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1 }, }; hr = g_pDevice->CreateInputLayout(layout, ARRAYSIZE(layout), pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pInstanceLayout); if (FAILED(hr)) return hr; g_pContext->IASetInputLayout(g_pInstanceLayout.Get()); // インスタンスバッファ D3D11_BUFFER_DESC ib = { 0 }; ib.ByteWidth = (sizeof InstanceBuffer) * NUM; ib.Usage = D3D11_USAGE_DEFAULT; ib.BindFlags = D3D11_BIND_VERTEX_BUFFER; hr = g_pDevice->CreateBuffer(&ib, nullptr, &g_pInstanceBuffer); if (FAILED(hr)) return hr; UINT stride = sizeof InstanceBuffer; UINT offset = 0; g_pContext->IASetVertexBuffers( 0, 1, g_pInstanceBuffer.GetAddressOf(), &stride, &offset); // 定数バッファ D3D11_BUFFER_DESC cb = { 0 }; cb.ByteWidth = sizeof ConstantBuffer; cb.Usage = D3D11_USAGE_DEFAULT; cb.BindFlags = D3D11_BIND_CONSTANT_BUFFER; hr = g_pDevice->CreateBuffer(&cb, nullptr, &g_pConstantBuffer); if (FAILED(hr)) return hr; // g_pContext->VSSetShader(g_pVertexShader.Get(), nullptr, 0); g_pContext->VSSetConstantBuffers(0, 1, g_pConstantBuffer.GetAddressOf()); g_pContext->PSSetShader(g_pPixelShader.Get(), nullptr, 0); return S_OK; } void CleanupDevice() { if (g_pContext) { g_pContext->ClearState(); } } LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; switch (uMsg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } void Render() { XMFLOAT4 target = g_rail[(g_frame + CYCLE / 2) % CYCLE]; float dx = g_cam.x - target.x; float dz = g_cam.z - target.z; float dist1 = sqrtf(dx * dx + dz * dz); float dist2 = fminf(fmaxf(dist1, 20), 100); if (dist1 != dist2) { float rad = atan2f(dx, dz); g_cam.x = target.x + sinf(rad) * dist2; g_cam.z = target.z + cosf(rad) * dist2; } // g_pContext->OMSetRenderTargets( 1, g_pRenderTargetView.GetAddressOf(), g_pDepthView.Get()); float ClearColor[4] = { 0, 0, 0, 1 }; g_pContext->ClearRenderTargetView(g_pRenderTargetView.Get(), ClearColor); g_pContext->ClearDepthStencilView(g_pDepthView.Get(), D3D11_CLEAR_DEPTH, 1, 0); XMFLOAT3 up(0, 1, 0); ConstantBuffer cb; cb.mView = XMMatrixLookAtLH( XMLoadFloat3(&g_cam), XMLoadFloat4(&target), XMLoadFloat3(&up)); cb.mProjection = XMMatrixPerspectiveFovLH( XMConvertToRadians(15), 640.0f / 400.0f, 1, 500); cb.frame = float(g_frame); cb.cycle = CYCLE; g_pContext->UpdateSubresource(g_pConstantBuffer.Get(), 0, nullptr, &cb, 0, 0); g_pContext->UpdateSubresource(g_pInstanceBuffer.Get(), 0, nullptr, g_ib, 0, 0); g_pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); g_pContext->DrawInstanced(18, NUM, 0, 0); g_pSwapChain->Present(1, 0); // CreateRail(); } void CreateRail() { if (g_frame % 32 == 0) { g_head.delta = float(rand() % 4 - 1); } float dist = 0.75; float rad = XMConvertToRadians(g_head.dir); float s, c; XMScalarSinCos(&s, &c, rad); g_head.x += s * dist; g_head.z += c * dist; g_head.dir += g_head.delta; int f = g_frame % CYCLE; g_rail[f] = { g_head.x, 0, g_head.z, rad }; if (g_frame % STEP == 0) { int id = f / STEP; g_ib[id].domino = g_rail[f]; } g_frame++; } }} dx11domino.fx #highlight(txt){{ cbuffer ConstantBuffer : register(b0) { matrix View; matrix Projection; float frame; float cycle; }; struct VS_INPUT { float4 domino : TEXCOORD; uint vid : SV_VertexID; uint iid : SV_InstanceID; }; struct PS_INPUT { float4 Pos : SV_Position; float4 Coord : TEXCOORD; }; matrix rotationX(float angle) { float c = cos(angle); float s = sin(angle); return matrix( 1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); } matrix rotationY(float angle) { float c = cos(angle); float s = sin(angle); return matrix( c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); } matrix translation(float3 t) { return matrix( 1, 0, 0, t.x, 0, 1, 0, t.y, 0, 0, 1, t.z, 0, 0, 0, 1); } static uint vertices[] = { 3,1,2,0, 0,4,2,6,3,7,1,5,0,4, 4,5,6,7 }; // Vertex Shader PS_INPUT VS(VS_INPUT input) { PS_INPUT output = (PS_INPUT)0; uint vtx = vertices[input.vid]; float3 coord = float3(vtx & 1, (vtx >> 1) & 1, (vtx >> 2) & 1); float3 pos = (coord - 0.5) * float3(2, 4, 1); float iframe = floor(frame / cycle) * cycle + float(input.iid) * 4.0; if (iframe < frame) { iframe += cycle; } float f = iframe - frame; float a = clamp((cycle / 2.0 - f) * 7.5, 0.0, 70.0); matrix world = mul(rotationY(input.domino.w), rotationX(radians(a))); world = mul(translation(input.domino.xyz), world); matrix mvp = mul(mul(Projection, View), world); output.Pos = mul(mvp, float4(pos, 1)); output.Coord = float4(coord, 1); return output; } // Pixel Shader float4 PS(PS_INPUT input) : SV_Target { float3 edge = step(0.48, abs(input.Coord.xyz - 0.5)); if (length(edge) > 1.0) { return float4(0.5, 0.5, 0.5, 1); } return input.Coord; } }}