接下來要做的事情就是把前面介紹的內容串起來,組成三角形
main.cpp
#include <cstdlib>
#include <directx/d3dx12_core.h>
#include <exception>
#include <stdexcept>
#include "graphics_engine.h"
#include "my_engine.h"
#include "skyline_debugger.h"
#include "system.h"
namespace
{
// 定義三角形單一頂點的位置資料
struct SimpleVertex
{
float x, y, z;
};
// 初始化三角形使用的 Root Signature
void initRootSignature(RootSignature & rs)
{
rs.init(D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_WRAP);
}
// 建立繪製三角形需要的 Graphics Pipeline State 描述
D3D12_GRAPHICS_PIPELINE_STATE_DESC createTrianglePipelineStateDescription(
ID3D12RootSignature * rootSignature, ID3DBlob * vertexShader, ID3DBlob * pixelShader)
{
// 確認有傳入有效的 Root Signature
if (rootSignature == nullptr)
throw std::invalid_argument("TrianglePipeline: Root signature is required.");
// 確認有傳入有效的 Vertex Shader
if (vertexShader == nullptr)
throw std::invalid_argument("TrianglePipeline: Vertex shader is required.");
// 確認有傳入有效的 Pixel Shader
if (pixelShader == nullptr)
throw std::invalid_argument("TrianglePipeline: Pixel shader is required.");
// 定義 Vertex Buffer 中 POSITION 資料的格式
static constexpr D3D12_INPUT_ELEMENT_DESC inputElementDescriptions[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};
// 建立並初始化 Graphics Pipeline State 描述
D3D12_GRAPHICS_PIPELINE_STATE_DESC description{};
// 設定 Vertex Shader 使用的 Input Layout
description.InputLayout = {inputElementDescriptions, _countof(inputElementDescriptions)};
// 設定 Pipeline 使用的 Root Signature
description.pRootSignature = rootSignature;
// 設定 Vertex Shader Bytecode
description.VS = CD3DX12_SHADER_BYTECODE(vertexShader);
// 設定 Pixel Shader Bytecode
description.PS = CD3DX12_SHADER_BYTECODE(pixelShader);
// 使用 Direct3D 12 預設 Rasterizer 設定
description.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
// 關閉三角形面的 Cull
description.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
// 使用 Direct3D 12 預設 Blend 設定
description.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
// 使用 Direct3D 12 預設 Depth Stencil 設定
description.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
// 關閉 Depth Test
description.DepthStencilState.DepthEnable = FALSE;
// 禁止寫入 Depth Buffer
description.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
// 關閉 Stencil Test
description.DepthStencilState.StencilEnable = FALSE;
// 允許所有 Multisampling Sample 被處理
description.SampleMask = UINT_MAX;
// 指定 Pipeline 使用 Triangle 類型的 Primitive
description.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
// 指定同時使用一個 Render Target
description.NumRenderTargets = 1;
// 設定第一個 Render Target 的像素格式
description.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
// 設定 Depth Stencil Buffer 的格式
description.DSVFormat = DXGI_FORMAT_D32_FLOAT;
// 設定每個 Pixel 只使用一個 Sample
description.SampleDesc.Count = 1;
// 回傳設定完成的 Graphics Pipeline State 描述
return description;
}
// 建立並初始化三角形使用的 Pipeline State
void initPipelineState(PipelineState & pipelineState, RootSignature & rs, Shader & vs, Shader & ps)
{
// 使用 Root Signature 與 Shader 建立 Graphics Pipeline State 描述
const D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc =
createTrianglePipelineStateDescription(rs.get(), vs.getCompiledBlob(), ps.getCompiledBlob());
// 使用建立好的描述初始化 Pipeline State
pipelineState.init(psoDesc);
}
}
// Windows 應用程式的主進入點
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
try
{
// 初始化 Skyline Debugger
SkylineDebugger::Initialize("DayX_Triangle");
// 建立應用程式視窗
initWindow(hInstance, hPrevInstance, lpCmdLine, nCmdShow, TEXT("Game"));
// 確認應用程式視窗建立成功
if (g_hWnd == nullptr)
throw std::runtime_error("DayX_Triangle: Failed to create the application window.");
// 建立 Graphics Engine
GraphicsEngine graphicsEngine;
// 使用視窗與 Frame Buffer 大小初始化 Graphics Engine
graphicsEngine.init(g_hWnd, FRAME_BUFFER_W, FRAME_BUFFER_H);
// 將 D3D12 Device 設定給 Skyline Debugger
SkylineDebugger::ConfigureD3D12(graphicsEngine.getD3DDevice());
// 建立 Root Signature
RootSignature rootSignature;
// 初始化三角形使用的 Root Signature
initRootSignature(rootSignature);
// 建立 Vertex Shader 與 Pixel Shader 物件
Shader vs, ps;
// 編譯 triangle.hlsl 中的 VSMain
vs.loadVS("assets/shaders/triangle.hlsl", "VSMain");
// 編譯 triangle.hlsl 中的 PSMain
ps.loadPS("assets/shaders/triangle.hlsl", "PSMain");
// 建立 Pipeline State
PipelineState pipelineState;
// 初始化三角形使用的 Graphics Pipeline State
initPipelineState(pipelineState, rootSignature, vs, ps);
// 建立三角形的三個頂點位置
SimpleVertex vertices[] = {
{-0.5f, -0.5f, 0.0f},
{0.0f, 0.5f, 0.0f},
{0.5f, -0.5f, 0.0f},
};
// 建立三角形使用的 Vertex Buffer
VertexBuffer triangleVB;
// 依照頂點數量與單一頂點大小初始化 Vertex Buffer
triangleVB.init(_countof(vertices), sizeof(vertices[0]));
// 將三角形頂點資料複製到 Vertex Buffer
triangleVB.copy(vertices);
// 定義三角形使用的三個 Index
uint16_t indices[] = {0, 1, 2};
// 建立三角形使用的 Index Buffer
IndexBuffer triangleIB;
// 依照 Index 數量與單一 Index 大小初始化 Index Buffer
triangleIB.init(_countof(indices), sizeof(indices[0]));
// 將三角形 Index 資料複製到 Index Buffer
triangleIB.copy(indices);
// 取得 Graphics Engine 使用的 Render Context
RenderContext& renderContext = graphicsEngine.getRenderContext();
// 將目前 Graphics Engine 的 Command List 設定給 Render Context
renderContext.SetCommandList(graphicsEngine.getCommandList());
// 持續處理視窗訊息並執行每一幀的繪製
while (dispatchWindowMessage())
{
// 開始目前 Frame 的 Rendering
graphicsEngine.beginRender();
// 設定 Graphics Pipeline 使用的 Root Signature
renderContext.setRootSignature(rootSignature);
// 設定目前使用的 Graphics Pipeline State
renderContext.setPipelineState(pipelineState);
// 設定 Input Assembler 使用 Triangle List
renderContext.setPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 將三角形 Vertex Buffer 綁定到 Input Assembler
renderContext.setVertexBuffer(triangleVB);
// 將三角形 Index Buffer 綁定到 Input Assembler
renderContext.setIndexBuffer(triangleIB);
// 使用三個 Index 繪製三角形
renderContext.drawIndexed(3);
// 結束目前 Frame 並提交繪製結果
graphicsEngine.endRender();
// 將 Direct3D 12 Debug Layer 訊息輸出到 Debugger
SkylineDebugger::LogD3D12Messages(graphicsEngine.getD3DDevice());
}
// 關閉 Skyline Debugger
SkylineDebugger::Shutdown();
// 程式正常結束
return EXIT_SUCCESS;
}
catch (const std::exception& exception)
{
// 顯示標準例外造成的致命錯誤
SkylineDebugger::ShowFatalError("DayX_Triangle initialization failed", exception);
// 關閉 Skyline Debugger
SkylineDebugger::Shutdown();
// 回傳程式執行失敗
return EXIT_FAILURE;
}
catch (...)
{
SkylineDebugger::ShowFatalError("DayX_Triangle initialization failed", "An unknown fatal error occurred.");
SkylineDebugger::Shutdown();
return EXIT_FAILURE;
}
}
最後要輸出三角形前,需要設定對應的 shader,之後會介紹 HLSL 的語法,這邊先簡單帶過
//triangle.hlsl
// 定義 Vertex Shader 接收的輸入資料
struct VSInput
{
float4 position : POSITION; // 接收 Vertex Buffer 傳入的頂點位置
};
// 定義 Vertex Shader 傳出的輸出資料
struct VSOutput
{
float4 position : SV_POSITION; // 輸出頂點經過 Vertex Shader 處理後的位置
};
// 接收頂點資料並輸出處理後的位置
VSOutput VSMain(VSInput input)
{
// 建立 VSOutput 並將所有欄位初始化為 0
VSOutput vsOut = (VSOutput) 0;
// 將輸入的頂點位置直接傳給輸出位置
vsOut.position = input.position;
// 回傳處理完成的 Vertex Shader 輸出資料
return vsOut;
}
// 接收 Vertex Shader 輸出並決定 Pixel 的最終顏色
float4 PSMain(VSOutput vsOut) : SV_Target0
{
// 輸出不透明的紅色
return float4(1.0f, 0.0f, 0.0f, 1.0f);
}
整個流程是這種感覺
Win32 Window
│
▼
GraphicsEngine::init()
│
├─ DXGI Factory
├─ D3D12 Device
├─ Command Queue
├─ Command Allocator
├─ Command List
├─ Swap Chain
├─ RTV / Back Buffer
├─ DSV / Depth Buffer
└─ Fence
│
▼
Root Signature
│
▼
VS / PS
│
▼
Pipeline State Object
│
▼
Vertex Buffer + Index Buffer
│
▼
Main Loop
│
├─ beginRender()
│
├─ Set Root Signature
├─ Set PSO
├─ Set Primitive Topology
├─ Set Vertex Buffer
├─ Set Index Buffer
├─ DrawIndexed()
│
└─ endRender()
│
├─ Close Command List
├─ ExecuteCommandLists()
├─ Present()
└─ Fence

DirectX 12の魔導書 3Dレンダリングの基礎からMMDモデルを踊らせるまで