開発環境 Microsoft Visual Studio Community 2019
実行環境 Microsoft Windows 10 Home (64bit)
プロジェクト テンプレート C++ 空のプロジェクト
プロジェクト名 DominoSaver


ダウンロード:DominoSaver.zip

構成プロパティ
HLSL コンパイラ/全般/シェーダー モデル Shader Model 5.0 (/5.0)

DominoSaver.cpp
#pragma comment(lib, "d3d11")
#pragma comment(lib, "scrnsavw")
#pragma comment(lib, "comctl32")
 
#include <Windows.h>
#include <wrl.h>
#include <d3d11.h>
#include <DirectXMath.h>
#include <ScrnSave.h>
 
#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;
 
// 関数宣言
HRESULT InitDevice(HWND hWnd);
void CleanupDevice();
void Render();
void CreateRail();
 
LRESULT WINAPI ScreenSaverProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_TIMER:
		Render();
		break;
	case WM_CREATE:
		InitDevice(hWnd);
		for (int i = 0; i < CYCLE; i++) {
			CreateRail();
		}
		SetTimer(hWnd, 1, 10, NULL);
		break;
	case WM_DESTROY:
		CleanupDevice();
		break;
	}
	return DefScreenSaverProc(hWnd, msg, wParam, lParam);
}
 
BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	return TRUE;
}
 
BOOL WINAPI RegisterDialogClasses(HANDLE hInst)
{
	return TRUE;
}
 
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);
 
	// 頂点シェーダ
	HMODULE hModule = GetModuleHandle(nullptr);
	HRSRC hResInfo = FindResource(hModule, L"VS", L"SHADER");
	HGLOBAL hResData = LoadResource(hModule, hResInfo);
	hr = g_pDevice->CreateVertexShader(
		LockResource(hResData), SizeofResource(hModule, hResInfo), nullptr, &g_pVertexShader);
	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),
		LockResource(hResData), SizeofResource(hModule, hResInfo), &g_pInstanceLayout);
	if (FAILED(hr)) return hr;
 
	g_pContext->IASetInputLayout(g_pInstanceLayout.Get());
 
	// ピクセルシェーダ
	hResInfo = FindResource(hModule, L"PS", L"SHADER");
	hResData = LoadResource(hModule, hResInfo);
	hr = g_pDevice->CreatePixelShader(
		LockResource(hResData), SizeofResource(hModule, hResInfo), nullptr, &g_pPixelShader);
	if (FAILED(hr)) return hr;
 
	// インスタンスバッファ
	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();
	}
}
 
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++;
}
 

DominoSaver.rc
VS	SHADER	"Release/VertexShader.cso"
PS	SHADER	"Release/PixelShader.cso"
 

VertexShader.hlsl
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 };
 
PS_INPUT main(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;
}
 

PixelShader.hlsl
struct PS_INPUT
{
    float4 Pos : SV_Position;
    float4 Coord : TEXCOORD;
};
 
float4 main(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;
}
 
最終更新:2020年07月08日 20:55