原英文版地址:http://www.rastertek.com/dx11tut03.html
本教程将首次介绍如何使用Directx11。我们将讨论如何初始化和关闭Direct3D以及如何渲染到窗口。
更新的框架
我们将向框架中添加另一个类,该类将处理所有Direct3D系统函数。我们将调用类d3dclass。我已经更新了下面的框架图:
如您所见,d3dclass将位于graphicsClass内。前面的教程提到,所有与图形相关的新类都将封装在graphicsClass中,这就是为什么它是新d3dclass的最佳位置。现在让我们看看对graphicsClass所做的更改:
Graphicsclass.h
// Filename: graphicsclass.h
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_
这是第一个改变。我们去掉了windows.h的include,改为包含了新的d3dclass.h。
///
// 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:
第二个变化是新的私有指针指向d3d class,我们称之为m_d3d。如果你想知道我在所有类变量上使用前缀m_。这样,当我编码时,我可以快速记住哪些变量是类的成员,哪些不是。
D3DClass* m_D3D;
};
#endif
Graphicsclass.cpp
如果您还记得上一个教程中的内容,那么这个类是完全空的,里面根本没有代码。现在我们有了一个d3dclass成员,我们将开始在graphicsClass中填充一些代码来初始化和关闭d3dclass对象。我们还将在渲染函数中添加对beginscene和endscene的调用,以便现在使用Direct3D绘制到窗口。
所以第一个变化是在类构造函数中。在这里,为了安全起见,我们初始化指向空的指针,就像处理所有类指针一样。
GraphicsClass::GraphicsClass()
{
m_D3D = 0;
}
第二个更改是在graphicsClass内的initialize函数中。在这里,我们创建d3dclass对象,然后调用d3dclass初始化函数。我们将这个函数的屏幕宽度、屏幕高度、句柄发送到窗口,以及graphicClass.h文件中的四个全局变量。d3dclass将使用所有这些变量来设置Direct3D系统。在查看d3dclass.cpp文件后,我们将详细介绍这一点。
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关闭放置在此函数中。注意,我检查指针是否初始化。如果不是,我们可以假设它从未设置过,也不会试图关闭它。这就是为什么在类构造函数中将所有指针设置为空很重要。如果它发现指针已经初始化,那么它将尝试关闭d3dclass,然后清除指针。
void GraphicsClass::Shutdown()
{
if(m_D3D)
{
m_D3D->Shutdown();
delete m_D3D;
m_D3D = 0;
}
return;
}
frame函数已更新,因此现在它调用每个帧的渲染函数。
bool GraphicsClass::Frame()
{
bool result;
// Render the graphics scene.
result = Render();
if(!result)
{
return false;
}
return true;
}
对这个类的最后一个更改是在render函数中。我们调用d3d对象将屏幕清除为灰色。之后我们调用endscene,使灰色显示在窗口中。
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
// Filename: d3dclass.h
#ifndef _D3DCLASS_H_
#define _D3DCLASS_H_
头中的第一件事是指定使用此对象模块时要链接的库。这些库包含用于在DirectX中设置和绘制3D图形的所有Direct3D功能,以及用于与计算机上的硬件交互的工具,以获取有关监视器刷新率、正在使用的视频卡等的信息。您会注意到一些Directx10库仍在使用,这是因为这些库从未为Directx11升级过,因为它们的功能不需要更改。
/
// LINKING //
/
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dx11.lib")
#pragma comment(lib, "d3dx10.lib")
接下来,我们要做的是包括那些链接到这个对象模块的库的头,以及DirectX类型定义的头等等。
//
// INCLUDES //
//
#include <dxgi.h>
#include <d3dcommon.h>
#include <d3d11.h>
#include <d3dx10math.h>
d3dclass的类定义在这里尽可能简单。它有常规构造函数、复制构造函数和析构函数。更重要的是,它具有初始化和关闭功能。这将是我们在本教程中主要关注的内容。除此之外,我还有几个对本教程不重要的助手函数和一些私有成员变量,这些变量将在我们检查d3dclass.cpp文件时查看。现在只需认识到初始化和关闭功能是我们关心的。
// 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();
ID3D11Device* GetDevice();
ID3D11DeviceContext* GetDeviceContext();
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;
ID3D11Device* m_device;
ID3D11DeviceContext* m_deviceContext;
ID3D11RenderTargetView* m_renderTargetView;
ID3D11Texture2D* m_depthStencilBuffer;
ID3D11DepthStencilState* m_depthStencilState;
ID3D11DepthStencilView* m_depthStencilView;
ID3D11RasterizerState* m_rasterState;
D3DXMATRIX m_projectionMatrix;
D3DXMATRIX m_worldMatrix;
D3DXMATRIX m_orthoMatrix;
};
#endif
对于已经熟悉Direct3D的人,您可能会注意到我在这个类中没有视图矩阵变量。原因是我将把它放在一个相机类,我们将在未来的教程中看到。
D3dclass.cpp
// Filename: d3dclass.cpp
#include "d3dclass.h"
因此,和大多数类一样,我们从初始化类构造函数中的所有成员指针为空开始。头文件中的所有指针都已在此处说明。
D3DClass::D3DClass()
{
m_swapChain = 0;
m_device = 0;
m_deviceContext = 0;
m_renderTargetView = 0;
m_depthStencilBuffer = 0;
m_depthStencilState = 0;
m_depthStencilView = 0;
m_rasterState = 0;
}
D3DClass::D3DClass(const D3DClass& other)
{
}
D3DClass::~D3DClass()
{
}
初始化函数是DirectX 11的整个Direct3D设置的内容。我已经在这里放置了所有必要的代码,以及一些额外的东西,以便于将来的教程。我本可以简化它并删除一些项目,但最好在专门针对它的单个教程中介绍所有这些内容。
为这个函数提供的屏幕宽度和屏幕高度变量是我们在SystemClass中创建的窗口的宽度和高度。Direct3D将使用这些来初始化和使用相同的窗口尺寸。hwnd变量是窗口的句柄。Direct3D需要此句柄才能访问以前创建的窗口。全屏变量是以窗口模式还是全屏模式运行。Direct3D也需要这样做来创建具有正确设置的窗口。ScreenDepth和ScreenNear变量是将在窗口中呈现的3D环境的深度设置。vsync变量指示我们是希望Direct3D根据用户监视器刷新率进行渲染,还是希望尽可能快地进行渲染。
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;
// Store the vsync setting.
m_vsync_enabled = vsync;
在初始化Direct3D之前,我们必须从视频卡/监视器获取刷新率。每台计算机可能略有不同,因此我们需要查询这些信息。我们查询分子和分母值,然后在设置期间将它们传递给DirectX,它将计算正确的刷新率。如果我们不这样做,只是将刷新率设置为一个默认值,而该默认值可能不存在于所有计算机上,那么DirectX将通过执行一个blit而不是缓冲区翻转来响应,这将降低性能并在调试输出中给我们带来恼人的错误。
// 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;
}
}
}
现在我们有了刷新率的分子和分母。最后我们将使用适配器检索到的内容是视频卡的名称和视频卡上的内存量。
// 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;
}
既然我们已经存储了刷新率的分子和分母以及视频卡信息,那么我们就可以释放用于获取这些信息的结构和接口。
// 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初始化了。我们要做的第一件事是填写交换链的描述。交换链是用于绘制图形的前后缓冲区。通常,您使用一个后缓冲区,对其进行所有绘图,然后将其交换到前缓冲区,然后将其显示在用户屏幕上。这就是它被称为交换链的原因。
// 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;
交换链描述的下一部分是刷新率。刷新率是每秒将后缓冲区拉到前缓冲区的次数。如果在graphicsClass.h头中将vsync设置为true,那么这会将刷新率锁定到系统设置(例如60hz)。这意味着它只能每秒绘制60次屏幕(如果系统刷新率超过60,则为60次以上)。但是,如果我们将vsync设置为false,那么它将每秒绘制尽可能多的屏幕,但是这可能会导致一些视觉效果。
// 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;
在设置交换链描述之后,我们还需要再设置一个称为功能级别的变量。这个变量告诉DirectX我们计划使用什么版本。这里我们将功能级别设置为11.0,即Directx11。如果计划支持多个版本或在低端硬件上运行,可以将此设置为10或9以使用较低级别的DirectX。
// Set the feature level to DirectX 11.
featureLevel = D3D_FEATURE_LEVEL_11_0;
现在交换链描述和功能级别已经填写完毕,我们可以创建交换链、Direct3D设备和Direct3D设备上下文。Direct3D设备和Direct3D设备上下文非常重要,它们是所有Direct3D功能的接口。从现在开始,我们将对几乎所有内容使用设备和设备上下文。
阅读本文的那些熟悉DirectX早期版本的人将识别Direct3D设备,但不熟悉新的Direct3D设备上下文。基本上,他们采用了Direct3D设备的功能,并将其分为两个不同的设备,因此您现在需要同时使用这两个设备。
请注意,如果用户没有DirectX 11视频卡,此函数调用将无法创建设备和设备上下文。另外,如果您自己测试DirectX 11功能,并且没有DirectX 11视频卡,则可以用d3d驱动程序类型参考替换d3d驱动程序类型\u硬件,DirectX将使用您的CPU绘制而不是视频卡硬件。请注意,它的速度是1/1000,但对于那些还没有在所有机器上安装Directx11视频卡的人来说,这是一个很好的选择。
// Create the swap chain, Direct3D device, and Direct3D device context.
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;
}
有时,如果主显卡与DirectX 11不兼容,则创建设备的调用将失败。有些计算机可能将主卡用作DirectX 10视频卡,将次卡用作DirectX 11视频卡。此外,一些混合图形卡的工作方式,主要是低功耗Intel卡,次要是高功率Nvidia卡。要解决这个问题,您将不需要使用默认设备,而是枚举机器中的所有视频卡,让用户选择要使用的卡,然后在创建设备时指定该卡。
既然我们有了交换链,那么就需要一个指向后缓冲区的指针,然后将它附加到交换链上。我们将使用createRenderTargetView函数将后缓冲区附加到交换链。
// Get the pointer to the back buffer.
result = m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (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;
我们还需要设置深度缓冲区描述。我们将使用这个创建一个深度缓冲区,以便我们的多边形可以在三维空间中正确地渲染。同时,我们将在深度缓冲区上附加一个模板缓冲区。模具缓冲区可用于实现运动模糊、体积阴影等效果。
// 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 = D3D11_USAGE_DEFAULT;
depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthBufferDesc.CPUAccessFlags = 0;
depthBufferDesc.MiscFlags = 0;
现在,我们使用该描述创建深度/模具缓冲区。您会注意到我们使用createTexture2d函数来创建缓冲区,因此缓冲区只是一个二维纹理。原因是,一旦你的多边形被排序,然后光栅化,它们最终会变成这个二维缓冲区中的彩色像素。然后将这个二维缓冲区绘制到屏幕上。
// 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;
}
现在我们需要设置深度模具描述。这允许我们控制Direct3D对每个像素的深度测试类型。
// Initialize the description of the stencil state.
ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
// Set up the description of the stencil state.
depthStencilDesc.DepthEnable = true;
depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
depthStencilDesc.StencilEnable = true;
depthStencilDesc.StencilReadMask = 0xFF;
depthStencilDesc.StencilWriteMask = 0xFF;
// Stencil operations if pixel is front-facing.
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;
// Stencil operations if pixel is back-facing.
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;
完成描述后,我们现在可以创建深度模具状态。
// Create the depth stencil state.
result = m_device->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);
if(FAILED(result))
{
return false;
}
使用创建的深度模具状态,我们现在可以设置它使其生效。注意,我们使用设备上下文来设置它。
// Set the depth stencil state.
m_deviceContext->OMSetDepthStencilState(m_depthStencilState, 1);
接下来我们需要创建的是深度模具缓冲区视图的描述。我们这样做是为了让Direct3D知道使用深度缓冲区作为深度模具纹理。填写完描述之后,我们调用函数createdDepthStencilView来创建它。
// 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 = D3D11_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。这将把渲染目标视图和深度模具缓冲区绑定到输出渲染管道。这样,管道呈现的图形将被绘制到我们先前创建的后台缓冲区。通过将图形写入后缓冲区,我们可以将其交换到前缓冲区,并在用户屏幕上显示我们的图形。
// Bind the render target view and depth stencil buffer to the output render pipeline.
m_deviceContext->OMSetRenderTargets(1, &m_renderTargetView, m_depthStencilView);
现在渲染目标已经设置好了,我们可以继续使用一些额外的函数,这些函数将为以后的教程提供对场景的更多控制。首先,我们将创建一个光栅化状态。这将使我们能够控制多边形的渲染方式。我们可以做一些事情,比如让我们的场景在线框模式下渲染,或者让DirectX同时绘制多边形的正面和背面。默认情况下,DirectX已经设置了一个光栅化器状态,并且与下面的状态完全相同,但除非您自己设置了一个,否则您无法更改它。
// Setup the raster description which will determine how and what polygons will be drawn.
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;
// 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_deviceContext->RSSetState(m_rasterState);
还需要设置视区,以便Direct3D可以将剪辑空间坐标映射到渲染目标空间。将其设置为窗口的整个大小。
// Setup the viewport for rendering.
viewport.Width = (float)screenWidth;
viewport.Height = (float)screenHeight;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
// Create the viewport.
m_deviceContext->RSSetViewports(1, &viewport);
现在我们将创建投影矩阵。投影矩阵用于将三维场景转换为我们先前创建的二维视区空间。我们需要保留这个矩阵的一个副本,以便我们可以将它传递给我们的着色器,这些着色器将用于渲染我们的场景。
// 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);
我们还将创建另一个称为世界矩阵的矩阵。此矩阵用于将对象的顶点转换为三维场景中的顶点。这个矩阵还将用于在三维空间中旋转、平移和缩放对象。从一开始,我们只需将矩阵初始化为标识矩阵,并在此对象中保留它的副本。还需要将副本传递给着色器进行渲染。
// Initialize the world matrix to the identity matrix.
D3DXMatrixIdentity(&m_worldMatrix);
这通常是创建视图矩阵的地方。视图矩阵用于计算我们从中查看场景的位置。你可以把它当作一个照相机,你只能通过这个照相机来观看场景。因为它的目的,我将在后面的教程中的相机类中创建它,因为它在逻辑上更适合那里,现在跳过它。
最后我们将在初始化函数中设置一个正交投影矩阵。此矩阵用于渲染二维元素,如屏幕上的用户界面,允许我们跳过三维渲染。当我们在屏幕上呈现二维图形和字体时,您将在后面的教程中看到这一点。
// Create an orthographic projection matrix for 2D rendering.
D3DXMatrixOrthoLH(&m_orthoMatrix, (float)screenWidth, (float)screenHeight, screenNear, screenDepth);
return true;
}
shutdown函数将释放并清除初始化函数中使用的所有指针,它非常直接。不过,在执行此操作之前,我先调用以强制交换链进入窗口模式,然后再释放任何指针。如果不这样做,并且您尝试在全屏模式下释放交换链,它将抛出一些异常。为了避免这种情况发生,我们总是在关闭Direct3D之前强制使用窗口模式。
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_deviceContext)
{
m_deviceContext->Release();
m_deviceContext = 0;
}
if(m_device)
{
m_device->Release();
m_device = 0;
}
if(m_swapChain)
{
m_swapChain->Release();
m_swapChain = 0;
}
return;
}
在d3dclass中,我有几个助手函数。前两个是开始和结束场景。每当我们要在每个帧的开头绘制一个新的3D场景时,都会调用beginscene。它所做的就是初始化缓冲区,使其为空并准备好被绘制。另一个功能是end scene,它告诉交换链在每帧结束时完成所有绘图后显示我们的3D场景。
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_deviceContext->ClearRenderTargetView(m_renderTargetView, color);
// Clear the depth buffer.
m_deviceContext->ClearDepthStencilView(m_depthStencilView, D3D11_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设备和Direct3D设备上下文的指针。这些助手函数通常由框架调用。
ID3D11Device* D3DClass::GetDevice()
{
return m_device;
}
ID3D11DeviceContext* D3DClass::GetDeviceContext()
{
return m_deviceContext;
}
接下来的三个助手函数将投影矩阵、世界矩阵和正交矩阵的副本提供给调用函数。大多数明暗器在渲染时都需要这些矩阵,因此需要一种简单的方法来让外部对象获得它们的副本。在本教程中我们不会调用这些函数,但我只是解释为什么它们在代码中。
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;
}
Last helper函数通过引用返回视频卡的名称和视频卡上的专用内存量。了解视频卡的名称和视频内存量有助于在不同的配置上进行调试。
void D3DClass::GetVideoCardInfo(char* cardName, int& memory)
{
strcpy_s(cardName, 128, m_videoCardDescription);
memory = m_videoCardMemory;
return;
}
Summary
因此,现在我们终于能够初始化和关闭Direct3D,并向窗口呈现颜色。编译和运行代码将生成与上一个教程相同的窗口,但Direct3D现在已初始化,窗口已清除为灰色。编译和运行代码还将显示编译器的设置是否正确,以及它是否可以从DirectX SDK中看到头文件和库文件。
做练习
1。重新编译代码并运行程序以确保DirectX正常工作,如果不看第一个教程中的步骤的话。窗口显示后,按Escape键退出。
2。将graphicclass.h中的global更改为全屏并重新编译/运行。
三。将graphicsClass::render中的“清除颜色”更改为黄色。
4。将显卡名称和内存写入文本文件。
(如果要看其他课的中文翻译版,请到我博客目录查找,我会抽时间把后续的课目都翻译出来,这取决于我有空闲时间。)
时间仓促,只是粗略翻译,可能有多处失误,请谅解。朋友如有发现哪里有错误,欢迎指正,联·系w新licheng16886