DirectX 3D编程入门教程(二)

发表于2017-09-12
评论0 2k浏览

本篇文章主要给大家介绍DirectX 3D编程入门教程系列,该教程适用于一些刚刚涉及DirectX 3D编程的朋友,可以让大家快速掌握,下面开始介绍第二篇。

提要

前几天很简单地跑了一个DirectX 9 程序,以为DirectX就那么绘制,事实证明有点Naive了。

之前的那个程序最多也就是个固定流水线的东西。但是今天要用DirectX11来写一个小的框架。

龙书就不要看了,看Introduction to 3D GAME PROGRAMMING WITH DIRECTX®11


几个重要的类

ID3D11Device : 一个虚拟适配器;它被用于运行渲染和创建资源。

ID3D11DeviceContext:  represents a device context which generates rendering commands.

ID3D11RenderTargetView:  identifies the render-target subresources that can be accessed during rendering.

ID3D11InputLayout: An input-layout interface holds a definition of how to feed vertex data that is laid out in memory into the input-assembler stage of the graphics pipeline.


渲染流水线(翻译自微软文档)




DirectX编程流水线是为了实时游戏应用设计的,上图显示了由输入到输出的各个阶段的数据流向。相对于DirectX10 的图形流水线,DirectX11添加了一些额外的Stage来支持一些新的特性。

你可以使用DirectX 11API来配置所有的Stage,通过HLSL语言来设置就可以了,这样整个流水线就拥有非常大的可扩展性和适应性了。下面列出每个阶段所做的事情.

Input-Assembler Stage : 提供渲染时的数据(三角形,线,点);

Vertex-Shader Stage:处理顶点,通常的操作有:Transformmation,蒙皮,光照计算。通常一个VertexShader输入是一组顶点,输出也是一组顶点。

Geometry-Shader Statge:这个阶段会处理所有的图元,输入是完整的图元(三角形就是三个顶点,线段就是两个顶点,还有就是单个的点)。另外,每个图元可以包含邻接图元的信息。另外这个阶段还可以对图元进行一定的简化和精细化。给定一个图元,geometry shader可以丢弃这个图元,或者可以生成新的一个或者多个的图元。

Stream-Output Stage: 从上一个阶段流下来的数据,可以将图元信息从流水线放入到存储中,或者放到Rasterizer阶段。被放到内存中的数据可以再次放到流水线中,或者被CPU读取。

Rasterizer Stage:裁剪图元,为pixel shader准备图元,并准备怎样调用pixel shader.

Pixel-Shader Stage: 收到经过插值的结果,生成最终的图像。

Output-Merger Stage :合并各种类型的输出信息(pixel shader 值,深度信息,Stencil值),并和当前render target的深度/Stencil 缓存,得到最后的流水线结果。

Tessellation stage:这个阶段由Hull-shader, tessellator还有domain-shader组成,它主要是将高阶表面转换成一系列的三角行,然后放到流水线中。


Direct3D 11 可编程流水线也可以用高速的计算任务,一个compute shader可以将Direct3D11扩展用于通用GPU计算。


用Shader绘制一个三角形

Direct3D,我们需要完成以下几个步骤:

1.定义我们需要检查的设备类型(device types)和特征级别(feature levels)

2.创建Direct3D设备,渲染设备(context)和交换链(swap chain)。

3.创建渲染目标(render target)。

4.设置视口(viewport)

5.开始渲染

6.渲染模型

7.清屏幕

代码清单

  1. // include the basic windows header files and the Direct3D header files  
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include "assimpmodel.h"  
  8.   
  9. // include the Direct3D Library file  
  10. #pragma comment (lib, "d3d11.lib")  
  11. #pragma comment (lib, "d3dx11.lib")  
  12. #pragma comment (lib, "d3dx10.lib")  
  13.   
  14. // define the screen resolution  
  15. #define SCREEN_WIDTH  800  
  16. #define SCREEN_HEIGHT 600  
  17.   
  18. // global declarations  
  19. IDXGISwapChain *swapchain;             // the pointer to the swap chain interface  
  20. ID3D11Device *dev;                     // the pointer to our Direct3D device interface  
  21. ID3D11DeviceContext *devcon;           // the pointer to our Direct3D device context  
  22. ID3D11RenderTargetView *backbuffer;    // the pointer to our back buffer  
  23. ID3D11InputLayout *pLayout;            // the pointer to the input layout  
  24. ID3D11VertexShader *pVS;               // the pointer to the vertex shader  
  25. ID3D11PixelShader *pPS;                // the pointer to the pixel shader  
  26. ID3D11Buffer *pVBuffer;                // the pointer to the vertex buffer  
  27.   
  28. // a struct to define a single vertex  
  29. struct VERTEX{ D3DXVECTOR3 position; D3DXCOLOR Color; };  
  30.   
  31. // function prototypes  
  32. void InitD3D(HWND hWnd);    // sets up and initializes Direct3D  
  33. void RenderFrame(void);     // renders a single frame  
  34. void CleanD3D(void);        // closes Direct3D and releases memory  
  35. void InitGraphics(void);    // creates the shape to render  
  36. void InitPipeline(void);    // loads and prepares the shaders  
  37.   
  38. // the WindowProc function prototype  
  39. LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);  
  40.   
  41.   
  42. // the entry point for any Windows program  
  43. int WINAPI WinMain(HINSTANCE hInstance,  
  44.     HINSTANCE hPrevInstance,  
  45.     LPSTR lpCmdLine,  
  46.     int nCmdShow)  
  47. {  
  48.     HWND hWnd;  
  49.     WNDCLASSEX wc;  
  50.   
  51.     ZeroMemory(&wc, sizeof(WNDCLASSEX));  
  52.   
  53.     wc.cbSize = sizeof(WNDCLASSEX);  
  54.     wc.style = CS_HREDRAW | CS_VREDRAW;  
  55.     wc.lpfnWndProc = WindowProc;  
  56.     wc.hInstance = hInstance;  
  57.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);  
  58.     wc.lpszClassName = "WindowClass";  
  59.   
  60.     RegisterClassEx(&wc);  
  61.   
  62.     RECT wr = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };  
  63.     AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);  
  64.   
  65.     hWnd = CreateWindowEx(NULL,  
  66.         "WindowClass",  
  67.         "Triangle",  
  68.         WS_OVERLAPPEDWINDOW,  
  69.         300,  
  70.         300,  
  71.         wr.right - wr.left,  
  72.         wr.bottom - wr.top,  
  73.         NULL,  
  74.         NULL,  
  75.         hInstance,  
  76.         NULL);  
  77.   
  78.     ShowWindow(hWnd, nCmdShow);  
  79.   
  80.     // set up and initialize Direct3D  
  81.     InitD3D(hWnd);  
  82.   
  83.     // enter the main loop:  
  84.   
  85.     MSG msg;  
  86.   
  87.     while (TRUE)  
  88.     {  
  89.         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))  
  90.         {  
  91.             TranslateMessage(&msg);  
  92.             DispatchMessage(&msg);  
  93.   
  94.             if (msg.message == WM_QUIT)  
  95.                 break;  
  96.         }  
  97.   
  98.         RenderFrame();  
  99.     }  
  100.   
  101.     // clean up DirectX and COM  
  102.     CleanD3D();  
  103.   
  104.     return msg.wParam;  
  105. }  
  106.   
  107.   
  108. // this is the main message handler for the program  
  109. LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  110. {  
  111.     switch (message)  
  112.     {  
  113.     case WM_DESTROY:  
  114.     {  
  115.                        PostQuitMessage(0);  
  116.                        return 0;  
  117.     } break;  
  118.     }  
  119.   
  120.     return DefWindowProc(hWnd, message, wParam, lParam);  
  121. }  
  122.   
  123.   
  124. // this function initializes and prepares Direct3D for use  
  125. void InitD3D(HWND hWnd)  
  126. {  
  127.     // create a struct to hold information about the swap chain  
  128.     DXGI_SWAP_CHAIN_DESC scd;  
  129.   
  130.     // clear out the struct for use  
  131.     ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));  
  132.   
  133.     // fill the swap chain description struct  
  134.     scd.BufferCount = 1;                                   // one back buffer  
  135.     scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;    // use 32-bit color  
  136.     scd.BufferDesc.Width = SCREEN_WIDTH;                   // set the back buffer width  
  137.     scd.BufferDesc.Height = SCREEN_HEIGHT;                 // set the back buffer height  
  138.     scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;     // how swap chain is to be used  
  139.     scd.OutputWindow = hWnd;                               // the window to be used  
  140.     scd.SampleDesc.Count = 4;                              // how many multisamples  
  141.     scd.Windowed = TRUE;                                   // windowed/full-screen mode  
  142.     scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;    // allow full-screen switching  
  143.   
  144.     // create a device, device context and swap chain using the information in the scd struct  
  145.     D3D11CreateDeviceAndSwapChain(NULL,  
  146.         D3D_DRIVER_TYPE_HARDWARE,  
  147.         NULL,  
  148.         NULL,  
  149.         NULL,  
  150.         NULL,  
  151.         D3D11_SDK_VERSION,  
  152.         &scd,  
  153.         &swapchain,  
  154.         &dev,  
  155.         NULL,  
  156.         &devcon);  
  157.   
  158.   
  159.     // get the address of the back buffer  
  160.     ID3D11Texture2D *pBackBuffer;  
  161.     swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);  
  162.   
  163.     // use the back buffer address to create the render target  
  164.     dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);  
  165.     pBackBuffer->Release();  
  166.   
  167.     // set the render target as the back buffer  
  168.     devcon->OMSetRenderTargets(1, &backbuffer, NULL);  
  169.   
  170.   
  171.     // Set the viewport  
  172.     D3D11_VIEWPORT viewport;  
  173.     ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));  
  174.   
  175.     viewport.TopLeftX = 0;  
  176.     viewport.TopLeftY = 0;  
  177.     viewport.Width = SCREEN_WIDTH;  
  178.     viewport.Height = SCREEN_HEIGHT;  
  179.   
  180.     devcon->RSSetViewports(1, &viewport);  
  181.   
  182.     InitPipeline();  
  183.     InitGraphics();  
  184. }  
  185.   
  186.   
  187. // this is the function used to render a single frame  
  188. void RenderFrame(void)  
  189. {  
  190.     // clear the back buffer to a deep blue  
  191.     devcon->ClearRenderTargetView(backbuffer, D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f));  
  192.   
  193.     // select which vertex buffer to display  
  194.     UINT stride = sizeof(VERTEX);  
  195.     UINT offset = 0;  
  196.     devcon->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);  
  197.   
  198.     // select which primtive type we are using  
  199.     devcon->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);  
  200.   
  201.     // draw the vertex buffer to the back buffer  
  202.     devcon->Draw(3, 0);  
  203.   
  204.     // switch the back buffer and the front buffer  
  205.     swapchain->Present(0, 0);  
  206. }  
  207.   
  208.   
  209. // this is the function that cleans up Direct3D and COM  
  210. void CleanD3D(void)  
  211. {  
  212.     swapchain->SetFullscreenState(FALSE, NULL);    // switch to windowed mode  
  213.   
  214.     // close and release all existing COM objects  
  215.     pLayout->Release();  
  216.     pVS->Release();  
  217.     pPS->Release();  
  218.     pVBuffer->Release();  
  219.     swapchain->Release();  
  220.     backbuffer->Release();  
  221.     dev->Release();  
  222.     devcon->Release();  
  223. }  
  224.   
  225.   
  226. // this is the function that creates the shape to render  
  227. void InitGraphics()  
  228. {  
  229.     //Assimpmodel *model = new Assimpmodel();  
  230.     //model->Initialize(dev);  
  231.     // create a triangle using the VERTEX struct  
  232.     VERTEX OurVertices[] =  
  233.     {  
  234.         { D3DXVECTOR3(0.0f, 0.5f, 0.0f), D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f) },  
  235.         { D3DXVECTOR3(0.45f, -0.5, 0.0f), D3DXCOLOR(0.0f, 1.0f, 0.0f, 1.0f) },  
  236.         { D3DXVECTOR3(-0.45f, -0.5f, 0.0f), D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f) }  
  237.     };  
  238.   
  239.   
  240.     // create the vertex buffer  
  241.     D3D11_BUFFER_DESC bd;  
  242.     ZeroMemory(&bd, sizeof(bd));  
  243.   
  244.     bd.Usage = D3D11_USAGE_DYNAMIC;                // write access access by CPU and GPU  
  245.     bd.ByteWidth = sizeof(VERTEX)* 3;             // size is the VERTEX struct * 3  
  246.     bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;       // use as a vertex buffer  
  247.     bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;    // allow CPU to write in buffer  
  248.   
  249.     dev->CreateBuffer(&bd, NULL, &pVBuffer);       // create the buffer  
  250.   
  251.   
  252.     // copy the vertices into the buffer  
  253.     D3D11_MAPPED_SUBRESOURCE ms;  
  254.     devcon->Map(pVBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);    // map the buffer  
  255.     memcpy(ms.pData, OurVertices, sizeof(OurVertices));                 // copy the data  
  256.     devcon->Unmap(pVBuffer, NULL);                                      // unmap the buffer  
  257. }  
  258.   
  259.   
  260. // this function loads and prepares the shaders  
  261. void InitPipeline()  
  262. {  
  263.     // load and compile the two shaders  
  264.     ID3D10Blob *VS, *PS;  
  265.     D3DX11CompileFromFile("shaders.shader", 0, 0, "VShader""vs_5_0", 0, 0, 0, &VS, 0, 0);  
  266.     D3DX11CompileFromFile("shaders.shader", 0, 0, "PShader""ps_5_0", 0, 0, 0, &PS, 0, 0);  
  267.   
  268.     // encapsulate both shaders into shader objects  
  269.     dev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &pVS);  
  270.     dev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS);  
  271.   
  272.     // set the shader objects  
  273.     devcon->VSSetShader(pVS, 0, 0);  
  274.     devcon->PSSetShader(pPS, 0, 0);  
  275.   
  276.     // create the input layout object  
  277.     D3D11_INPUT_ELEMENT_DESC ied[] =  
  278.     {  
  279.         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },  
  280.         { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },  
  281.     };  
  282.   
  283.     dev->CreateInputLayout(ied, 2, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout);  
  284.     devcon->IASetInputLayout(pLayout);  
  285. }  


shader.shader

  1. struct VOut  
  2. {  
  3.     float4 position : SV_POSITION;  
  4.     float4 color : COLOR;  
  5. };  
  6.   
  7. VOut VShader(float4 position : POSITION, float4 color : COLOR)  
  8. {  
  9.     VOut output;  
  10.   
  11.     output.position = position;  
  12.     output.color = color;  
  13.   
  14.     return output;  
  15. }  
  16.   
  17.   
  18. float4 PShader(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET  
  19. {  
  20.     return color;  
  21. }  

代码就不详细说了,具体可以看参考的链接。

结果如下



封装出一个简单的框架

将所有的代码都写在main.cpp里肯定不是太好,最好按功能抽象出各种类型。下面是参照教程写的一个框架。



简单的一个框架,还是渲染一个小小的三角形,但是是抽了很多个类出来,比如相机,输入之类的,方面后面扩展。

当然还是一个比较槽的框架哈。

代码直接看github吧。


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引