基于D11的教程(初始化Direct3D)

基于D11的教程(初始化Direct3D)

本章学习目标

  1. 了解渲染管线、设备、交换链、上下文的概念
  2. 代码实践

预备小知识: 在VS2019中,选择【使用C++的桌面开发】会默认选中一个叫做Windows 10 SDK的组件。这个SDK已经集成DirectX的开发库,即曾经游戏开发需要写入的头文件d3dx11.h和库文件d3dx11.lib是不需要加入的代码中的

概念学习:
渲染管线:也叫渲染流水线,将3维物体或者三维场景描述为一幅二维图像

设备:创建资源和枚举显示适配器的性能。设备支持的特性与显卡有关,对于D3D11而言,只要是支持3D3的设备一定支持3D3的全部特性。

交换链:用于绘制过程的保存前台缓存和后台缓存。前台缓存存储显示在屏幕上的图像数据;后台缓存存储下一帧绘制的图像。
过程是这样的:前台缓存的图像显示后,前台缓存与后台缓存相互交换,后台缓存变成前台缓存用于显示屏幕上的图像,而原来的前台缓存变成后台缓存用于绘制下一帧图像。
总结:两个缓存交替。像跑马灯一样,图像以动画的形式呈现出来。

上下文:设置管线状态,生成渲染命令。有两类,立即上下文(向驱动发送渲染命令)和延迟上下文。

d3dUtility.h

#pragma once
#ifndef __d3dUtility_H_

#endif  __d3dUtility_H_
#include<windows.h>
/*************************************************************************
    > File Name: d3dUtility.h
    > Author: YLD10
    > Mail: yl1315348050@yahoo.com
    > Created Time: 2018.5.31 22:58
    > Describe: Win32 Application InitD3D.
                《基于 DirectX11 的 3D 图形程序设计案例教程》
                4.2 节程序例子 初始化 D3D
 ************************************************************************/
#pragma once

#include <Windows.h>

 //
// XNA 数学库相关头文件
//
#include <d3dCompiler.h>
#include <DirectXMath.h>

//
// DirectX11 相关头文件
//
#include <d3d11.h>


//
// DirectX11 相关库
//
#pragma comment(lib, "d3d11.lib")
//#pragma comment(lib, "d3dx11.lib")

#pragma comment(lib, "d3dCompiler.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "winmm.lib")

namespace d3d  // 定义一个 d3d 命名空间
{
    /*
     * 初始化 D3D
     */
    bool InitD3D(HINSTANCE hInstance,
        int width, int height,
        ID3D11RenderTargetView** renderTargerView,  // 目标渲染视图接口
        ID3D11DeviceContext** immediateContext,     // 执行上下文接口
        IDXGISwapChain** swapChain,                 // 交换链接口
        ID3D11Device** device);                     // 设备用接口, 每个 D3D 程序至少有一个设备

/*
 * 消息循环
 */
    int EnterMsgLoop(bool (*ptr_display)(float timeDelta));

    /*
     * 回调函数
     */
    LRESULT CALLBACK WndProc(HWND,
        UINT msg,
        WPARAM,
        LPARAM lParam);
}

d3dUnit.cpp

/*************************************************************************
    > File Name: d3dUnit.cpp
    > Author: YLD10
    > Mail: yl1315348050@yahoo.com
    > Created Time: 2018.5.31 22:57
    > Describe: Win32 Application InitD3D.
                《基于 DirectX11 的 3D 图形程序设计案例教程》
                4.2 节程序例子 初始化 D3D
 ************************************************************************/
#include "d3dUtility.h"

ID3D11Device* device = NULL;                        // D3D11 设备指针
IDXGISwapChain* swapChain = NULL;                   // 交换链指针
ID3D11DeviceContext* immediateContext = NULL;       // 执行上下文指针
ID3D11RenderTargetView* renderTargetView = NULL;    // 渲染目标视图指针

//************* 以下为框架函数 ***************
bool Setup()
{
    // 本例中 setup 函数没有内容, 以后的实验中会往里面填写内容

    return true;
}

void Cleanup()
{
    // 释放指针
    if (renderTargetView)
    {
        renderTargetView->Release();
    }
    if (immediateContext)
    {
        immediateContext->Release();
    }
    if (swapChain)
    {
        swapChain->Release();
    }
    if (device)
    {
        device->Release();
    }
}

bool Display(float timeDelta)
{
    if (device)
    {
        // 声明一个数组存放颜色信息, 4 个元素分别表示红, 绿, 蓝以及 alpha
        float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f };

        // 清除渲染目标视图
        immediateContext->ClearRenderTargetView(renderTargetView,
            ClearColor);

        // 显示渲染好的图像给用户
        swapChain->Present(0,   // 指定如何同步显示, 设置 0 表示不同步显示
            0);  // 可选项, 设置 0 表示为从每个缓存中显示一帧
    }

    return true;
}
//************* 框架函数编写结束 ***************

/*
 * 回调函数
 */
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg,
    WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_DESTROY:
        ::PostQuitMessage(0);
        break;
    case WM_KEYDOWN:
        if (VK_ESCAPE == wParam)
        {
            ::DestroyWindow(hwnd);
        }
        break;
    }

    return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

/*
 * 主函数 WinMain
 */
int WINAPI WinMain(HINSTANCE hInstance,
    HINSTANCE hPreInstance,
    PSTR pCmdLine,
    int nShowCmd)
{
    // 初始化
    // 上面声明的 4 个指针, 在这里作为参数传给 InitD3D 函数
    if (!d3d::InitD3D(hInstance,
        800,
        600,
        &renderTargetView,
        &immediateContext,
        &swapChain,
        &device))
    {
        ::MessageBox(0, L"InitD3D() - FAILED", 0, 0);

        return 0;
    }
    if (!Setup())
    {
        ::MessageBox(0, L"Setup() - FAILED", 0, 0);

        return 0;
    }

    // 执行消息循环, 将函数 Display 的指针作为参数传递
    d3d::EnterMsgLoop(Display);

    Cleanup();

    return 0;
}

d3dUtility.cpp

/*************************************************************************
    > File Name: d3dUtility.cpp
    > Author: YLD10
    > Mail: yl1315348050@yahoo.com
    > Created Time: 2018.5.31 22:56
    > Describe: Win32 Application InitD3D.
                《基于 DirectX11 的 3D 图形程序设计案例教程》
                4.2 节程序例子 初始化 D3D
 ************************************************************************/
#include "d3dUtility.h"

 /*
  * D3D 初始化
  * 这个函数中包括两个部分: 第一部分: 创建一个窗口; 第二部分: 初始化 D3D
  * 函数参数包括:
  * 1.HINSTANCE   当前应用程序实例的句柄
  * 2.int width   窗口宽
  * 3.int height  窗口高
  * 4.ID3D11RenderTargetView** renderTargetView  目标渲染视图指针
  * 5.ID3D11DeviceContext** immediateContext     立即执行上下文指针
  * 6.IDXGISwapChain** swapChain                 交换链指针
  * 7.ID3D11Device** device                      设备用指针, 每个 D3D 程序至少有一个设备
  */
bool d3d::InitD3D(HINSTANCE hInstance,
    int width, int height,
    ID3D11RenderTargetView** renderTargerView,
    ID3D11DeviceContext** immediateContext,
    IDXGISwapChain** swapChain,
    ID3D11Device** device)
{
    //************* 第一部分: 创建一个窗口开始 ***************
    // 这部分的代码和 2.2 节中创建窗口代码基本一致
    // 具体参数的注释可以参考 2.2 节程序例子
    // 创建窗口的 4 个步骤: 1 设计一个窗口类; 2 注册窗口类
    //                    3 创建窗口;       4 窗口显示和更新
    // 1 设计一个窗口类
    WNDCLASS wc;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)d3d::WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = 0;
    wc.lpszClassName = L"Direct3D11App";

    // 2 注册窗口类
    if (!RegisterClass(&wc))
    {
        ::MessageBox(0, L"RegisterClass() - FAILED", 0, 0);

        return false;
    }

    // 3 创建窗口
    HWND hwnd;
    hwnd = ::CreateWindow(L"Direct3D11App",
        L"D3D11",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        width,
        height,
        0,
        0,
        hInstance,
        0);
    if (!hwnd)
    {
        ::MessageBox(0, L"CreateWindow() - FAILED", 0, 0);

        return false;
    }

    // 4 窗口显示和更新
    ::ShowWindow(hwnd, SW_SHOW);
    ::UpdateWindow(hwnd);

    //************* 第一部分: 创建一个窗口结束 ***************

    //************* 第二部分: 初始化 D3D 开始 ***************
    // 初始化 D3D 设备主要为以下步骤
    // 1. 描述交换链, 即填充 DXGI_SWAP_CHAIN_DESC 结构
    // 2. 使用 D3D11CreateDeviceAndSwapChain 创建 D3D 设备 (ID3D11Device)
    //    执行上下文接口 (ID3D11DeviceContext), 交换链接口 (IDXGISwapChain)
    // 3. 创建目标渲染视图 (ID3D11RenderTargetView)
    // 4. 设置视口 (View Port)

    // 第一步, 描述交换链, 即填充 DXGI_SWAP_CHAIN_DESC 结构
    DXGI_SWAP_CHAIN_DESC sd;          // 首先声明一个 DXGI_SWAP_CHAIN_DESC 的对象 sd
    ZeroMemory(&sd, sizeof(sd));      // 用 ZeroMemory 对 sd 进行初始化
    sd.BufferCount = 1;         // 交换链中后台缓存数量, 通常为 1
    sd.BufferDesc.Width = width;     // 缓存区中的窗口宽
    sd.BufferDesc.Height = height;    // 缓存区中的窗口高
    sd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;    // 表示红绿蓝 Alpha 各 8 位
    sd.BufferDesc.RefreshRate.Numerator = 60;           // 刷新频率的分子为 60
    sd.BufferDesc.RefreshRate.Denominator = 1;            // 刷新频率的分母为 1, 即
                                                          // 刷新频率为每秒 60 次
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;     // 用来描述后台缓存用法
                                                          // 控制 CPU 对后台缓存的访问
    sd.OutputWindow = hwnd;     // 指向渲染目标窗口的句柄
    sd.SampleDesc.Count = 1;        // 多重采样, 本例中不采用多重采样
    sd.SampleDesc.Quality = 0;        // 所以 Count = 1, Quality = 0
    sd.Windowed = TRUE;     // TRUE 为窗口模式, FALSE 为全屏模式

    // 第二步, 创建设备, 交换链以及立即执行上下文
    // 创建一个数组确定尝试创建 Featurelevel 的顺序
    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,  // D3D11 所支持的特征, 包括 shader model 5
        D3D_FEATURE_LEVEL_10_1,  // D3D10 所支持的特征, 包括 shader model 4
        D3D_FEATURE_LEVEL_10_0
    };

    // 获取 D3D_FEATURE_LEVEL 数组的元素个数
    UINT numFeatureLevels = ARRAYSIZE(featureLevels);

    // 调用 D3D11CreateDeviceAndSwaoChain 创建交换链、设备和执行上下文
    // 分别存入 swapChain, device, immediateContext
    if (FAILED(D3D11CreateDeviceAndSwapChain(
        NULL,                // 确定显示适配器, NULL 表示默认显示适配器
        D3D_DRIVER_TYPE_HARDWARE,  // 驱动类型, 这里表示使用三维硬件加速
        NULL,                // 只有上一个参数设置为
                             // D3D_DRIVER_TYPE_SOFTWARE 时才使用该参数
        0,                   // 也可以设置为 D3D11_CREATE_DEVICE_DEBUG 开启调试模式
        featureLevels,       // 前面定义的 D3D_FEATURE_LEVEL 数组
        numFeatureLevels,    // D3D_FEATURE_LEVEL 的元素个数
        D3D11_SDK_VERSION,   // SDK 的版本, 这里为 D3D11
        &sd,                 // 前面定义的 DXGI_SWAP_CHAIN_DESC 对象
        swapChain,           // 返回创建好的交换链指针, InitD3D 函数传递的实参
        device,              // 返回创建好的设备用指针, InitD3D 函数传递到实参
        NULL,                // 返回当前设备支持的 featureLevels 数组中的第一个对象,
                             // 一般设置为 NULL
        immediateContext)))  // 返回创建好的执行上下文指针,
                             // InitD3D 函数传递的实参
    {
        ::MessageBox(0, L"CreateDevice - FAILED", 0, 0);

        return false;
    }

    // 第三步, 创建并设置渲染目标视图
    HRESULT hr = 0;                         // COM 要求所有的方法都会返回一个 HRESULT 类型的错误号
    ID3D11Texture2D* pBackBuffer = NULL;    // ID3D11Texture2D 类型的后台缓存指针

    // 调用 GetBuffer() 函数得到后台缓存对象, 并存入 &pBackBuffer 中
    hr = (*swapChain)->GetBuffer(0,         // 缓存索引, 一般设置为 0
        __uuidof(ID3D11Texture2D),  // 缓存类型
        (LPVOID*)&pBackBuffer);     // 缓存指针
// 判断 GetBuffer 是否调用成功
    if (FAILED(hr))
    {
        ::MessageBox(0, L"GetBuffer - FAILED", 0, 0);

        return false;
    }

    // 调用 CreateRenderTargetView 创建好渲染目标视图
    // 创建后存入 renderTargetView 中
    hr = (*device)->CreateRenderTargetView(pBackBuffer,        // 上面创建好的后台缓存
        NULL,               // 设置为 NULL 得到默认的渲染目标视图
        renderTargerView);  // 返回创建好的渲染目标视图
                            // InitD3D 函数传递的实参
    pBackBuffer->Release();  // 释放后台缓存

    // 判断 CreateRenderTargetView 是否调用成功
    if (FAILED(hr))
    {
        ::MessageBox(0, L"CreateRender - FAILED", 0, 0);

        return false;
    }

    // 将渲染目标视图绑定到渲染管线
    (*immediateContext)->OMSetRenderTargets(1,                 // 绑定的目标视图的个数
        renderTargerView,  // 渲染目标视图
        NULL);             // 不绑定深度模版

// 第四步, 设置视口大小, D3D11 默认不会设置视口, 此步骤必须手动设置
    D3D11_VIEWPORT vp;      // 创建一个视口的对象
    vp.Width = width;    // 视口的宽
    vp.Height = height;   // 视口的高
    vp.MinDepth = 0.0f;     // 深度值的下限, 由于深度值是 [0, 1] 所以下限值是 0
    vp.MaxDepth = 1.0f;     // 深度值的上限, 上限值是 1
    vp.TopLeftX = 0;        // 视口左上角的横坐标
    vp.TopLeftY = 0;        // 视口左上角的纵坐标

    // 设置视口
    (*immediateContext)->RSSetViewports(1,      // 视口的个数
        &vp);   // 上面创建的视口对象

    return true;

    //************* 第二部分: 初始化 D3D 结束 ***************
}

/*
 * 消息循环函数, 和之前 "Hello World" 程序中 Run() 起到同样的功能
 * bool(*ptr_display)(float timeDelta) 表示传递一个函数指针作为参数
 * 这个函数有一个 float 类型的参数, 有一个 bool 类型的返回
 */
int d3d::EnterMsgLoop(bool(*ptr_display)(float timeDelta))
{
    MSG msg;
    ::ZeroMemory(&msg, sizeof(MSG));                // 初始化内存

    static float lastTime = (float)timeGetTime();   // 第一次获取当前时间

    while (WM_QUIT != msg.message)
    {
        if (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
        else
        {
            // 第二次获取当前时间
            float currTime = (float)timeGetTime();

            // 获取两次时间之间的时间差
            float timeDelta = (currTime - lastTime) * 0.001f;

            // 调用显示函数, 这在后面时间图形的变化 (如旋转) 时会用到
            ptr_display(timeDelta);
            lastTime = currTime;
        }
    }

    return msg.wParam;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值