从零开始手敲次世代游戏引擎(十一)

上一篇我们用Direct 2D绘制了一个平面图形。接下来我们用Direct 3D绘制一个3D图形。

首先我们还是通过复制的方法重用我们的代码。拷贝helloengine_d2d.cpp到helloengine_d3d.cpp。然后作如下修改(本文使用的是D3D 11接口,因为直接上D3D 12的话学习难度坡度太大):

@@ -2,13 +2,39 @@
 #include <windows.h>
 #include <windowsx.h>
 #include <tchar.h>
+#include <stdint.h>

-#include <d2d1.h>
+#include <d3d11.h>
+#include <d3d11_1.h>
+#include <d3dcompiler.h>
+#include <DirectXMath.h>
+#include <DirectXPackedVector.h>
+#include <DirectXColors.h>

-ID2D1Factory                   *pFactory = nullptr;
-ID2D1HwndRenderTarget  *pRenderTarget = nullptr;
-ID2D1SolidColorBrush   *pLightSlateGrayBrush = nullptr;
-ID2D1SolidColorBrush   *pCornflowerBlueBrush = nullptr;
+using namespace DirectX;
+using namespace DirectX::PackedVector;
+
+const uint32_t SCREEN_WIDTH  =  960;
+const uint32_t SCREEN_HEIGHT =  480;
+
+// global declarations
+IDXGISwapChain          *g_pSwapchain = nullptr;              // the pointer to the swap chain interface
+ID3D11Device            *g_pDev       = nullptr;              // the pointer to our Direct3D device interface
+ID3D11DeviceContext     *g_pDevcon    = nullptr;              // the pointer to our Direct3D device context
+
+ID3D11RenderTargetView  *g_pRTView    = nullptr;
+
+ID3D11InputLayout       *g_pLayout    = nullptr;              // the pointer to the input layout
+ID3D11VertexShader      *g_pVS        = nullptr;              // the pointer to the vertex shader
+ID3D11PixelShader       *g_pPS        = nullptr;              // the pointer to the pixel shader
+
+ID3D11Buffer            *g_pVBuffer   = nullptr;              // Vertex Buffer
+
+// vertex buffer structure
+struct VERTEX {
+        XMFLOAT3    Position;
+        XMFLOAT4    Color;
+};

 template<class T>
 inline void SafeRelease(T **ppInterfaceToRelease)
@@ -21,32 +47,164 @@ inline void SafeRelease(T **ppInterfaceToRelease)
     }
 }

+void CreateRenderTarget() {
+    HRESULT hr;
+    ID3D11Texture2D *pBackBuffer;
+
+    // Get a pointer to the back buffer
+    g_pSwapchain->GetBuffer( 0, __uuidof( ID3D11Texture2D ),
+                                 ( LPVOID* )&pBackBuffer );
+
+    // Create a render-target view
+    g_pDev->CreateRenderTargetView( pBackBuffer, NULL,
+                                          &g_pRTView );
+    pBackBuffer->Release();
+
+    // Bind the view
+    g_pDevcon->OMSetRenderTargets( 1, &g_pRTView, NULL );
+}
+
+void SetViewPort() {
+    D3D11_VIEWPORT viewport;
+    ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
+
+    viewport.TopLeftX = 0;
+    viewport.TopLeftY = 0;
+    viewport.Width = SCREEN_WIDTH;
+    viewport.Height = SCREEN_HEIGHT;
+
+    g_pDevcon->RSSetViewports(1, &viewport);
+}
+
+// this is the function that loads and prepares the shaders
+void InitPipeline() {
+    // load and compile the two shaders
+    ID3DBlob *VS, *PS;
+
+    D3DReadFileToBlob(L"copy.vso", &VS);
+    D3DReadFileToBlob(L"copy.pso", &PS);
+
+    // encapsulate both shaders into shader objects
+    g_pDev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &g_pVS);
+    g_pDev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &g_pPS);
+
+    // set the shader objects
+    g_pDevcon->VSSetShader(g_pVS, 0, 0);
+    g_pDevcon->PSSetShader(g_pPS, 0, 0);
+
+    // create the input layout object
+    D3D11_INPUT_ELEMENT_DESC ied[] =
     {
+        {"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},
+    };


+    g_pDev->CreateInputLayout(ied, 2, VS->GetBufferPointer(), VS->GetBufferSize(), &g_pLayout);
+    g_pDevcon->IASetInputLayout(g_pLayout);

+    VS->Release();
+    PS->Release();
+}

+// this is the function that creates the shape to render
+void InitGraphics() {
+    // create a triangle using the VERTEX struct
+    VERTEX OurVertices[] =
+    {
+        {XMFLOAT3(0.0f, 0.5f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f)},
+        {XMFLOAT3(0.45f, -0.5, 0.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f)},
+        {XMFLOAT3(-0.45f, -0.5f, 0.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f)}
+    };

+    // create the vertex buffer
+    D3D11_BUFFER_DESC bd;
+    ZeroMemory(&bd, sizeof(bd));

+    bd.Usage = D3D11_USAGE_DYNAMIC;                // write access access by CPU and GPU
+    bd.ByteWidth = sizeof(VERTEX) * 3;             // size is the VERTEX struct * 3
+    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;       // use as a vertex buffer
+    bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;    // allow CPU to write in buffer
+
+    g_pDev->CreateBuffer(&bd, NULL, &g_pVBuffer);       // create the buffer
+
+    // copy the vertices into the buffer
+    D3D11_MAPPED_SUBRESOURCE ms;
+    g_pDevcon->Map(g_pVBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);    // map the buffer
+    memcpy(ms.pData, OurVertices, sizeof(VERTEX) * 3);                       // copy the data
+    g_pDevcon->Unmap(g_pVBuffer, NULL);                                      // unmap the buffer
+}
+
+// this function prepare graphic resources for use
HRESULT CreateGraphicsResources(HWND hWnd)
{
    HRESULT hr = S_OK;
-    if (pRenderTarget == nullptr)
-    {
-        RECT rc;
-        GetClientRect(hWnd, &rc);
-        D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left,
-                        rc.bottom - rc.top);
-        hr = pFactory->CreateHwndRenderTarget(
-            D2D1::RenderTargetProperties(),
-            D2D1::HwndRenderTargetProperties(hWnd, size),
-            &pRenderTarget);
-        if (SUCCEEDED(hr))
-        {
-            hr = pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::LightSlateGray), &pLightSlateGrayBrush);
-        }

-        if (SUCCEEDED(hr))
-        {
-            hr = pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::CornflowerBlue), &pCornflowerBlueBrush);
+    if (g_pSwapchain == nullptr)
+    {
+        // create a struct to hold information about the swap chain
+        DXGI_SWAP_CHAIN_DESC scd;
+
+        // clear out the struct for use
+        ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
+
+        // fill the swap chain description struct
+        scd.BufferCount = 1;                                    // one back buffer
+        scd.BufferDesc.Width = SCREEN_WIDTH;
+        scd.BufferDesc.Height = SCREEN_HEIGHT;
+        scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;     // use 32-bit color
+        scd.BufferDesc.RefreshRate.Numerator = 60;
+        scd.BufferDesc.RefreshRate.Denominator = 1;
+        scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // how swap chain is to be used
+        scd.OutputWindow = hWnd;                                // the window to be used
+        scd.SampleDesc.Count = 4;                               // how many multisamples
+        scd.Windowed = TRUE;                                    // windowed/full-screen mode
+        scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;     // allow full-screen switching
+
+        const D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_1,
+                                                    D3D_FEATURE_LEVEL_11_0,
+                                                    D3D_FEATURE_LEVEL_10_1,
+                                                    D3D_FEATURE_LEVEL_10_0,
+                                                    D3D_FEATURE_LEVEL_9_3,
+                                                    D3D_FEATURE_LEVEL_9_2,
+                                                    D3D_FEATURE_LEVEL_9_1};
+        D3D_FEATURE_LEVEL FeatureLevelSupported;
+
+        HRESULT hr = S_OK;
+
+        // create a device, device context and swap chain using the information in the scd struct
+        hr = D3D11CreateDeviceAndSwapChain(NULL,
+                                      D3D_DRIVER_TYPE_HARDWARE,
+                                      NULL,
+                                      0,
+                                      FeatureLevels,
+                                      _countof(FeatureLevels),
+                                      D3D11_SDK_VERSION,
+                                      &scd,
+                                      &g_pSwapchain,
+                                      &g_pDev,
+                                      &FeatureLevelSupported,
+                                      &g_pDevcon);
+
+        if (hr == E_INVALIDARG) {
+            hr = D3D11CreateDeviceAndSwapChain(NULL,
+                                      D3D_DRIVER_TYPE_HARDWARE,
+                                      NULL,
+                                      0,
+                                      &FeatureLevelSupported,
+                                      1,
+                                      D3D11_SDK_VERSION,
+                                      &scd,
+                                      &g_pSwapchain,
+                                      &g_pDev,
+                                      NULL,
+                                      &g_pDevcon);
+        }
+
+        if (hr == S_OK) {
+            CreateRenderTarget();
+            SetViewPort();
+            InitPipeline();
+            InitGraphics();
         }
     }
     return hr;
@@ -54,11 +212,40 @@ HRESULT CreateGraphicsResources(HWND hWnd)

 void DiscardGraphicsResources()
 {
-    SafeRelease(&pRenderTarget);
-    SafeRelease(&pLightSlateGrayBrush);
-    SafeRelease(&pCornflowerBlueBrush);
+    SafeRelease(&g_pLayout);
+    SafeRelease(&g_pVS);
+    SafeRelease(&g_pPS);
+    SafeRelease(&g_pVBuffer);
+    SafeRelease(&g_pSwapchain);
+    SafeRelease(&g_pRTView);
+    SafeRelease(&g_pDev);
+    SafeRelease(&g_pDevcon);
 }

+// this is the function used to render a single frame
+void RenderFrame()
+{
+    // clear the back buffer to a deep blue
+    const FLOAT clearColor[] = {0.0f, 0.2f, 0.4f, 1.0f};
+    g_pDevcon->ClearRenderTargetView(g_pRTView, clearColor);
+
+    // do 3D rendering on the back buffer here
+    {
+        // select which vertex buffer to display
+        UINT stride = sizeof(VERTEX);
+        UINT offset = 0;
+        g_pDevcon->IASetVertexBuffers(0, 1, &g_pVBuffer, &stride, &offset);
+
+        // select which primtive type we are using
+        g_pDevcon->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+
+        // draw the vertex buffer to the back buffer
+        g_pDevcon->Draw(3, 0);
+    }
+
+    // swap the back buffer and the front buffer
+    g_pSwapchain->Present(0, 0);
+}

 // the WindowProc function prototype
 LRESULT CALLBACK WindowProc(HWND hWnd,
@@ -77,9 +264,6 @@ int WINAPI WinMain(HINSTANCE hInstance,
     // this struct holds information for the window class
     WNDCLASSEX wc;

-    // initialize COM
-    if (FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) return -1;
-
     // clear out the window class for use
     ZeroMemory(&wc, sizeof(WNDCLASSEX));

@@ -97,17 +281,17 @@ int WINAPI WinMain(HINSTANCE hInstance,

     // create the window and use the result as the handle
     hWnd = CreateWindowEx(0,
-                          _T("WindowClass1"),    // name of the window class
-                          _T("Hello, Engine![Direct 2D]"),   // title of the window
-                          WS_OVERLAPPEDWINDOW,    // window style
-                          100,    // x-position of the window
-                          100,    // y-position of the window
-                          960,    // width of the window
-                          540,    // height of the window
-                          NULL,    // we have no parent window, NULL
-                          NULL,    // we aren't using menus, NULL
-                          hInstance,    // application handle
-                          NULL);    // used with multiple windows, NULL
+                          _T("WindowClass1"),                   // name of the window class
+                          _T("Hello, Engine![Direct 3D]"),      // title of the window
+                          WS_OVERLAPPEDWINDOW,                  // window style
+                          100,                                  // x-position of the window
+                          100,                                  // y-position of the window
+                          SCREEN_WIDTH,                         // width of the window
+                          SCREEN_HEIGHT,                        // height of the window
+                          NULL,                                 // we have no parent window, NULL
+                          NULL,                                 // we aren't using menus, NULL
+                          hInstance,                            // application handle
+                          NULL);                                // used with multiple windows, NULL

     // display the window on the screen
     ShowWindow(hWnd, nCmdShow);
@@ -127,9 +311,6 @@ int WINAPI WinMain(HINSTANCE hInstance,
         DispatchMessage(&msg);
     }

-    // uninitialize COM
-    CoUninitialize();
-
     // return this part of the WM_QUIT message to Windows
     return msg.wParam;
 }
@@ -144,108 +325,27 @@ LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
     switch(message)
     {
        case WM_CREATE:
-               if (FAILED(D2D1CreateFactory(
-                                       D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory)))
-               {
-                       result = -1; // Fail CreateWindowEx.
-               }
                wasHandled = true;
-        result = 1;
         break;

        case WM_PAINT:
-           {
-                       HRESULT hr = CreateGraphicsResources(hWnd);
-                       if (SUCCEEDED(hr))
-                       {
-                               PAINTSTRUCT ps;
-                               BeginPaint(hWnd, &ps);
-
-                               // start build GPU draw command
-                               pRenderTarget->BeginDraw();
-
-                               // clear the background with white color
-                               pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
-
-                // retrieve the size of drawing area
-                D2D1_SIZE_F rtSize = pRenderTarget->GetSize();
-
-                // draw a grid background.
-                int width = static_cast<int>(rtSize.width);
-                int height = static_cast<int>(rtSize.height);
-
-                for (int x = 0; x < width; x += 10)
-                {
-                    pRenderTarget->DrawLine(
-                        D2D1::Point2F(static_cast<FLOAT>(x), 0.0f),
-                        D2D1::Point2F(static_cast<FLOAT>(x), rtSize.height),
-                        pLightSlateGrayBrush,
-                        0.5f
-                        );
-                }
-
-                for (int y = 0; y < height; y += 10)
-                {
-                    pRenderTarget->DrawLine(
-                        D2D1::Point2F(0.0f, static_cast<FLOAT>(y)),
-                        D2D1::Point2F(rtSize.width, static_cast<FLOAT>(y)),
-                        pLightSlateGrayBrush,
-                        0.5f
-                        );
-                }
-
-                // draw two rectangles
-                D2D1_RECT_F rectangle1 = D2D1::RectF(
-                     rtSize.width/2 - 50.0f,
-                     rtSize.height/2 - 50.0f,
-                     rtSize.width/2 + 50.0f,
-                     rtSize.height/2 + 50.0f
-                     );
-
-                 D2D1_RECT_F rectangle2 = D2D1::RectF(
-                     rtSize.width/2 - 100.0f,
-                     rtSize.height/2 - 100.0f,
-                     rtSize.width/2 + 100.0f,
-                     rtSize.height/2 + 100.0f
-                     );
-
-                // draw a filled rectangle
-                pRenderTarget->FillRectangle(&rectangle1, pLightSlateGrayBrush);
-
-                // draw a outline only rectangle
-                pRenderTarget->DrawRectangle(&rectangle2, pCornflowerBlueBrush);
-
-                               // end GPU draw command building
-                               hr = pRenderTarget->EndDraw();
-                               if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET)
-                               {
-                                       DiscardGraphicsResources();
-                               }
-
-                               EndPaint(hWnd, &ps);
-                       }
-           }
+               result = CreateGraphicsResources(hWnd);
+               RenderFrame();
                wasHandled = true;
         break;

        case WM_SIZE:
-               if (pRenderTarget != nullptr)
+               if (g_pSwapchain != nullptr)
                {
-                       RECT rc;
-                       GetClientRect(hWnd, &rc);
-
-                       D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
-
-                       pRenderTarget->Resize(size);
+                   DiscardGraphicsResources();
                }
                wasHandled = true;
         break;

        case WM_DESTROY:
                DiscardGraphicsResources();
-               if (pFactory) {pFactory->Release(); pFactory=nullptr; }
                PostQuitMessage(0);
-        result = 1;
                wasHandled = true;
         break;

简单解释一下:

(关于D3D编程的系统教程在这里:Getting Started with Direct3D

基本窗体创建没有任何变化。因为不是COM,不需要创建Factory。(需要考证。从结果上来说是这样,但是这里不需要COM相关的代码的原因应该是D3D11的库当中已经进行了相关的处理。因为D3D12明显是COM)所以WM_CREATE里面基本不做任何事情。

新加了如下几个函数(子过程):

         CreateRenderTarget();
            SetViewPort();
            InitPipeline();
            InitGraphics();

第一个依然是我们熟悉的,创建RenderTarget,也就是画布。

第二个是设置视口。也就是设置渲染结果在画布当中的映射。我们目前是将整个画布都分配给了一个视口。在实际的游戏开发当中,会有多人分屏游玩的模式。这个时候就需要把一张画布分割成好几个视口。另外一个典型的运用就是VR。VR需要绘制左眼和右眼两幅图像,因此也需要将画布分割为两个视口。

第三个是初始化渲染管道。渲染管道就是GPU的工作流水线。使用GPU进行3D渲染的时候,最一般的会有顶点变换,像素化和像素填色这3个阶段。在这个过程当中,顶点变换和填色是可以编程的(而像素化是硬件固定功能,不可编程)。在这个初始化函数里面,我们可以看到我们从磁盘读取了两个GPU用的程序,一个叫“copy.vso”,一个叫”copy.pso”。它们分别对应着GPU的顶点变换阶段(Vertex Shading)和像素填色阶段(Pixel Shading)。这两个程序是我们编写的,使用的语言是HLSL(这是一种类似C语言的,微软推出的GPU编程语言)。具体内容在下面说明。

第四个则是传入实际要绘制的模型的顶点信息了。我们这里绘制的是一个三角形,因此有3个顶点。注意在D3D当中,使用的坐标系为左手坐标系,就是x轴向右,y轴向上,z轴指向屏幕里面。(这点很特别,今后写别的图形API的时候就有比较)

Coordinate Systems (Direct3D 9)

我们的顶点结构是这样的:

// vertex buffer structure
struct VERTEX {
        XMFLOAT3    Position;
        XMFLOAT4    Color;
};

因此,我们是这样初始化顶点的:

 // create a triangle using the VERTEX struct
    VERTEX OurVertices[] =
    {
        {XMFLOAT3(0.0f, 0.5f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f)},
        {XMFLOAT3(0.45f, -0.5, 0.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f)},
        {XMFLOAT3(-0.45f, -0.5f, 0.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f)}
    };

坐标系的原点在视口的中心,因为我们只有一个视口,所以就是屏幕中心。视口在各个坐标轴的缺省的范围是[-1, 1],因此0.5差不多正好是在画布长宽各1/4的地方。第二个部分是颜色。分别对应R(红色通道)G(绿色通道)B(蓝色通道)和A(透明通道)。这个顺序是在代码的layout部分指定的。范围是[0, 1]。所以我们可以看到

  1. 第一个顶点是上方中央,红色
  2. 第二个顶点是下方右侧,绿色
  3. 第三个顶点是下方左侧,蓝色

然后我们来看我们所写的GPU程序(Shader,称为着色器)

copy.vs

#include "cbuffer.h"
#include "vsoutput.hs"

v2p main(a2v input) {
	v2p output;
	output.position = float4(input.position, 1.0);
	output.color = input.color;

	return output;
}

看到这个程序基本就是将输入原样输出。其中输入是来自我们的应用程序(就是我们上面定义的VERTEX),而输出是输出给流水线的下一个步骤。在我们这个例子里面,就是像素着色器:

copy.ps

#include "vsoutput.hs"

float4 main(v2p input) : SV_TARGET
{
    return input.color;
}

而像素着色器也只是原样输出输入的颜色。

注意到我们用到了两个头文件。一个是定义应用程序传给Vertex Shader的数据结构,一个是定义Vertex Shader输出给Pixel Shader的数据结构。内容如下:

cbuffer.h

struct a2v {
	float3 position : POSITION;
	float4 color	: COLOR;
};

vsoutput.hs

struct v2p {
	float4 position : SV_POSITION;
	float4 color	: COLOR;
};

我们可以看到,这里面有一些奇怪的,标准C/C++不支持的东西。就是冒号后面的那些东西。这些东西是用来将变量和GPU的寄存器进行绑定的。因为GPU并不是全部可编程的,整个处理流水线当中混杂着可编程的环节和不可编程的环节。因此,当我们的输出要提供给不可编程的环节使用的时候(比如Vertex Shader的输出当中的position会被像素化模块用来插值计算三角形内部的点的坐标;比如Pixel Shader输出的color会被GPU的显示输出模块用来输出画面),就需要将这些变量绑定到一些事先定义好的寄存器当中去。

具体细节,请参考HLSL教程:

HLSL (Windows)

以及GPU渲染管道的说明:

en.m.wikipedia.org/wiki

在Windows当中,编译Shader(D3D规格)的方法如下:

fxc /T vs_5_0 /Zi /Fo copy.vso copy.vs
fxc /T ps_5_0 /Zi /Fo copy.pso copy.ps

如果找不到fxc.exe,那么应该是没有安装DirectX相关的开发包。重新运行Visual Studio选择安装就可以了。

代码编译的方法如下(调试版)(编译出现大量DirectXMath相关的错误的话,继续看下面):

D:wenliSourceReposGameEngineFromScratchPlatformWindows>clang-cl -I./DirectXMath/Inc -c -Z7 -o helloengine_d3d.obj helloengine_d3d.cpp
D:wenliSourceReposGameEngineFromScratchPlatformWindows>link -debug user32.lib d3d11.lib d3dcompiler.lib helloengine_d3d.obj

Release版:

D:wenliSourceReposGameEngineFromScratchPlatformWindows>clang -I./DirectXMath/Inc -l user32.lib -l d3d11.lib -l d3dcompiler.lib -o helloengine_d3d.exe helloengine_d3d.cpp

Direct 3D工作的方式与Direct 2D不同,不是采用COM的方式,(需要考证。从结果上来说确实不需要链接ole32.lib。但是有可能是D3D11的库里面包括了这一部分)而是直接调用显卡的驱动。所以我们去掉COM相关的初始化代码,链接的时候也去掉ole32.lib。

注意我们这里加入了一个新的头文件目录:./DirectXMath。这是因为目前随Visual Studio安装的(正确来说应该是随Windows SDK安装的)DirectXMath库似乎版本还是比较老的,不支持clang的编译(就是说用了clang所不支持的特性)。所以我们需要从github上面下载一个最新的版本:

D:wenliSourceReposGameEngineFromScratchPlatformWindows>git submodule add https://github.com/Microsoft/DirectXMath.git DirectXMath
D:wenliSourceReposGameEngineFromScratchPlatformWindows>git submodule init DirectXMath
D:wenliSourceReposGameEngineFromScratchPlatformWindows>git submodule update DirectXMath

然后重新编译应该就好了。

运行的效果如下:

三角形内部出现了漂亮的过渡色。这是因为我们程序给GPU的只有3个顶点,对于其它的点,GPU是根据它到3个顶点的距离(准确说是重心坐标,就是从被计算的点向3个顶点作辅助线,从而整个3角形被划分为3个小三角形(如果点在三角形边缘的时候会出现面积为0的塌缩三角形),每个小三角性的面积除以原本的大三角形的面积,得到3个[0,1]之间的值,根据这个值加权平均3个顶点的颜色)来进行插值运算的。这种插值运算不仅仅发生在position这个参数当中,也发生在color这个参数当中。所以最终就形成了这么一种结果。

好了。我们已经跨入了3D的殿堂了。保存我们的代码,准备下一个部分。

参考引用:

  1. DirectX 11 Tutorials
  2. Direct3D Tutorials

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

您正在使用您的 WordPress.com 账号评论。 登出 /  更改 )

Google photo

您正在使用您的 Google 账号评论。 登出 /  更改 )

Twitter picture

您正在使用您的 Twitter 账号评论。 登出 /  更改 )

Facebook photo

您正在使用您的 Facebook 账号评论。 登出 /  更改 )

Connecting to %s

%d 博主赞过: