DirectX 10 教程3:初始化DirectX 10



源代码:dx10tut03.zip

本教程开始介绍真正的DirectX 10编程,但只涉及两件事:初始化Direct3D和关闭Direct3D。

更新后的框架

我们需要在在框架中添加一个类用于处理所有Direct3D方法,名称为D3Dclass类。结构图如下:

框架结构图

框架结构图

如你所见,D3Dclass位于GraphicsClass之中。前面的教程我们说过所有与图形相关的类都会封装在GraphicsClass中,所以新的D3Dclass类也应放入其中。

下面看一下GraphicsClass的改变:

Graphicsclass.h


1
2
3
4
5
// Filename: graphicsclass.h
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_

下面是第一个变化,我们取消了windows.h,取而代之的是d3dclass.h。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
///
// MY CLASS INCLUDES //
///
#include "d3dclass.h"
 
 
/
// GLOBALS //
/
const bool FULL_SCREEN = false ;
const bool VSYNC_ENABLED = true ;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;
 
 
// Class name: GraphicsClass
class GraphicsClass
{
public :
     GraphicsClass();
     GraphicsClass( const GraphicsClass&);
     ~GraphicsClass();
 
     bool Initialize( int , int , HWND );
     void Shutdown();
     bool Frame();
 
private :
     bool Render();
 
private :

第二个变化是新的指向D3Dclass的指针,名称为m_D3D。我在所有的类变量之前都加了前缀m_,这样当我编写代码时就可以知道哪些是类成员变量而哪些不是。


1
2
3
4
     D3DClass* m_D3D;
};
 
#endif

Graphicsclass.cpp

上一个教程中的这个类是空的,没有任何代码。现在我们就需要在GraphicsClass类中编写代码进行D3Dclass对象的初始化和关闭。我们还在Render方法中调用BeginScene和EndScene,这样就可以使用Direct3D进行绘制了。第一个变化时在构造函数中,首先基于安全的考虑将指针初始化为null,所有类指针我们都是这样处理的。


1
2
3
4
GraphicsClass::GraphicsClass()
{
     m_D3D = 0;
}

第二个变化在Initialize方法中。我们创建了D3Dclass对象并调用它的Initialize方法,传递的参数为屏幕宽度、屏幕高度,窗口句柄和Graphicsclass.h文件中的4个全局变量。D3Dclass会根据这些参数创建Direct3D系统。在说到d3dclass文件时会涉及更多细节。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool GraphicsClass::Initialize( int screenWidth, int screenHeight, HWND hwnd)
{
     bool result;
 
 
     // Create the Direct3D object.
     m_D3D = new D3DClass;
     if (!m_D3D)
     {
         return false ;
     }
 
     // Initialize the Direct3D object.
     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 ;
}

下一个变化是GraphicsClass中的Shutdown方法。因为所有图形对象的关闭操作位于这个方法内,所以也需将D3Dclass类的关闭代码放入其中。我还检查了指针是否被初始化,如果没有就无需关闭了,这也就是为什么在类的构造函数中将所有指针设为null的原因。如果发现指针已经被初始化后才会关闭D3Dclass并清理指针。


1
2
3
4
5
6
7
8
9
10
11
12
void GraphicsClass::Shutdown()
{
     // Release the D3D object.
     if (m_D3D)
     {
         m_D3D->Shutdown();
         delete m_D3D;
         m_D3D = 0;
     }
 
     return ;
}

Frame方法也更新为可以在每帧调用Render方法。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool GraphicsClass::Frame()
{
     bool result;
 
 
     // Render the graphics scene.
     result = Render();
     if (!result)
     {
         return false ;
     }
 
     return true ;
}

最后一个变化时在Render方法中。我们调用D3D对象将屏幕清除为灰色,然后调用EndScene将会在显示在屏幕上。


1
2
3
4
5
6
7
8
9
10
11
bool GraphicsClass::Render()
{
     // Clear the buffers to begin the scene.
     m_D3D->BeginScene(0.5f, 0.5f, 0.5f, 1.0f);
 
 
     // Present the rendered scene to the screen.
     m_D3D->EndScene();
 
     return true ;
}

下面看一下D3Dclass的头文件:

D3dclass.h


1
2
3
4
5
// Filename: d3dclass.h
#ifndef _D3DCLASS_H_
#define _D3DCLASS_H_

首先要指定链接的库。前两个库包含了创建并绘制3D图像的所有Direct3D函数。第三个库包含了一些工具,这些工具可以获取诸如屏幕刷新率、使用的显卡等硬件信息。


1
2
3
4
5
6
/
// LINKING //
/
#pragma comment(lib, "d3d10.lib")
#pragma comment(lib, "d3dx10.lib")
#pragma comment(lib, "dxgi.lib")

下一步需要包含这些库的头文件,只需用到d3d10.h和d3dx10.h。


1
2
3
4
5
//
// INCLUDES //
//
#include <d3d10.h>
#include <d3dx10.h>

D3Dclass的类定义尽量保持简单。它有常规的构造函数,复制的构造函数和析构函数。更重要的是Initialize和Shutdown方法,本教程我们主要介绍这两个方法。还有几个本教程中不重要的辅助方法和几个私有变量。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Class name: D3DClass
class D3DClass
{
public :
     D3DClass();
     D3DClass( const D3DClass&);
     ~D3DClass();
 
     bool Initialize( int , int , bool , HWND , bool , float , float );
     void Shutdown();
     
     void BeginScene( float , float , float , float );
     void EndScene();
 
     ID3D10Device* GetDevice();
 
     void GetProjectionMatrix(D3DXMATRIX&);
     void GetWorldMatrix(D3DXMATRIX&);
     void GetOrthoMatrix(D3DXMATRIX&);
 
     void GetVideoCardInfo( char *, int &);
 
private :
     bool m_vsync_enabled;
     int m_videoCardMemory;
     char m_videoCardDescription[128];
     IDXGISwapChain* m_swapChain;
     ID3D10Device* m_device;
     ID3D10RenderTargetView* m_renderTargetView;
     ID3D10Texture2D* m_depthStencilBuffer;
     ID3D10DepthStencilState* m_depthStencilState;
     ID3D10DepthStencilView* m_depthStencilView;
     ID3D10RasterizerState* m_rasterState;
     D3DXMATRIX m_projectionMatrix;
     D3DXMATRIX m_worldMatrix;
     D3DXMATRIX m_orthoMatrix;
};
 
#endif

对那些熟悉Direct3D的人可能会注意到这个类中并没有视矩阵变量,我把它放在了以后教程中会编写的camera类中了。

D3dclass.cpp


1
2
3
4
// Filename: d3dclass.cpp
#include "d3dclass.h"

与其他类一样,我们首先在构造函数中将所有指针初始化为null。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
D3DClass::D3DClass()
{
     m_device = 0;
     m_swapChain = 0;
     m_renderTargetView = 0;
     m_depthStencilBuffer = 0;
     m_depthStencilState = 0;
     m_depthStencilView = 0;
     m_rasterState = 0;
}
 
 
D3DClass::D3DClass( const D3DClass& other)
{
}
 
 
D3DClass::~D3DClass()
{
}

Initialize方法用于创建Direct3D。我将所有必需代码放在此处,还有一些东西是用于后面的教程的。这个方法的参数screenWidth和screenHeight是窗体的宽和高,它们是在SystemClass中创建的。Hwnd变量为窗体的句柄,Direct3D需要这个句柄访问创建的窗体。Fullscreen变量表示是否全屏,Direct3D需要这个信息创建窗体。screenDepth和screenNear表示3D环境的深度设置。Vsync变量表示Direct3D的绘制是根据屏幕刷新率还是尽可能快地绘制。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
     ID3D10Texture2D* backBufferPtr;
     D3D10_TEXTURE2D_DESC depthBufferDesc;
     D3D10_DEPTH_STENCIL_DESC depthStencilDesc;
     D3D10_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
     D3D10_VIEWPORT viewport;
     float fieldOfView, screenAspect;
     D3D10_RASTERIZER_DESC rasterDesc;
 
 
     // Store the vsync setting.
     m_vsync_enabled = vsync;

在初始化Direct3D前我们必须从显卡/显示器中获取刷新率。每台电脑都有所不同,因此我们需要查询这个信息。我们需要查询分子和分母的值,将它们传递到DirectX,然后就能计算出正确地刷新率。如果没有这步而是设置一个当前计算机上可能不存在的默认值,DirectX的性能会下降,并会在调试时显示错误。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Create a DirectX graphics interface factory.
     result = CreateDXGIFactory(__uuidof(IDXGIFactory), ( void **)&factory);
     if (FAILED(result))
     {
         return false ;
     }
 
     // Use the factory to create an adapter for the primary graphics interface (video card).
     result = factory->EnumAdapters(0, &adapter);
     if (FAILED(result))
     {
         return false ;
     }
 
     // Enumerate the primary adapter output (monitor).
     result = adapter->EnumOutputs(0, &adapterOutput);
     if (FAILED(result))
     {
         return false ;
     }
 
     // Get the number of modes that fit the DXGI_FORMAT_R8G8B8A8_UNORM display format for the adapter output (monitor).
     result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM,
         DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
     if (FAILED(result))
     {
         return false ;
     }
 
     // Create a list to hold all the possible display modes for this monitor/video card combination.
     displayModeList = new DXGI_MODE_DESC[numModes];
     if (!displayModeList)
     {
         return false ;
     }
 
     // Now fill the display mode list structures.
     result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM,
         DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList);
     if (FAILED(result))
     {
         return false ;
     }
 
     // Now go through all the display modes and find the one that matches the screen width and height.
     // When a match is found store the numerator and denominator of the refresh rate for that monitor.
     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;
             }
         }
     }

有了刷新率的分子和分母,最后要获得的就是显卡的名称和显存的大小。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Get the adapter (video card) description.
result = adapter->GetDesc(&adapterDesc);
if (FAILED(result))
{
     return false ;
}
 
// Store the dedicated video card memory in megabytes.
m_videoCardMemory = ( int )(adapterDesc.DedicatedVideoMemory / 1024 / 1024);
 
// Convert the name of the video card to a character array and store it.
error = wcstombs_s(&stringLength, m_videoCardDescription, 128, adapterDesc.Description, 128);
if (error != 0)
{
     return false ;
}

有了刷新率分子和分母信息和显卡信息,我们就可以释放所用的结构体和接口了。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Release the display mode list.
delete [] displayModeList;
displayModeList = 0;
 
// Release the adapter output.
adapterOutput->Release();
adapterOutput = 0;
 
// Release the adapter.
adapter->Release();
adapter = 0;
 
// Release the factory.
factory->Release();
factory = 0;

有了刷新率就可以开始初始化DirectX了。首先需要填充交换链的描述。交换链就是前缓存和后备缓存。通常使用一块后备缓存,将内容绘制其上,然后将内容交换到前缓存,并显示在屏幕上,这也就是称为交换链的原因。


1
2
3
4
5
6
7
8
9
10
11
12
// Initialize the swap chain description.
ZeroMemory(&swapChainDesc, sizeof (swapChainDesc));
 
// Set to a single back buffer.
swapChainDesc.BufferCount = 1;
 
// Set the width and height of the back buffer.
swapChainDesc.BufferDesc.Width = screenWidth;
swapChainDesc.BufferDesc.Height = screenHeight;
 
// Set regular 32-bit surface for the back buffer.
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

交换链描述的下一部分是刷新率。刷新率表示一秒内将后备缓存中的内容绘制到前缓存的次数。如果vsync设置为true,则刷新率锁定为系统设置(比方说60hz),这意味着每秒绘制60次(系统刷新率越高次数越多)。如果将vsync设置为false,则以尽可能快的速度绘制,但这样做会导致图像出现瑕疵。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Set the refresh rate of the back buffer.
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;
}
 
// Set the usage of the back buffer.
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 
// Set the handle for the window to render to.
swapChainDesc.OutputWindow = hwnd;
 
// Turn multisampling off.
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
 
// Set to full screen or windowed mode.
if (fullscreen)
{
     swapChainDesc.Windowed = false ;
}
else
{
     swapChainDesc.Windowed = true ;
}
 
// Set the scan line ordering and scaling to unspecified.
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
 
// Discard the back buffer contents after presenting.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
 
// Don't set the advanced flags.
swapChainDesc.Flags = 0;

填充完交换链描述后,就可以创建Direct3D设备了。Direct3D设备非常重要,它是调用所有Direct3D方法的接口。


1
2
3
4
5
6
7
// Create the swap chain and the Direct3D device.
result = D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION,
                                        &swapChainDesc, &m_swapChain, &m_device);
if (FAILED(result))
{
     return false ;
}

有了设备和交换链后,我们需要一个指向后备缓存的指针,然后将它链接到交换链。我们使用CreateRenderTargetView方法将后备缓存链接到交换链。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Get the pointer to the back buffer.
result = m_swapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), ( LPVOID *)&backBufferPtr);
if (FAILED(result))
{
     return false ;
}
 
// Create the render target view with the back buffer pointer.
result = m_device->CreateRenderTargetView(backBufferPtr, NULL, &m_renderTargetView);
if (FAILED(result))
{
     return false ;
}
 
// Release pointer to the back buffer as we no longer need it.
backBufferPtr->Release();
backBufferPtr = 0;

我们还需要创建一个深度缓存描述,用来创建一个深度缓存。同时,还将一个模板缓存链接到了深度缓存。模板缓存可以用来实现诸如运动模糊、体积阴影等效果。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Initialize the description of the depth buffer.
ZeroMemory(&depthBufferDesc, sizeof (depthBufferDesc));
 
// Set up the description of the depth buffer.
depthBufferDesc.Width = screenWidth;
depthBufferDesc.Height = screenHeight;
depthBufferDesc.MipLevels = 1;
depthBufferDesc.ArraySize = 1;
depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthBufferDesc.SampleDesc.Count = 1;
depthBufferDesc.SampleDesc.Quality = 0;
depthBufferDesc.Usage = D3D10_USAGE_DEFAULT;
depthBufferDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL;
depthBufferDesc.CPUAccessFlags = 0;
depthBufferDesc.MiscFlags = 0;

现在我们已经创建了深度/模板缓存。我们使用了CreateTexture2D方法创建了缓存,因此缓存其实是一张2D纹理。这是因为当绘制的多边形光栅化之后,它们其实就是在2D缓存中的有颜色的像素,绘制到屏幕上的就是这个2D缓存。


1
2
3
4
5
6
// Create the texture for the depth buffer using the filled out description.
result = m_device->CreateTexture2D(&depthBufferDesc, NULL, &m_depthStencilBuffer);
if (FAILED(result))
{
     return false ;
}

现在设置深度模板描述,让我们可以控制深度测试的类型。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Initialize the description of the stencil state.
ZeroMemory(&depthStencilDesc, sizeof (depthStencilDesc));
 
// Set up the description of the stencil state.
depthStencilDesc.DepthEnable = true ;
depthStencilDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
depthStencilDesc.DepthFunc = D3D10_COMPARISON_LESS;
 
depthStencilDesc.StencilEnable = true ;
depthStencilDesc.StencilReadMask = 0xFF;
depthStencilDesc.StencilWriteMask = 0xFF;
 
// Stencil operations if pixel is front-facing.
depthStencilDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_INCR;
depthStencilDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
 
// Stencil operations if pixel is back-facing.
depthStencilDesc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_DECR;
depthStencilDesc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilFunc = D3D10_COMPARISON_ALWAYS;

填充完描述后就可以创建深度模板状态了。


1
2
3
4
5
6
// Create the depth stencil state.
result = m_device->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);
if (FAILED(result))
{
     return false ;
}

创建了深度模板状态之后,我们就可以将它设置到Direct3D设备了。


1
2
// Set the depth stencil state on the D3D device.
m_device->OMSetDepthStencilState(m_depthStencilState, 1);

最后需要创建深度模板缓存的视图,这样Direct3D就能知道将深度缓存作为一个深度模板纹理,之后就可以通过Direct3D设备调用CreateDepthStencilView方法了。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Initailze the depth stencil view.
ZeroMemory(&depthStencilViewDesc, sizeof (depthStencilViewDesc));
 
// Set up the depth stencil view description.
depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilViewDesc.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Texture2D.MipSlice = 0;
 
// Create the depth stencil view.
result = m_device->CreateDepthStencilView(m_depthStencilBuffer, &depthStencilViewDesc, &m_depthStencilView);
if (FAILED(result))
{
     return false ;
}

最后就可以调用OMSetRenderTargets,这个方法将渲染目标视图和深度模板缓存绑定到渲染管道,通过这种方法将图形管道绘制的图像传送到后备缓存。写入到后备缓存之后,就可以将它交换到前缓存,在屏幕上显示内容。


1
2
// Bind the render target view and depth stencil buffer to the output render pipeline.
m_device->OMSetRenderTargets(1, &m_renderTargetView, m_depthStencilView);

创建了渲染目标之后,我们可以继续做一些额外的工作,赋予更多的控制权,为下面的教程做准备。首先要创建的是光栅状态,让我们可以控制多边形的绘制方式,比如绘制为网格或同时绘制多边形的前后表面。默认状态时DirectX已经有了一个光栅状态,除非你自己创建一个,否则无法改变这个状态。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
     // Setup the raster description which will determine how
//and what polygons will be drawn.
     rasterDesc.AntialiasedLineEnable = false ;
     rasterDesc.CullMode = D3D10_CULL_BACK;
     rasterDesc.DepthBias = 0;
     rasterDesc.DepthBiasClamp = 0.0f;
     rasterDesc.DepthClipEnable = true ;
     rasterDesc.FillMode = D3D10_FILL_SOLID;
     rasterDesc.FrontCounterClockwise = false ;
     rasterDesc.MultisampleEnable = false ;
     rasterDesc.ScissorEnable = false ;
     rasterDesc.SlopeScaledDepthBias = 0.0f;
 
     // Create the rasterizer state from the description we just filled out.
     result = m_device->CreateRasterizerState(&rasterDesc, &m_rasterState);
     if (FAILED(result))
     {
         return false ;
     }
 
     // Now set the rasterizer state.
     m_device->RSSetState(m_rasterState);

视口也需要被设置,这样Direct3D就可以将剪裁空间坐标映射到渲染目标空间,下面的代码设置为整个窗口尺寸。


1
2
3
4
5
6
7
8
9
10
// Setup the viewport for rendering.
viewport.Width = screenWidth;
viewport.Height = screenHeight;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
 
// Create the viewport.
m_device->RSSetViewports(1, &viewport);

下面我们将创建投影矩阵。投影矩阵用来将世界空间变换到2D视口空间。我们需要保存这个矩阵的副本,这样就可以将它传递到shader中。


1
2
3
4
5
6
// Setup the projection matrix.
fieldOfView = ( float )D3DX_PI / 4.0f;
screenAspect = ( float )screenWidth / ( float )screenHeight;
 
// Create the projection matrix for 3D rendering.
D3DXMatrixPerspectiveFovLH(&m_projectionMatrix, fieldOfView, screenAspect, screenNear, screenDepth);

我们还需创建一个世界矩阵,用来将模型的顶点从对象空间转换到世界空间。这个矩阵还用来旋转、平移和缩放3D场景中的物体。开始时可以将这个矩阵设置为单位矩阵并保存它的副本,这个副本需要传递到shader用于绘制。


1
2
// Initialize the world matrix to the identity matrix.
D3DXMatrixIdentity(&m_worldMatrix);

通常还需要创建一个视矩阵。视矩阵用来计算我们观察世界的位置,你可以将它想象成一个相机,你只能看到这个相机中能看到的动向。因此我会在以后的教程汇一个在camera类中创建这个矩阵,这样更符合逻辑,本教程不需要视矩阵。

在Initialize方法中要创建的最后一个东西是正交投影矩阵,这个矩阵用来绘制平面上的输入用户界面之类的2D内容,下一个教程中我们会在平面上绘制一个2D图像。


1
2
3
4
5
     // Create an orthographic projection matrix for 2D rendering.
     D3DXMatrixOrthoLH(&m_orthoMatrix, ( float )screenWidth, ( float )screenHeight, screenNear, screenDepth);
 
     return true ;
}

Shutdown方法释放并清理所有在Initialize方法中用到的指针,它非常简单。但是在清理之前我放置了一个调用,强制交换链变为窗口模式。如果没有这步且在全屏模式释放交换链时,程序会报错。为了避免这个错误,在关闭Direct3D前我总是强制进入窗口模式。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
void D3DClass::Shutdown()
{
     // Before shutting down set to windowed mode or when you release the swap chain it will throw an exception.
     if (m_swapChain)
     {
         m_swapChain->SetFullscreenState( false , NULL);
     }
 
     if (m_rasterState)
     {
         m_rasterState->Release();
         m_rasterState = 0;
     }
 
     if (m_depthStencilView)
     {
         m_depthStencilView->Release();
         m_depthStencilView = 0;
     }
 
     if (m_depthStencilState)
     {
         m_depthStencilState->Release();
         m_depthStencilState = 0;
     }
 
     if (m_depthStencilBuffer)
     {
         m_depthStencilBuffer->Release();
         m_depthStencilBuffer = 0;
     }
 
     if (m_renderTargetView)
     {
         m_renderTargetView->Release();
         m_renderTargetView = 0;
     }
 
     if (m_swapChain)
     {
         m_swapChain->Release();
         m_swapChain = 0;
     }
 
     if (m_device)
     {
         m_device->Release();
         m_device = 0;
     }
 
     return ;
}

D3Dclass中有几个辅助函数。前两个是BeginScene和EndScene。BeginScene会在每帧绘制一个新3D场景前调用,它将缓存清空做好绘制的准备。在每帧最后所有绘制完成后,Endscene让交换链显示场景。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void D3DClass::BeginScene( float red, float green, float blue, float alpha)
{
     float color[4];
 
 
     // Setup the color to clear the buffer to.
     color[0] = red;
     color[1] = green;
     color[2] = blue;
     color[3] = alpha;
 
     // Clear the back buffer.
     m_device->ClearRenderTargetView(m_renderTargetView, color);
     
     // Clear the depth buffer.
     m_device->ClearDepthStencilView(m_depthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0);
 
     return ;
}
 
void D3DClass::EndScene()
{
     // Present the back buffer to the screen since rendering is complete.
     if (m_vsync_enabled)
     {
         // Lock to screen refresh rate.
         m_swapChain->Present(1, 0);
     }
     else
     {
         // Present as fast as possible.
         m_swapChain->Present(0, 0);
     }
 
     return ;
}

下一个方法返回指向Direct3D设备的指针,它经常会被框架调用。


1
2
3
4
ID3D10Device* D3DClass::GetDevice()
{
     return m_device;
}

后面三个辅助方法将投影矩阵、世界矩阵和正交矩阵返回给调用函数。大多数shader都需要这些矩阵进行绘制。本教程中我们不使用这三个方法。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void D3DClass::GetProjectionMatrix(D3DXMATRIX& projectionMatrix)
{
     projectionMatrix = m_projectionMatrix;
     return ;
}
 
 
void D3DClass::GetWorldMatrix(D3DXMATRIX& worldMatrix)
{
     worldMatrix = m_worldMatrix;
     return ;
}
 
 
void D3DClass::GetOrthoMatrix(D3DXMATRIX& orthoMatrix)
{
     orthoMatrix = m_orthoMatrix;
     return ;
}

最后一个辅助类返回显卡的名称和显存大小,知道这些信息可以帮助程序员在不同配置下进行调试工作。


1
2
3
4
5
6
void D3DClass::GetVideoCardInfo( char * cardName, int & memory)
{
     strcpy_s(cardName, 128, m_videoCardDescription);
     memory = m_videoCardMemory;
     return ;
}

总结

现在我们终于完成了Direct3D的初始化和关闭的代码,编译并运行代码后你会看到一个与前一个教程相同的窗口,但本教程中Direct3D已被初始化。

练习

1.编译代码运行程序,按下escape键退出。

2.改变graphicsclass.h中的full screen变量,重新编译观看结果。

3.将GraphicsClass::Render中的清除颜色变为黄色。

4.将显卡名称和内存大小信息保存在一个文本文件中。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页