基于 DirectX11 的 MMDViewer 03-渲染管线

Posted 为了邮箱5

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于 DirectX11 的 MMDViewer 03-渲染管线相关的知识,希望对你有一定的参考价值。

  准备工作:  


   开始搭建框架之前,你需要确保已经进行了 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", styl

以上是关于基于 DirectX11 的 MMDViewer 03-渲染管线的主要内容,如果未能解决你的问题,请参考以下文章

基于 DirectX11 的 MMDViewer 02-创建一个窗口

基于 DirectX11 的 MMDViewer 04-渲染目标视图和多视口

Visual Studio图形调试器详细使用教程(基于DirectX11)

粒子系统与雨的效果 (DirectX11 with Windows SDK)

DirectX 12 应用程序在 Windows 11 中崩溃

DirectX11第九篇 光照模型——高光