
// Create index buffer
WORD indices[] =
{
3,1,0,
2,1,3,
0,5,4,
1,5,0,
3,4,7,
0,4,3,
1,6,5,
2,6,1,
2,7,6,
3,7,2,
6,4,5,
7,4,6,
};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(WORD) * 36;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
initData.pSysMem = indices;
hr = g_pDevice->CreateBuffer(&bd, &initData, &g_pIndexBuffer);
if (FAILED(hr))
{
return hr;
}
g_pImmediateContext->IASetIndexBuffer(g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
이전 게시물에 Vertex buffer를 만드는 과정과 거의 동일하다. 마지막에 파이프라인에 설정하는 함수만 제외하고는..
생성하고 설정하는 건 쉬운데, Index buffer의 역할이 무엇일까
Index buffer는 Vertex buffer에 있는 Vertex들이 그려지는 순서라고 생각하면 될 거 같다.
즉, Vertex buffer와 Index buffer를 이용하여 primitive를 그리는 것이다.
이 과정을 IA(Input assembly stage)에서 수행한다.
struct ConstantBuffer
{
XMMATRIX mWorld;
XMMATRIX mView;
XMMATRIX mProjection;
};
Constant buffer는 셰이더에서 사용하는 변수들을 코드상에서 생성하여 전달하기 위한 buffer이다.
이 Constant buffer는 렌더링 되는 과정 속에서 값이 변하지 않을 뿐, 한 번 렌더링 된 이후에 변경될 수 있다.
위의 buffer 속에 들어있는 멤버 변수들처럼, 카메라의 위치를 갱신하는 등에 사용 될 수 있다. [참고자료]
cbuffer ConstantBuffer : register(b0)
{
matrix World;
matrix View;
matrix Projection;
}
따라서 위와 같은 코드가 셰이더 내에 존재해야 한다.
cbuffer = constant buffer
register(b0) = data를 수동적으로 pack하는데 사용되며 레지스터 번호(b0)로 주어진constant buffer에서만 레지스터에 packing 될 수 있다. [공식문서]
여기서 말하는 레지스터가 CPU에 있는 레지스터를 의미하는 건가? 싶어서 찾아보았다.
역시 아니였다
DirectX11에서 모든 셰이더 resource들은 특정한 slot에 binding되어 있다. 보통 셰이더 slot들은 선언한 순서대로 할당되는데, register를 사용하면 특정 슬롯에 할당할 수 있다고 한다. [참고자료1] [참고자료2]
이제 Constant buffer가 무엇인지 알았으니 이제 생성하고 설정하는 방법을 알아보도록 하겠다.
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(ConstantBuffer);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
hr = g_pDevice->CreateBuffer(&bd, nullptr, &g_pConstantBuffer);
if (FAILED(hr))
{
return hr;
}
Constant buffer를 만들었고
void Render()
{
...
g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pConstantBuffer);
...
}
Constant buffer를 설정했다.
언제나 그렇듯, 개념이 어렵지 실제로 생성하고 설정하는 과정은 처음이 어렵지 그 이후는 대체로 비슷하다.
Constant buffer를 활용하여 3차원 큐브를 회전 시키는 코드를 알아보자.
우선 3차원 큐브 정점을 설정해준다.
constexpr SimpleVertex vertices[] =
{
{ XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT4(0.0f, 1.0f, 1.0f, 1.0f) },
{ XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
{ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(1.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f) },
{ XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f) },
};
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(D3D11_BUFFER_DESC));
bd.Usage = D3D11_USAGE_DEFAULT; // default로 사용
bd.ByteWidth = sizeof(SimpleVertex) * 8; // vertex
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; // vertex buffer로 바인드
bd.CPUAccessFlags = 0; // cpu access하지 않음.
그리고 위에 설정한 Index buffer를 만들어 준다.
g_World = XMMatrixIdentity();
XMVECTOR eye = XMVectorSet(0.0f, 1.0f, -5.0f, 0.0f);
XMVECTOR at = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
g_View = XMMatrixLookAtLH(eye, at, up);
g_Projection = XMMatrixPerspectiveFovLH(XM_PIDIV2, static_cast<float>(width) / height, 0.01f, 100.0f);
그 후, World, View, Projection 행렬을 생성한다.
이 행렬은 게임 수학에서 가장 기본적이긴한데, 간단하게 말하자면 월드 좌표를 카메라 좌표로 변환하고 스크린 좌표로 변환하는 것이다.
그러면 이제 카메라 위치도 설정했겠다. 큐브만 돌리면 끝이다.
// Update our time
static float t = 0.0f;
if (g_driverType == D3D_DRIVER_TYPE_REFERENCE)
{
t += static_cast<float>(XM_PI) * 0.0125f;
}
else
{
static DWORD dwTimeStart = 0;
const DWORD dwTimeCur = GetTickCount();
if (dwTimeStart == 0)
dwTimeStart = dwTimeCur;
t = (dwTimeCur - dwTimeStart) / 1000.0f;
}
g_World = XMMatrixRotationY(t);
위의 코드로 타이머도 만들고, 그 타이머에 따라 World 행렬을 y축을 기준으로 회전 해준다.
ConstantBuffer cb;
cb.mWorld = XMMatrixTranspose(g_World);
cb.mView = XMMatrixTranspose(g_View);
cb.mProjection = XMMatrixTranspose(g_Projection);
g_pImmediateContext->UpdateSubresource(g_pConstantBuffer, 0, nullptr, &cb, 0, 0);
그 후, Vertex shader에 보내줄 Constant buffer 값을 설정해준다.
여기서 행렬을 전치하는 이유는 DirectX는 행 우선 행렬을 사용한다. 그러나 셰이더 상에서는 열 우선 행렬을 사용하기 때문에, 전치를 해서 셰이더 코드에 전송해준다.
여기서 Constant buffer는 Buffer의 Usage에 따라 값을 쓰는 방식에는 2가지가 있다.
D3D_USAGE_DEFAULT일 때는 서브리소스에 접근하여 값을 쓸 수 있다.
g_pImmediateContext->UpdateSubresource(g_pConstantBuffer, 0, nullptr, &cb, 0, 0);같이 쓸 수 있다.
D3D_USAGE_DYNAMIC일 때는 ID3D11Device->Map~UnMap을 사용하여 값을 쓸 수 있다.
조금 더 자세한 정보는 여기를 참고하면 좋을 거 같다.
#include <Windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
//--------------------------------------------------------------------------------------
// Using namespaces
//--------------------------------------------------------------------------------------
using namespace DirectX;
//--------------------------------------------------------------------------------------
// Structures
//--------------------------------------------------------------------------------------
struct SimpleVertex
{
XMFLOAT3 Pos;
XMFLOAT4 Color;
};
struct ConstantBuffer
{
XMMATRIX mWorld;
XMMATRIX mView;
XMMATRIX mProjection;
};
//--------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------
HINSTANCE g_hInstance;
HWND g_hWnd;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pDevice = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
ID3D11RenderTargetView* g_pRenterTargetView = nullptr;
ID3D11VertexShader* g_pVertexShader = nullptr;
ID3D11InputLayout* g_pVertexLayout = nullptr;
ID3D11PixelShader* g_pPixelShader = nullptr;
ID3D11Buffer* g_pVertexBuffer = nullptr;
ID3D11Buffer* g_pIndexBuffer = nullptr;
ID3D11Buffer* g_pConstantBuffer = nullptr;
XMMATRIX g_World;
XMMATRIX g_View;
XMMATRIX g_Projection;
//--------------------------------------------------------------------------------------
// Forward declarations
//--------------------------------------------------------------------------------------
HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow);
HRESULT InitDevice();
void CleanupDevice();
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Render();
//--------------------------------------------------------------------------------------
// Entry point to the program. Initializes everything and goes into a message processing
// loop. Idle time is used to render the scene.
//--------------------------------------------------------------------------------------
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
if (FAILED(InitWindow(hInstance, nCmdShow)))
{
return 0;
}
if (FAILED(InitDevice()))
{
CleanupDevice();
return 0;
}
MSG msg = { nullptr };
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Render();
}
}
CleanupDevice();
return static_cast<int>(msg.wParam);
}
HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow)
{
// Register Window class
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = reinterpret_cast<HBRUSH>((COLOR_WINDOW + 1));
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = L"TutorialWindowClass";
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
{
return E_FAIL;
}
g_hInstance = hInstance;
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
g_hWnd = CreateWindowEx(NULL,
L"TutorialWindowClass",
L"Direct3D 11 Tutorial 1: Direct3D 11 Basics",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
rc.right - rc.left,
rc.bottom - rc.top,
nullptr,
nullptr,
hInstance,
nullptr);
if (g_hWnd == nullptr)
{
return E_FAIL;
}
ShowWindow(g_hWnd, nCmdShow);
return S_OK;
}
HRESULT CompileShaderFromFile(LPCTSTR szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
{
HRESULT hr = S_OK;
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR;
#if defined( DEBUG ) || defined( _DEBUG )
// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
// Setting this flag improves the shader debugging experience, but still allows
// the shaders to be optimized and to run exactly the way they will run in
// the release configuration of this program.
dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif
ID3DBlob* pErrorBlob;
hr = D3DCompileFromFile(
szFileName, // 파일명
nullptr, // 매크로 정의
nullptr, // Include 파일 정의
szEntryPoint, // 셰이더 Entry point 이름(메인 함수 이름)
szShaderModel, // 셰이더 컴파일 버전(vs_4_0 = Vertex shader version 4.0)
dwShaderFlags, // 컴파일 옵션
0, // 이펙트 컴파일 옵션
ppBlobOut, // 컴파일 된 Byte코드
&pErrorBlob // 에러 메세지
);
if (FAILED(hr))
{
if (pErrorBlob != nullptr)
{
OutputDebugStringA(static_cast<LPCSTR>(pErrorBlob->GetBufferPointer()));
}
else
{
pErrorBlob->Release();
}
return hr;
}
if (pErrorBlob)
{
pErrorBlob->Release();
}
return S_OK;
}
HRESULT InitDevice()
{
HRESULT hr = S_OK;
RECT rc;
GetClientRect(g_hWnd, &rc);
const UINT width = rc.right - rc.left;
const UINT height = rc.bottom - rc.top;
UINT createDeviceFlags = 0;
#ifdef _DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
constexpr UINT numDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
constexpr UINT numFeatureLevels = ARRAYSIZE(featureLevels);
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(DXGI_SWAP_CHAIN_DESC));
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;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = g_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
{
g_driverType = driverTypes[driverTypeIndex];
hr = D3D11CreateDeviceAndSwapChain(
nullptr, // device를 생성하기 위한 video adapter(nullptr = default adapter)
g_driverType, // driver type
nullptr, // software handle
createDeviceFlags, // device flag
featureLevels, // feature level array
numFeatureLevels, // feature level array length
D3D11_SDK_VERSION, // sdk version
&sd, // swap chain structure
&g_pSwapChain, // 생성된 swap chain
&g_pDevice, // 생성된 device
&g_featureLevel, // 생성된 feature level
&g_pImmediateContext); // 생성된 device context
if (SUCCEEDED(hr))
{
break;
}
}
if (FAILED(hr))
{
return hr;
}
ID3D11Texture2D* pBackBuffer = nullptr;
hr = g_pSwapChain->GetBuffer(
0, // 백버퍼 index
__uuidof(ID3D11Texture2D), // back buffer를 다루기 위한 interface
reinterpret_cast<LPVOID*>(&pBackBuffer) // back buffer interface output
);
if (FAILED(hr))
{
return hr;
}
hr = g_pDevice->CreateRenderTargetView(
pBackBuffer, // View에서 접근할 리소스
nullptr, // RTV 정의
&g_pRenterTargetView); // RTV를 받아올 변수
// 사용한 back buffer를 Release
pBackBuffer->Release();
if (FAILED(hr))
{
return hr;
}
g_pImmediateContext->OMSetRenderTargets(
1, // Render target의 개수(최대 8개)
&g_pRenterTargetView, // Render target view의 배열
nullptr // Depth stencil view 포인터
);
D3D11_VIEWPORT vp;
vp.Width = static_cast<FLOAT>(width);
vp.Height = static_cast<FLOAT>(height);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pImmediateContext->RSSetViewports(1, &vp);
// compile the vertex shader
ID3DBlob* pVSBlob = nullptr;
hr = CompileShaderFromFile(L"Tutorial04.fx", "VS", "vs_4_0", &pVSBlob);
if (FAILED(hr))
{
MessageBox(nullptr, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.",
L"Error", MB_OK);
return hr;
}
// create the vertex shader
hr = g_pDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &g_pVertexShader);
if (FAILED(hr))
{
pVSBlob->Release();
return hr;
}
// define the input layout (정점 데이터를 GPU에게 알려주는 구조체)
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{"POSITION", // Element의 목적이 무엇인이 알려주는 문자열 (POSITION, NORMAL, ...)
0, // 동일한 Element의 목적을 가진 정점의 Index
DXGI_FORMAT_R32G32B32_FLOAT, // 자료형
0, // 정점 버퍼 슬롯 index
0, // 정점 버퍼 offset
D3D11_INPUT_PER_VERTEX_DATA, // D3D11_INPUT_PER_VERTEX_DATA를 사용함.
0 // Instancing에 사용됨
},
{
"COLOR",
0,
DXGI_FORMAT_R32G32B32A32_FLOAT,
0,
12,
D3D11_INPUT_PER_VERTEX_DATA,
0
}
};
constexpr UINT numElements = ARRAYSIZE(layout);
// create the input layout
hr = g_pDevice->CreateInputLayout(layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout);
pVSBlob->Release();
if (FAILED(hr))
{
return hr;
}
// set the input layout
g_pImmediateContext->IASetInputLayout(g_pVertexLayout);
// compile the pixel shader
ID3DBlob* pPSBlob = nullptr;
hr = CompileShaderFromFile(L"Tutorial04.fx", "PS", "ps_4_0", &pPSBlob);
if (FAILED(hr))
{
MessageBox(nullptr, L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.",
L"Error", MB_OK);
return hr;
}
// create the pixel shader
hr = g_pDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &g_pPixelShader);
pPSBlob->Release();
if (FAILED(hr))
{
return hr;
}
// create vertex buffer
constexpr SimpleVertex vertices[] =
{
{ XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT4(0.0f, 1.0f, 1.0f, 1.0f) },
{ XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
{ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(1.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f) },
{ XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f) },
};
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(D3D11_BUFFER_DESC));
bd.Usage = D3D11_USAGE_DEFAULT; // default로 사용
bd.ByteWidth = sizeof(SimpleVertex) * 8; // vertex
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; // vertex buffer로 바인드
bd.CPUAccessFlags = 0; // cpu access하지 않음.
D3D11_SUBRESOURCE_DATA initData;
ZeroMemory(&initData, sizeof(D3D11_SUBRESOURCE_DATA));
initData.pSysMem = vertices; // 버퍼 데티
hr = g_pDevice->CreateBuffer(&bd, &initData, &g_pVertexBuffer);
if (FAILED(hr))
{
return hr;
}
// set vertex buffer
UINT stride = sizeof(SimpleVertex);
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);
// Create index buffer
WORD indices[] =
{
3,1,0,
2,1,3,
0,5,4,
1,5,0,
3,4,7,
0,4,3,
1,6,5,
2,6,1,
2,7,6,
3,7,2,
6,4,5,
7,4,6,
};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(WORD) * 36;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
initData.pSysMem = indices;
hr = g_pDevice->CreateBuffer(&bd, &initData, &g_pIndexBuffer);
if (FAILED(hr))
{
return hr;
}
g_pImmediateContext->IASetIndexBuffer(g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
// set primitive topology
g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(ConstantBuffer);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
hr = g_pDevice->CreateBuffer(&bd, nullptr, &g_pConstantBuffer);
if (FAILED(hr))
{
return hr;
}
g_World = XMMatrixIdentity();
XMVECTOR eye = XMVectorSet(0.0f, 1.0f, -5.0f, 0.0f);
XMVECTOR at = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
g_View = XMMatrixLookAtLH(eye, at, up);
g_Projection = XMMatrixPerspectiveFovLH(XM_PIDIV2, static_cast<float>(width) / height, 0.01f, 100.0f);
return S_OK;
}
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()
{
// Update our time
static float t = 0.0f;
if (g_driverType == D3D_DRIVER_TYPE_REFERENCE)
{
t += static_cast<float>(XM_PI) * 0.0125f;
}
else
{
static DWORD dwTimeStart = 0;
const DWORD dwTimeCur = GetTickCount();
if (dwTimeStart == 0)
dwTimeStart = dwTimeCur;
t = (dwTimeCur - dwTimeStart) / 1000.0f;
}
g_World = XMMatrixRotationY(t);
constexpr float clearColor[4] = { 0.0f, 0.125f,0.3f,1.0f };
g_pImmediateContext->ClearRenderTargetView(g_pRenterTargetView, clearColor);
ConstantBuffer cb;
cb.mWorld = XMMatrixTranspose(g_World);
cb.mView = XMMatrixTranspose(g_View);
cb.mProjection = XMMatrixTranspose(g_Projection);
g_pImmediateContext->UpdateSubresource(g_pConstantBuffer, 0, nullptr, &cb, 0, 0);
g_pImmediateContext->VSSetShader(g_pVertexShader, nullptr, 0);
g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pConstantBuffer);
g_pImmediateContext->PSSetShader(g_pPixelShader, nullptr, 0);
g_pImmediateContext->DrawIndexed(36, 0, 0);
g_pSwapChain->Present(0, 0);
}
void CleanupDevice()
{
if (g_pImmediateContext)
{
g_pImmediateContext->ClearState();
}
if (g_pConstantBuffer)
{
g_pConstantBuffer->Release();
}
if (g_pVertexBuffer)
{
g_pVertexBuffer->Release();
}
if (g_pVertexLayout)
{
g_pVertexLayout->Release();
}
if (g_pVertexShader)
{
g_pVertexShader->Release();
}
if (g_pPixelShader)
{
g_pPixelShader->Release();
}
if (g_pRenterTargetView)
{
g_pRenterTargetView->Release();
}
if (g_pSwapChain)
{
g_pSwapChain->Release();
}
if (g_pImmediateContext)
{
g_pImmediateContext->Release();
}
if (g_pDevice)
{
g_pDevice->Release();
}
}
cbuffer ConstantBuffer : register(b0)
{
matrix World;
matrix View;
matrix Projection;
}
struct VS_OUTPUT
{
float4 Pos : SV_Position;
float4 Color : COLOR0;
};
VS_OUTPUT VS(float4 Pos : POSITION, float4 Color : COLOR)
{
VS_OUTPUT output = (VS_OUTPUT) 0;
output.Pos = mul(Pos, World);
output.Pos = mul(output.Pos, View);
output.Pos = mul(output.Pos, Projection);
output.Color = Color;
return output;
}
float4 PS(VS_OUTPUT input) : SV_Target
{
return input.Color;
}