zoukankan      html  css  js  c++  java
  • Directx11教程(4) 一个最基本D3D应用程序(2)

    接着上篇教程的代码,本篇加入基本的D3D代码,实现一个完整的D3D11程序框架。

    我们增加一个新类D3DClass, 用来处理3D渲染功能。增加该类后,程序的框架如下图:

    image

        GraphicsClass.h代码改变如下, 主要是增加了一个D3DClass类成员变量,在Render函数中,将会调用D3DClass的相应Render函数,比如BeginScene、EndScene,BeginScene主要是清除framebuffer,设置渲染背景颜色,而EndScene则是把交换前后缓冲,使当前渲染的内容在屏幕上显示出来。

    class GraphicsClass
        {

        private:
            bool Render();

            //定义一个D3DClass类成员变量

            D3DClass* m_D3D;

        };

    GraphicsClass.cpp的代码如下:

    #include "GraphicsClass.h"


    GraphicsClass::GraphicsClass(void)
        {

           m_D3D = 0;       
        }

    bool GraphicsClass:: Initialize(int screenWidth, int screenHeight, HWND hwnd)
        {
          bool result;


        // 创建一个D3DClass对象.
        m_D3D = new D3DClass;
        if(!m_D3D)
            {
            return false;
            }

       // 调用D3DClass初始化函数
        result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
        if(!result)
            {
            MessageBox(hwnd, L"Could not initialize Direct3D", L"Error", MB_OK);
            return false;
            }

        return true;
        }

    void GraphicsClass::Shutdown()
        {

        //销毁m_D3D对象
        if(m_D3D)
            {
            m_D3D->Shutdown();
            delete m_D3D;
            m_D3D = 0;
            }

        return;
        }


    bool GraphicsClass::Frame()
        {

         bool result;

        //调用Render函数,渲染3D场景

        //Render是GraphicsClass的私有函数.

        result = Render();
        if(!result)
            {
            return false;
            }

        return true;
        }


    bool GraphicsClass::Render()
        {

    // 设置framebuffer为浅蓝色
    m_D3D->BeginScene(00f, 0.0f, 0.5f, 1.0f);


    //把framebuffer中的图像present到屏幕上.

    m_D3D->EndScene();

        return true;
        }

    Initialize函数,来初始化D3D11设备,设置相关状态。

    初始化D3D的代码主要包括以下几个步骤:

    1. 创建一个交换链(swapchain)描述变量,该变量包括后缓冲的设置(大小,格式)等。

    2. 通过函数D3D11CreateDeviceAndSwapChain创建D3D11 设备和上下文设备(注:在D3D10中,只有一个device)。

    3. 创建一个渲染目标视图指向后缓冲。

    4. 创建深度、模版缓冲以及相应的视图。

    5. 创建深度、模版状态并在OM(输出合并)阶段绑定到管线。

    6. 创建并设置渲染状态。

    7. 设置视口。

         设备创建后,我们可以通过ClearRenderTargetView(m_renderTargetView, color); 函数来清除帧缓冲(即设置渲染背景),ClearDepthStencilView(m_depthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);函数清除深度模版缓冲,然后开始渲染3D场景,渲染完成后,我们还要用Present函数把后缓冲内容显示到输出设备上。

          在D3D中,我们使用双缓冲的概念(也可以设置多个后缓冲,但我从来只设置1个),前缓冲就是屏幕上现在显示的内容,后缓冲是现在正在渲染的内容。使用两个缓冲,可以保证渲染的内容完整,避免屏幕上显示不完整的渲染图像。当渲染完成时候,我们交换前后缓冲(这个概念在D3D中称作present,在opengl中叫做swapbuffer),后缓冲成为前缓冲,前缓冲成为后缓冲,这样,后缓冲的内容就在屏幕上显示,而下一帧的内容则输出到前一帧前缓冲(现在的后缓冲)。

         在D3D11中,我们在很多地方使用2D纹理,除了纹理图像外,后缓冲,深度缓冲等等都用2D纹理表示。2D纹理可以看作一副存储数据的二维图像。当然,它还有更多的功能,比如mipmaps层,实施滤波和多采样操作等等。

      

    纹理数据通常为以下几种格式:

    DXGI_FORMAT_R32G32B32_FLOAT : 每个元素包括3个32位浮点数。
    DXGI_FORMAT_R16G16B16A16_UNORM :
    每个元素有4个16位分量,每个分量都被映射到[0, 1] 范围,UNORM表示无符号归一化。
    DXGI_FORMAT_R32G32_UINT: 每个元素有2个32位无符号整数。
    DXGI_FORMAT_R8G8B8A8_UNORM :
    每个元素有4个8位无符号整数,被映射到 [0,1] 范围。
    DXGI_FORMAT_R8G8B8A8_SNORM : 每个元素有4个8位有符号分量,被映射到[ -1, 1]范围。
    DXGI_FORMAT_R8G8B8A8_SINT: 每个元素有4个8位有符号整数,被映射到[ − 128, 127] 范围

    D3D 初始化的代码主要在D3DClass的初始化函数中,需要注意的取得显卡的刷新率主要是设置垂直同步时使用

    //Initialize函数包含完成D3D设置的所有代码。

    bool D3DClass::Initialize(int screenWidth, int screenHeight, bool vsync, HWND hwnd, bool fullscreen,
        float screenDepth, float screenNear)
        {
        HRESULT result;
        IDXGIFactory* factory;
        IDXGIAdapter* adapter;
        IDXGIOutput* adapterOutput;
        unsigned int numModes, i, numerator, denominator, stringLength;
        DXGI_MODE_DESC* displayModeList;
        DXGI_ADAPTER_DESC adapterDesc;
        int error;
        DXGI_SWAP_CHAIN_DESC swapChainDesc;
        D3D_FEATURE_LEVEL featureLevel;
        ID3D11Texture2D* backBufferPtr;
        D3D11_TEXTURE2D_DESC depthBufferDesc;
        D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
        D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
        D3D11_RASTERIZER_DESC rasterDesc;
        D3D11_VIEWPORT viewport;
        float fieldOfView, screenAspect;


        // 保存垂直同步设置
        m_vsync_enabled = vsync;

       // 创建一个DirectX graphics interface factory. 
       result = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);
        if(FAILED(result))
            {
            return false;
            }

        // 用接口工厂创建一个主显卡的适配
        result = factory->EnumAdapters(0, &adapter);
        if(FAILED(result))
            {
            return false;
            }

      // 得到主适配器的输出.
        result = adapter->EnumOutputs(0, &adapterOutput);
        if(FAILED(result))
            {
            return false;
            }

        //得到适合 DXGI_FORMAT_R8G8B8A8_UNORM 的显示模式.
        result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
        if(FAILED(result))
            {
            return false;
            }

        displayModeList = new DXGI_MODE_DESC[numModes];
        if(!displayModeList)
            {
            return false;
            }

        // 保存显示模式到displayModeList中
        result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList);
        if(FAILED(result))
            {
            return false;
            }

        //遍历所有显示模式,得到刷新率两个参数值numerator 和 denominator
        for(i=0; i<numModes; i++)
            {
            if(displayModeList[i].Width == (unsigned int)screenWidth)
                {
                if(displayModeList[i].Height == (unsigned int)screenHeight)
                    {
                    numerator = displayModeList[i].RefreshRate.Numerator;
                    denominator = displayModeList[i].RefreshRate.Denominator;
                    }
                }
            }
     
    // 得到显卡描述
        result = adapter->GetDesc(&adapterDesc);
        if(FAILED(result))
            {
            return false;
            }

       // 保存显存大小.
        m_videoCardMemory = (int)(adapterDesc.DedicatedVideoMemory / 1024 / 1024);

        //保存显卡描述串

       //wcstombs_s, wide char转化为char
        error = wcstombs_s(&stringLength, m_videoCardDescription, 128, adapterDesc.Description, 128);
        if(error != 0)
            {
            return false;
            }
       // 释放显示模式列表
        delete [] displayModeList;
        displayModeList = 0;

        //释放适配器输出.
        adapterOutput->Release();
        adapterOutput = 0;

       //释放适配器
        adapter->Release();
        adapter = 0;

        // 释放接口工厂.
        factory->Release();
        factory = 0;

        // 初始化交换链描述
        ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));

       // 用1个后缓冲
        swapChainDesc.BufferCount = 1;

       //帧缓冲的大小和应用程序窗口大小相等 
        swapChainDesc.BufferDesc.Width = screenWidth;
        swapChainDesc.BufferDesc.Height = screenHeight;

       // 后缓冲的surface的格式为DXGI_FORMAT_R8G8B8A8_UNORM  
       // surface的每个像素用4个无符号的8bit[映射到0-1]来表示。NORM表示归一化
     
        swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

       // 如果使用垂直同步,设置后缓冲的刷新率

        //刷新率就是一秒钟把后缓冲内容在屏幕上画出的次数 
        //如果开启垂直同步,则锁定刷新率,则fps是固定的


        if(m_vsync_enabled)
            {
            swapChainDesc.BufferDesc.RefreshRate.Numerator = numerator;
            swapChainDesc.BufferDesc.RefreshRate.Denominator = denominator;
            }
        else
            {
            swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
            swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
            }

        // 设置后缓冲的用途
        // 我们的渲染目标缓冲为后缓冲
     
        swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

       // 后缓冲输出的窗口句柄 
        swapChainDesc.OutputWindow = hwnd;

        // 不使用多重采样
        swapChainDesc.SampleDesc.Count = 1;
        swapChainDesc.SampleDesc.Quality = 0;

        // 设置全屏或者窗口模式 
        if(fullscreen)
            {
            swapChainDesc.Windowed = false;
            }
        else
            {
            swapChainDesc.Windowed = true;
            }

      // 设定扫描线ordering以及缩放为unspecified 
        swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
        swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

        // 后缓冲内容呈现到屏幕后,放弃其内容
        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

       //不设置标志
        swapChainDesc.Flags = 0;

       // 设置feature level为D3D11
        // 如果显卡不支持D3D11,我们能够通过设置这个参数,使用D3D10,或者9
     
        featureLevel = D3D_FEATURE_LEVEL_11_0;

        // 创建交换链,设备以及设备上下文 
        result = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &featureLevel, 1,
            D3D11_SDK_VERSION, &swapChainDesc, &m_swapChain, &m_device, NULL, &m_deviceContext);
        if(FAILED(result))
            {
            return false;
            }

    说明:在本教程代码中,我们同时创建device和swapchain,可以通过两个函数分别创建device和swapchain

    D3D11CreateDevice( NULL,  //默认显卡

                                 D3D_DRIVER_TYPE_HARDWARE,

                                  0,  //表示没有软件device

                                  0,  //创建设备的标志,可以指定为D3D11_CREATE_DEVICE_DEBUG,可在vs中输入调试信息

                                  0, 0, //FeatureLevel的信息,设置为0,将使用最高的feature,也就是D3D11 feature

                                  D3D11_SDK_VERSION, //SDK版本

                                  &m_device,

                                  &featureLevel, //返回现在选择的feature level信息

                                   &m_deviceContext

                                  );

    创建设备后,我们可以查询到当前device支持msaa的情况,比如下面的代码,查询device是否支持4xaa。

    UINT m4xMsaaQuality;

    m_device->CheckMultisampleQualityLevels( DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m4xMsaaQuality));

    如果这个函数返回0,则表示显示格式和采样数量的组合硬件不支持。否则,返回有效的quality数目,有效的quality值是0-m4xMsaaQuality。

    之后可以,单独创建swapchain:

    result = factory->CreateSwapChain(m_device,&swapChainDesc, &m_swapChain);

        // 得到交换链中的后缓冲指针 
        result = m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr);
        if(FAILED(result))
            {
            return false;
            }

       // 用后缓冲创建渲染目标视图.
        result = m_device->CreateRenderTargetView(backBufferPtr, NULL, &m_renderTargetView);
        if(FAILED(result))
            {
            return false;
            }

       //释放后缓冲(引用计数减1)
        backBufferPtr->Release();
        backBufferPtr = 0;

        // 初始化深度缓冲描述.
        ZeroMemory(&depthBufferDesc, sizeof(depthBufferDesc));

        //设置深度缓冲描述
        depthBufferDesc.Width = screenWidth;
        depthBufferDesc.Height = screenHeight;
        depthBufferDesc.MipLevels = 1;
    //对于深度缓冲为1
        depthBufferDesc.ArraySize = 1;
    //对于深度缓冲为1,对于纹理,这2个参数有更多用途
        depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
        depthBufferDesc.SampleDesc.Count = 1;
        depthBufferDesc.SampleDesc.Quality = 0;
        depthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
        depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
        depthBufferDesc.CPUAccessFlags = 0;
        depthBufferDesc.MiscFlags = 0;

      // 创建深度缓冲
        result = m_device->CreateTexture2D(&depthBufferDesc, NULL, &m_depthStencilBuffer);
        if(FAILED(result))
            {
            return false;

            }

        // 初始化深度模版状态描述 

    像素shader完成后,会进行深度模版测试,深度模版测试流程见:http://www.cnblogs.com/mikewolf2002/p/5149729.html
        ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));

        // 设置深度模版状态描述
        depthStencilDesc.DepthEnable = true;
        depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;

        //D3D11_DEPTH_WRITE_MASK_ZERO禁止写深度缓冲
        depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;

        depthStencilDesc.StencilEnable = true;
        depthStencilDesc.StencilReadMask = 0xFF;
        depthStencilDesc.StencilWriteMask = 0xFF;

       // 对于front face 像素使用的模版操作操作

        depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
        depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
        depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
        depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

       // 对于back face像素使用的模版操作模式
        depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
        depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
        depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
        depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

        //创建深度模版状态
        result = m_device->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);
        if(FAILED(result))
            {
            return false;

            }

      // 设置深度模版状态,使其生效
        m_deviceContext->OMSetDepthStencilState(m_depthStencilState, 1);

       // 初始化深度模版视图.
        ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));

        // 设置深度模版视图描述.
        depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
        depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
        depthStencilViewDesc.Texture2D.MipSlice = 0;

        // 创建深度模版视图.
        result = m_device->CreateDepthStencilView(m_depthStencilBuffer, &depthStencilViewDesc, &m_depthStencilView);
        if(FAILED(result))
            {
            return false;
            }

        // 绑定渲染目标视图和深度缓冲到渲染管线.
        m_deviceContext->OMSetRenderTargets(1, &m_renderTargetView, m_depthStencilView);


        // 设置光栅化描述,指定多边形如何被渲染.
        rasterDesc.AntialiasedLineEnable = false;
        rasterDesc.CullMode = D3D11_CULL_BACK; //背面剔除,看不见三角形会被剔除掉。
        rasterDesc.DepthBias = 0;
        rasterDesc.DepthBiasClamp = 0.0f;
        rasterDesc.DepthClipEnable = true;
        rasterDesc.FillMode = D3D11_FILL_SOLID;
        rasterDesc.FrontCounterClockwise = false;
        rasterDesc.MultisampleEnable = false;
        rasterDesc.ScissorEnable = false;
        rasterDesc.SlopeScaledDepthBias = 0.0f;

       // 创建光栅化状态.
        result = m_device->CreateRasterizerState(&rasterDesc, &m_rasterState);
        if(FAILED(result))
            {
            return false;
            }

       //设置光栅化状态,使其生效
        m_deviceContext->RSSetState(m_rasterState);


       // 设置视口,显示全部后缓冲内容
        viewport.Width = (float)screenWidth;
        viewport.Height = (float)screenHeight;
        viewport.MinDepth = 0.0f;
        viewport.MaxDepth = 1.0f;
        viewport.TopLeftX = 0.0f;
        viewport.TopLeftY = 0.0f;

       // 创建视口
        m_deviceContext->RSSetViewports(1, &viewport);

        // 设置透视投影矩阵
        fieldOfView = (float)D3DX_PI / 4.0f;
        screenAspect = (float)screenWidth / (float)screenHeight;

        //创建透视投影矩阵

        D3DXMatrixPerspectiveFovLH(&m_projectionMatrix, fieldOfView, screenAspect, screenNear, screenDepth);

        //初始化world矩阵为单位矩阵.

        //该矩阵实现局部坐标到世界坐标的转换
        D3DXMatrixIdentity(&m_worldMatrix);


       // 创建正交投影矩阵,主要用来实施2D渲染.
        D3DXMatrixOrthoLH(&m_orthoMatrix, (float)screenWidth, (float)screenHeight, screenNear, screenDepth);

        return true;


        }

       程序执行后,界面如下图所示。

    image

    完整的代码请参考:

    工程文件myTutorialD3D11_3

    代码下载:

    https://files.cnblogs.com/mikewolf2002/myTutorialD3D11.zip

  • 相关阅读:
    Analysis Services features supported by SQL Server editions
    Azure DevOps to Azure AppServices
    Power BI For Competition
    Win10开机“提示语音”以及”随机播放音乐”
    Azure DevOps
    Allow Only Ajax Requests For An Action In ASP.NET Core
    Mobile CI/CD 101
    Configure SSL for SharePoint 2013
    AWS Step Function Serverless Applications
    Cordova Upload Images using File Transfer Plugin and .Net core WebAPI
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/2385838.html
Copyright © 2011-2022 走看看