准备工作:
开始搭建框架之前,你需要确保已经进行了 D3D 开发环境的搭建,相关教程可以阅读这篇文章。不了解 DirectX11 的人,这个作者有关 DirectX11 的教程最好阅读一下,虽然文章不多,但都很详细,有了基础以后在进行深一步的扩展。
和 OpenGL 一样,在渲染出图形之前,都需要经过很多步骤(窗口配置、图形上下文的创建、顶点数据配置、着色器的配置、变换矩阵配置等等),不是一两行代码就可以了。而 DirectX11 则更为复杂,其中如果发生一点错误,将导致图形渲染失败,但你难以检测发生的错误。对于初学者,在你辛辛苦苦编写完渲染代码后,却出现显示不出图形但又不知道哪里出错的情况,那你又能怎么办呢?
下面给出一个基本的渲染框架(能够渲染出一个旋转正方体),通过在这个框架上进行扩展,开发 MMDViewer 项目。而不是自己编写一个可能有错误的框架,这样会浪费大量时间。
这个框架并不是最简单的框架,但它包括了渲染管线的大部分内容:着色器设置、MVP 变换矩阵设置、裁剪矩形区域(在 GUI 项目常用)、光栅化状态设置、模板测试、深度测试、颜色混合(支持 alpha 通道)。
#include <windows.h> #include <string> /* 引入 D3D 头文件 */ #include <D3D11.h> #include <D3DX11.h> #include <D3Dcompiler.h> #include <xnamath.h> #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d3dx10.lib") #pragma comment(lib, "d3dx11.lib") #pragma comment(lib, "dxguid.lib") #pragma comment(lib, "d3dcompiler.lib") //-------------------------------------------------------------------------------------- // Structures //-------------------------------------------------------------------------------------- struct SimpleVertex { XMFLOAT3 Pos; XMFLOAT4 Color; }; struct ConstantBuffer { XMMATRIX mWorld; XMMATRIX mView; XMMATRIX mProjection; }; //-------------------------------------------------------------------------------------- // Global Variables //-------------------------------------------------------------------------------------- HINSTANCE g_hInst = NULL; HWND g_hWnd = NULL; D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL; D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0; ID3D11Device* g_pd3dDevice = NULL; ID3D11DeviceContext* g_pImmediateContext = NULL; IDXGISwapChain* g_pSwapChain = NULL; ID3D11RenderTargetView* g_pRenderTargetView = NULL; ID3D11DepthStencilView* g_pDepthStencilView = NULL; ID3D11VertexShader* g_pVertexShader = NULL; ID3D11PixelShader* g_pPixelShader = NULL; ID3D11InputLayout* g_pVertexLayout = NULL; ID3D11Buffer* g_pVertexBuffer = NULL; ID3D11Buffer* g_pIndexBuffer = NULL; ID3D11Buffer* g_pConstantBuffer = NULL; XMMATRIX g_World; XMMATRIX g_View; XMMATRIX g_Projection; //-------------------------------------------------------------------------------------- // Helper for compiling shaders with D3DX11 //-------------------------------------------------------------------------------------- HRESULT CompileShaderFromFile(char* fiel_name, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut) { HRESULT hr = S_OK; DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; #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 = D3DX11CompileFromFileA(fiel_name, NULL, NULL, szEntryPoint, szShaderModel, dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL); if ( FAILED(hr) ) { if ( pErrorBlob != NULL ) { OutputDebugStringA(( char* ) pErrorBlob->GetBufferPointer()); } if ( pErrorBlob ) pErrorBlob->Release(); return hr; } if ( pErrorBlob ) pErrorBlob->Release(); return S_OK; } //-------------------------------------------------------------------------------------- // Forward declarations //-------------------------------------------------------------------------------------- HRESULT InitDevice() { HRESULT hr = S_OK; RECT rc; GetClientRect(g_hWnd, &rc); UINT width = rc.right - rc.left; UINT height = rc.bottom - rc.top; //---------------------------------------------------------------------------- // 渲染管线设置,创建设备、设备上下文和交换链 //---------------------------------------------------------------------------- /* 设置调试标记,程序退出时也可以显示所有对象的引用数量,可以用于内存泄露检测 */ UINT create_device_flags = 0; #ifdef _DEBUG create_device_flags |= D3D11_CREATE_DEVICE_DEBUG; #endif D3D_DRIVER_TYPE driver_types[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE, }; UINT num_driver_types = ARRAYSIZE(driver_types); D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, }; UINT numFeatureLevels = ARRAYSIZE(featureLevels); DXGI_SWAP_CHAIN_DESC swap_desc; ZeroMemory(&swap_desc, sizeof(swap_desc)); swap_desc.BufferCount = 1; swap_desc.BufferDesc.Width = width; swap_desc.BufferDesc.Height = height; swap_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_desc.BufferDesc.RefreshRate.Numerator = 60; swap_desc.BufferDesc.RefreshRate.Denominator = 1; swap_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_desc.OutputWindow = g_hWnd; swap_desc.SampleDesc.Count = 1; swap_desc.SampleDesc.Quality = 0; swap_desc.Windowed = TRUE; for ( UINT driver_type_index = 0; driver_type_index < num_driver_types; driver_type_index++ ) { g_driverType = driver_types[driver_type_index]; hr = D3D11CreateDeviceAndSwapChain(NULL, g_driverType, NULL, create_device_flags, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &swap_desc, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext); if ( SUCCEEDED(hr) ) break; } if ( FAILED(hr) ) return hr; //---------------------------------------------------------------------------- // 渲染管线输出设置 //---------------------------------------------------------------------------- /* 获取交换链的后缓冲 */ ID3D11Texture2D* pBackBuffer = NULL; hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), ( LPVOID* ) &pBackBuffer); if ( FAILED(hr) ) return hr; /* 创建渲染目标视图 */ hr = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView); pBackBuffer->Release(); if ( FAILED(hr) ) return hr; /* 设置视口 */ D3D11_VIEWPORT view_port; view_port.Width = ( FLOAT ) width; view_port.Height = ( FLOAT ) height; view_port.MinDepth = 0.0f; view_port.MaxDepth = 1.0f; view_port.TopLeftX = 0; view_port.TopLeftY = 0; g_pImmediateContext->RSSetViewports(1, &view_port); //---------------------------------------------------------------------------- // 渲染管线输入数据 //---------------------------------------------------------------------------- /* 顶点缓冲区 */ SimpleVertex vertices[] = { { XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }, { XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }, { XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }, { XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) }, { XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) }, { XMFLOAT3(0.5f, 0.5f, -0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) }, { XMFLOAT3(0.5f, -0.5f, -0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) }, { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) } }; D3D11_BUFFER_DESC vertex_desc; vertex_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertex_desc.ByteWidth = sizeof( SimpleVertex ) * 8; vertex_desc.CPUAccessFlags = 0; vertex_desc.MiscFlags = 0; vertex_desc.StructureByteStride = 0; vertex_desc.Usage = D3D11_USAGE_DEFAULT; D3D11_SUBRESOURCE_DATA vertex_data; vertex_data.pSysMem = vertices; vertex_data.SysMemPitch = 0; vertex_data.SysMemSlicePitch = 0; hr = g_pd3dDevice->CreateBuffer(&vertex_desc, &vertex_data, &g_pVertexBuffer); if ( FAILED(hr) ) return hr; /* 索引缓冲区 */ UINT indices[] = { 7, 4, 5, 7, 5, 6, // 前面 4, 0, 1, 4, 1, 5, // 上面 3, 7, 6, 3, 6, 2, // 下面 2, 1, 0, 2, 0, 3, // 后面 6, 5, 1, 6, 1, 2, // 右面 3, 0, 4, 3, 4, 7 // 左面 }; D3D11_BUFFER_DESC index_desc; index_desc.BindFlags = D3D11_BIND_INDEX_BUFFER; index_desc.ByteWidth = sizeof( UINT ) * 36; index_desc.MiscFlags = 0; index_desc.CPUAccessFlags = 0; index_desc.StructureByteStride = 0; index_desc.Usage = D3D11_USAGE_DEFAULT; D3D11_SUBRESOURCE_DATA index_data; index_data.pSysMem = indices; index_data.SysMemPitch = 0; index_data.SysMemSlicePitch = 0; hr = g_pd3dDevice->CreateBuffer(&index_desc, &index_data, &g_pIndexBuffer); if ( FAILED(hr) ) return hr; /* 设置顶点缓冲区 */ UINT stride = sizeof(SimpleVertex); UINT offset = 0; g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset); g_pImmediateContext->IASetIndexBuffer(g_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0); /* 设置图元 */ g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); //---------------------------------------------------------------------------- // 着色器设置 //---------------------------------------------------------------------------- /* 编译顶点着色器 */ ID3DBlob* pVSBlob = NULL; hr = CompileShaderFromFile("shader.hlsl", "VS", "vs_5_0", &pVSBlob); if ( FAILED(hr) ) { MessageBox(NULL, L"不能编译着色程序", L"Error", MB_OK); return hr; } /* 创建顶点着色器 */ hr = g_pd3dDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &g_pVertexShader); if ( FAILED(hr) ) { pVSBlob->Release(); return hr; } D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = ARRAYSIZE(layout); /* 创建输入布局 */ hr = g_pd3dDevice->CreateInputLayout(layout, numElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout); pVSBlob->Release(); if ( FAILED(hr) ) return hr; /* 设置输入布局 */ g_pImmediateContext->IASetInputLayout(g_pVertexLayout); /* 编译像素着色器 */ ID3DBlob* pPSBlob = NULL; hr = CompileShaderFromFile("shader.hlsl", "PS", "ps_5_0", &pPSBlob); if ( FAILED(hr) ) { MessageBox(NULL, L"不能编译着色程序", L"Error", MB_OK); return hr; } /* 创建像素着色器 */ hr = g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShader); pPSBlob->Release(); if ( FAILED(hr) ) return hr; //---------------------------------------------------------------------------- // 光栅化状态 //---------------------------------------------------------------------------- ID3D11RasterizerState* rasterater_state = nullptr; D3D11_RASTERIZER_DESC rasterizer_desc; ZeroMemory(&rasterizer_desc, sizeof(rasterizer_desc)); rasterizer_desc.CullMode = D3D11_CULL_BACK; // 背面剔除 rasterizer_desc.FillMode = D3D11_FILL_SOLID; // 填充由顶点形成的三角形 rasterizer_desc.ScissorEnable = true; // 开启裁剪 rasterizer_desc.FrontCounterClockwise = false; // 顺时针为正方向 hr = g_pd3dDevice->CreateRasterizerState(&rasterizer_desc, &rasterater_state); if ( FAILED(hr) ) return hr; g_pImmediateContext->RSSetState(rasterater_state); rasterater_state->Release(); /* 设置裁剪矩形,这里仅设置和窗口大小一样,所以没有效果 */ D3D11_RECT scissor = { 0 /* left */, 0 /* top */, 800 /* right */, 600 /* bottom */ }; g_pImmediateContext->RSSetScissorRects(1, &scissor); //---------------------------------------------------------------------------- // 模板测试和深度测试 //---------------------------------------------------------------------------- ID3D11Texture2D* depth_stencil_tex = nullptr; D3D11_TEXTURE2D_DESC depth_stencil_tex_desc; ZeroMemory(&depth_stencil_tex_desc, sizeof(depth_stencil_tex_desc)); depth_stencil_tex_desc.Width = width; depth_stencil_tex_desc.Height = height; depth_stencil_tex_desc.MipLevels = 1; depth_stencil_tex_desc.ArraySize = 1; depth_stencil_tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depth_stencil_tex_desc.SampleDesc.Count = 1; depth_stencil_tex_desc.SampleDesc.Quality = 0; depth_stencil_tex_desc.Usage = D3D11_USAGE_DEFAULT; depth_stencil_tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depth_stencil_tex_desc.MiscFlags = 0; depth_stencil_tex_desc.CPUAccessFlags = 0; hr = g_pd3dDevice->CreateTexture2D(&depth_stencil_tex_desc, 0, &depth_stencil_tex); if ( FAILED(hr) ) return hr; hr = g_pd3dDevice->CreateDepthStencilView(depth_stencil_tex, 0, &g_pDepthStencilView); if ( FAILED(hr) ) return hr; /* 设置深度、模板视图到渲染管线 */ g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView); depth_stencil_tex->Release(); /* 设置深度、模板状态到渲染管线 */ ID3D11DepthStencilState* depth_stencil_state = nullptr; D3D11_DEPTH_STENCIL_DESC depth_stencil_desc; ZeroMemory(&depth_stencil_desc, sizeof(depth_stencil_desc)); depth_stencil_desc.DepthEnable = TRUE; depth_stencil_desc.DepthFunc = D3D11_COMPARISON_FUNC::D3D11_COMPARISON_LESS; depth_stencil_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK::D3D11_DEPTH_WRITE_MASK_ALL; depth_stencil_desc.StencilEnable = FALSE; depth_stencil_desc.FrontFace.StencilFunc = D3D11_COMPARISON_FUNC::D3D11_COMPARISON_ALWAYS; depth_stencil_desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_KEEP; depth_stencil_desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_KEEP; depth_stencil_desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_KEEP; depth_stencil_desc.BackFace = depth_stencil_desc.FrontFace; depth_stencil_desc.StencilReadMask = 0xFF; depth_stencil_desc.StencilWriteMask = 0xFF; hr = g_pd3dDevice->CreateDepthStencilState(&depth_stencil_desc, &depth_stencil_state); if ( FAILED(hr) ) return hr; g_pImmediateContext->OMSetDepthStencilState(depth_stencil_state, 1); depth_stencil_state->Release(); //---------------------------------------------------------------------------- // 颜色混合 //---------------------------------------------------------------------------- D3D11_BLEND_DESC blend_desc; ZeroMemory(&blend_desc, sizeof(blend_desc)); blend_desc.AlphaToCoverageEnable = false; blend_desc.IndependentBlendEnable = false; // 是否使用多个目标渲染视图 blend_desc.RenderTarget[0].BlendEnable = true; blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP::D3D11_BLEND_OP_ADD; blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP::D3D11_BLEND_OP_ADD; blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; ID3D11BlendState* blend_state = nullptr; hr = g_pd3dDevice->CreateBlendState(&blend_desc, &blend_state); if ( FAILED(hr) ) return hr; const float blend_factor[4] = { 1, 1, 1, 1 }; g_pImmediateContext->OMSetBlendState(blend_state, blend_factor, 0xFFFFFFFF); blend_state->Release(); //---------------------------------------------------------------------------- // 视图矩阵和投影矩阵 //---------------------------------------------------------------------------- D3D11_BUFFER_DESC constant_desc; constant_desc.Usage = D3D11_USAGE_DEFAULT; constant_desc.ByteWidth = sizeof(ConstantBuffer); constant_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; constant_desc.CPUAccessFlags = 0; constant_desc.MiscFlags = 0; constant_desc.StructureByteStride = 0; hr = g_pd3dDevice->CreateBuffer(&constant_desc, NULL, &g_pConstantBuffer); if ( FAILED(hr) ) return hr; /* 初始化世界矩阵 */ g_World = XMMatrixIdentity(); /* 初始化视图矩阵 */ XMVECTOR eye = XMVectorSet(1.0f, 1.0f, -1.5f, 0.0f); XMVECTOR at = XMVectorSet(0.0f, 0.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, width / ( FLOAT ) height, 0.01f, 100.0f); return S_OK; } void CleanupDevice() { if ( g_pImmediateContext ) g_pImmediateContext->ClearState(); if ( g_pVertexBuffer ) g_pVertexBuffer->Release(); if ( g_pIndexBuffer ) g_pIndexBuffer->Release(); if ( g_pVertexLayout ) g_pVertexLayout->Release(); if ( g_pVertexShader ) g_pVertexShader->Release(); if ( g_pPixelShader ) g_pPixelShader->Release(); if ( g_pConstantBuffer ) g_pConstantBuffer->Release(); if ( g_pRenderTargetView ) g_pRenderTargetView->Release(); if ( g_pDepthStencilView ) g_pDepthStencilView->Release(); if ( g_pSwapChain ) g_pSwapChain->Release(); if ( g_pImmediateContext ) g_pImmediateContext->Release(); if ( g_pd3dDevice ) g_pd3dDevice->Release(); } void Render() { //---------------------------------------------------------------------------- // 旋转正方体 //---------------------------------------------------------------------------- /* 更新时间 */ static float t = 0.0f; if ( g_driverType == D3D_DRIVER_TYPE_REFERENCE ) { t += ( float ) XM_PI * 0.0125f; } else { static DWORD dwTimeStart = 0; DWORD dwTimeCur = GetTickCount(); if ( dwTimeStart == 0 ) { dwTimeStart = dwTimeCur; } t = (dwTimeCur - dwTimeStart) / 1000.0f; } /* 更新世界矩阵,使正方体旋转 */ g_World = XMMatrixRotationY(t); //---------------------------------------------------------------------------- // 渲染 //---------------------------------------------------------------------------- /* 清空后缓冲 */ float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, ClearColor); g_pImmediateContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); /* 更新常量缓冲区数据 */ ConstantBuffer cb; cb.mView = XMMatrixTranspose(g_View); cb.mWorld = XMMatrixTranspose(g_World); cb.mProjection = XMMatrixTranspose(g_Projection); g_pImmediateContext->UpdateSubresource(g_pConstantBuffer, 0, NULL, &cb, 0, 0); /* 渲染三角形 */ g_pImmediateContext->VSSetShader(g_pVertexShader, NULL, 0); g_pImmediateContext->PSSetShader(g_pPixelShader, NULL, 0); g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pConstantBuffer); g_pImmediateContext->DrawIndexed(36, 0, 0); g_pSwapChain->Present(1, 0); } /* 窗口事件处理回调函数 */ LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch ( msg ) { case WM_CLOSE: case WM_DESTROY: PostQuitMessage(0); break; case WM_PAINT: RECT rect; if ( GetUpdateRect(wnd, &rect, FALSE) ) { ValidateRect(wnd, &rect); } break; } return DefWindowProc(wnd, msg, wParam, lParam); } /* 创建窗口并返回句柄 */ HWND Create() { /* 设计窗口类 */ WNDCLASS wndclass; memset(&wndclass, 0, sizeof(WNDCLASSA)); wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; wndclass.lpfnWndProc = ( WNDPROC ) WindowProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = ( HINSTANCE ) GetModuleHandle(0); wndclass.hIcon = 0; wndclass.hCursor = 0; wndclass.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); wndclass.lpszMenuName = 0; wndclass.lpszClassName = L"MMDViewer"; /* 注册窗口类 */ RegisterClass(&wndclass); /* 不能改变窗口大小 */ int style = WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX; /* 根据客户区大小计算窗口大小 */ RECT rect = { 0, 0, 800, 600 }; AdjustWindowRect(&rect, style, 0); /* 居中显示计算窗口位置和大小 */ int w = rect.right - rect.left; int h = rect.bottom - rect.top; int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; /* 创建窗口 */ HWND hwnd = CreateWindow(L"MMDViewer", L"MMDViewer", style, x, y, w, h, NULL, 0, ( HINSTANCE ) GetModuleHandle(0), 0); /* 显示窗口 */ ShowWindow(hwnd, SW_SHOWNORMAL); UpdateWindow(hwnd); return hwnd; } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); g_hWnd = Create(); if ( FAILED(InitDevice()) ) { CleanupDevice(); return 0; } /* 主事件循环 */ MSG msg = { 0 }; while ( WM_QUIT != msg.message ) { if ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) { TranslateMessage(&msg); DispatchMessage(&msg); } Render(); } CleanupDevice(); return 0; }
Shader 代码:
//-------------------------------------------------------------------------------------- // Constant Buffer Variables //-------------------------------------------------------------------------------------- cbuffer ConstantBuffer : register( b0 ) { matrix World; matrix View; matrix Projection; } //-------------------------------------------------------------------------------------- struct VS_OUTPUT { float4 Pos : SV_POSITION; float4 Color : COLOR0; }; //-------------------------------------------------------------------------------------- // Vertex Shader //-------------------------------------------------------------------------------------- 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; } //-------------------------------------------------------------------------------------- // Pixel Shader //-------------------------------------------------------------------------------------- float4 PS( VS_OUTPUT input ) : SV_Target { return input.Color; }
配置好相关的库后,运行程序,你会看到一个旋转的三角形:
渲染管线:
渲染管线就是将 3D 图像转换成 2D 图像输出到屏幕的过程,所以你要进行渲染操作就要使用渲染管线。那么渲染管线在哪呢?有如何创建渲染管线呢?渲染管线的渲染结果如何输出到你创建的窗口呢?
为此需要关注三个对象:设备(ID3D11Device)、设备上下文(ID3D11DeviceContext)和交换链(IDXGISwapChain),这三者和渲染管线有以下关系:
1、设备(ID3D11Device):用于分配GPU资源,如缓冲,纹理,着色器和状态对象(仅举几例),从框架代码中可以看出,基本上都是 CreateXX() 函数。
2、设备上下文(ID3D11DeviceContext):你可以将它理解成渲染管线,用于设置管线状态、将资源绑定到渲染管线和生成渲染命令。
3、交换链(IDXGISwapChain):渲染管线和你创建的窗口间的桥梁,将窗口句柄和交换链绑定,渲染管线输出到交换链的后缓冲,最后呈现到窗口上。
通过调用函数 D3D11CreateDeviceAndSwapChain() 就可以一次性创建这三个对象。下图为渲染管线过程:
结语:
这篇文章简要说明一下渲染管线,接下来将介绍框架源码中的内容。
源码下载:MMDViewer 03.zip