win32 – Direct3D 11的demo创建

我们可以使用D3D为游戏,科学和桌面应用程序创建3-D图形。

非官方demo实例: https://github.com/Ray1024/D3D11Tutorial

当然,我们第一步要开始认识里面的基本api。

#include <d3d11.h>  
#include <chrono>

using namespace std::chrono;

#pragma comment(lib,"D3D11.lib")
//-----------------------------------------------------------------------------  
// 全局变量  
//-----------------------------------------------------------------------------  
HRESULT hr;
ID3D11Device* g_pd3dDevice = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
ID3D11RenderTargetView* pTarget = nullptr;
std::chrono::steady_clock::time_point last;

//-----------------------------------------------------------------------------  
// Desc: 初始化Direct3D  
//-----------------------------------------------------------------------------  
HRESULT InitD3D(HWND hWnd)
{
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 1;  // BufferCount: number of buffers  1: 设置一个后缓存,再加上一个自动设置的前缓存,就是双缓存
    sd.BufferDesc.Width = 0; //系统自动设置合适的值
    sd.BufferDesc.Height = 0;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 0;  //不处于全屏模式,所以不用管刷新率
    sd.BufferDesc.RefreshRate.Denominator = 0;
    sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;  //程序自己决定缩放比率
    sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; //对于大多数显示器,不用设置扫描线顺序
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;  // Use the surface or resource as an output render target.
    sd.OutputWindow = hWnd;
    sd.SampleDesc.Count = 1;   //不需要抗锯齿,一般默认该值
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE; //窗口化

    if (FAILED(hr = D3D11CreateDeviceAndSwapChain(
        NULL,
        D3D_DRIVER_TYPE_HARDWARE,
        NULL,
        0,
        NULL,
        0,
        D3D11_SDK_VERSION,
        &sd,
        &g_pSwapChain,
        &g_pd3dDevice,
        NULL,
        &g_pImmediateContext)))
    {
        return hr;
    }
    //gain access to texture subresource in swap chain (back buffer)
    ID3D11Resource* pBackBuffer = nullptr;
    g_pSwapChain->GetBuffer(0, _uuidof(ID3D11Resource), reinterpret_cast<void**>(&pBackBuffer)); // Get the back buffer  0: 后缓存的索引
    g_pd3dDevice->CreateRenderTargetView(
        pBackBuffer,
        nullptr,
        &pTarget  // pTarget is used to save RenderTarget view
    );
    pBackBuffer->Release(); 

}


//-----------------------------------------------------------------------------  
// Desc: 释放创建对象  
//-----------------------------------------------------------------------------  
VOID Cleanup()
{
    //释放Direct3D设备对象  
    if (g_pd3dDevice != NULL)
        g_pd3dDevice->Release();
    //释放Direct3D对象  
    if (g_pSwapChain != NULL)
        g_pSwapChain->Release();
    if (g_pImmediateContext != NULL)
        g_pImmediateContext->Release();
    if (pTarget != NULL)
        pTarget->Release();
}

//定时器
float Peek()
{
    return duration<float>(steady_clock::now() - last).count();
}

//-----------------------------------------------------------------------------  
// Desc: 渲染图形  
//-----------------------------------------------------------------------------  
void Render()
{
    // Just clear the backbuffer
    float r = sin(Peek()) / 2.0f + 0.5f;
    float ClearColor[4] = { r, r, 1.0f, 1.0f }; //red,green,blue,alpha
    g_pImmediateContext->ClearRenderTargetView(pTarget, ClearColor); // undertake all the drawing work
    g_pSwapChain->Present(0u, 0u);    //Presents a rendered image to the user.
}


//-----------------------------------------------------------------------------  
// Desc: 消息处理  
//-----------------------------------------------------------------------------  
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_DESTROY:
        Cleanup();
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
        Render();
        ValidateRect(hWnd, NULL);
        return 0;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}


//-----------------------------------------------------------------------------  
// Desc: 程序入口  
//-----------------------------------------------------------------------------  
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, INT)
{
    //注册窗口类  
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      L"ClassName", NULL };
    RegisterClassEx(&wc);

    //创建窗口  
    HWND hWnd = CreateWindow(L"ClassName", L"A Direct3D appp",
        WS_OVERLAPPEDWINDOW, 200, 100, 600, 500,
        NULL, NULL, wc.hInstance, NULL);
    last = steady_clock::now(); //running timer
    //初始化Direct3D  
    if (SUCCEEDED(InitD3D(hWnd)))
    {
        //显示主窗口  
        ShowWindow(hWnd, SW_SHOWDEFAULT);
        UpdateWindow(hWnd);

        //进入消息循环  
        MSG msg;
        ZeroMemory(&msg, sizeof(msg));
        while (msg.message != WM_QUIT)
        {
            if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else
            {
                Render();  //渲染图形  
            }
        }
    }

    UnregisterClass(L"ClassName", wc.hInstance);
    return 0;
}

先上一个简单的demo供参考,里面有很多的参数介绍。

大概流程:

创建一个交换链(D3D11CreateDeviceAndSwapChain),然后创建一个后缓存(GetBuffer),再创建一个RenderTarget作为后台缓冲区的渲染目标(ID3D11RenderTargetView* pTarget = nullptr)。 使用CreateRenderTargetView创建用于访问资源数据的渲染目标视图,它承担所有绘图工作。

ClearRenderTargetView将渲染目标中的所有元素设置为一个值。

上面都是对象的创建, 我们还需要context,即上下文,用于发送渲染命令并配置渲染管道。D3D11CreateDeviceAndSwapChain已经帮我们创建好了

等这些都创建完毕,则可以调用g_pSwapChain->Present(0u, 0u); 来将渲染的图片发给user --------------------Present命令相当于bitblt,用于将后缓存的视图快速复制给前缓存

另附一张图:

win32 - Direct3D 11的demo创建

 

 简单说来就是,传统情况下,显示器从单个缓冲区扫描像素的时候,它遵循自左上角到右下角的点扫描,就是一个像素一个像素的扫描,那么如果我们在单个缓冲区里绘制东西的时候,还没绘制好就被显示器扫描走了,那就会显示不完整的图形。

 所以D3D需要后缓存来避免这样的问题。当后缓存的东西绘制完毕,使用present或者filp来将后缓存快速复制给前缓存,那么显示器在扫描的时候,每次扫描完前缓存的东西时(扫描也需要时间的),后缓存已经准备好了。

言归正传,在代码中我们有了后缓存pBackBuffer,但是我们得需要一个渲染视图在后缓存中绘制我们需要的东西,那么我们就需要创建一个渲染对象pTarget,然后使用CreateRenderTargetView来使pTarget保存渲染的视图(渲染视图相当于后缓冲区)。 好了,有了渲染视图,就可以使用ClearRenderTargetView将RGBA的数值渲染到视图中,这样我们就可以使用Present来显示图像了。

为什么需要渲染呢?

从逻辑上将, 我们会说:“后缓存...!" 并完成。 但是,Direct3D在这一点上并不知道。 

在Direct3D中渲染时,必须建立渲染目标。 这是一个简单的com对象,它在视频内存中维护一个位置,供我们渲染。 在大多数情况下, 这是后台缓冲区。

拓展:

纹理资源是旨在存储纹理像素的数据的结构化集合。纹素表示管道可以读取或写入的纹理的最小单位。与缓冲区不同,纹理可以被着色器单元读取,从而可以通过纹理采样器进行过滤。

渲染管线是指:在给定一个3D场景的几何描述及一架已确定位置和方向的虚拟摄像机(virtual camera)时,根据虚拟摄像机的视角生成2D图像的一系列步骤。

原文链接: https://www.cnblogs.com/strive-sun/p/14216932.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    win32 - Direct3D 11的demo创建

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/404749

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月25日 下午4:42
下一篇 2023年4月25日 下午4:42

相关推荐