第十四章 Hello, Rendering

第十四章 Hello, Rendering

现在,终于完成了应用程序的基础框架,并开发了足够的代码以用于渲染工作。本章将会实现第一个完整的3D应用程序。并使用大量的Direct3D和Effects 11 API函数。完成这些工作之后,将会更深入的理解Direct3D图形管线。

Your First Full Rendering Application

现在正式开始在屏幕上渲染第一个3D object:一个三角形。由于这是第一个完整的Direct3D程序,似乎只适合把该应用程序命名为“Hello, Rendering”。但是这个名称掩盖了程序的复杂性,因为从前面几章的学习中已经认识到,仅仅只是渲染一个简单的三角形也需要大量的基础组件。另外,在本节中添加了大量的代码。远比一个简单的单行输出“Hello, World!”程序要复杂的多。那我们正式开始吧。

An Updated Game Project

你可以继续使用之前创建的Game工程,或者创建一个新工程。不需要修改Game工程的设计结构,使用相同的Program.cpp文件,初始化一个RenderingGame对象并调用RenderingGame::Run()函数。RenderingGame类的实现代码与之前一样,除了两点:增加了FirstPersonCamera,以及一个TriangleDemo类型的成员变量。TriangleDemo类继承自DrawableGameComponent类,包含了渲染一个三角形的全部代码。增加了这些新的成员变量之后,RenderingGame类的实现代码如列表14.1所示。

列表14.1 The RenderingGame.cpp File

#include "RenderingGame.h"
#include "GameException.h"
#include "Keyboard.h"
#include "Mouse.h"
#include "FpsComponent.h"
#include "ColorHelper.h"
#include "FirstPersonCamera.h"
#include "TriangleDemo.h"

namespace Rendering
{
	const XMVECTORF32 RenderingGame::BackgroundColor = ColorHelper::CornflowerBlue;

	RenderingGame::RenderingGame(HINSTANCE instance, const std::wstring& windowClass, const std::wstring& windowTitle, int showCommand)
		:  Game(instance, windowClass, windowTitle, showCommand),
		   mFpsComponent(nullptr),
		   mDirectInput(nullptr), mKeyboard(nullptr), mMouse(nullptr),
		   mDemo(nullptr)
	{
		mDepthStencilBufferEnabled = true;
		mMultiSamplingEnabled = true;
	}

	RenderingGame::~RenderingGame()
	{
	}

	void RenderingGame::Initialize()
	{
		if (FAILED(DirectInput8Create(mInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (LPVOID*)&mDirectInput, nullptr)))
		{
			throw GameException("DirectInput8Create() failed");
		}

		mKeyboard = new Keyboard(*this, mDirectInput);
		mComponents.push_back(mKeyboard);
		mServices.AddService(Keyboard::TypeIdClass(), mKeyboard);
	
		mMouse = new Mouse(*this, mDirectInput);
		mComponents.push_back(mMouse);
		mServices.AddService(Mouse::TypeIdClass(), mMouse);

		mCamera = new FirstPersonCamera(*this);
		mComponents.push_back(mCamera);
		mServices.AddService(Camera::TypeIdClass(), mCamera);
	
		mFpsComponent = new FpsComponent(*this);
		mFpsComponent->Initialize();

		mDemo = new TriangleDemo(*this, *mCamera);
		mComponents.push_back(mDemo);

		Game::Initialize();

		mCamera->SetPosition(0.0f, 0.0f, 5.0f);
	}

	void RenderingGame::Shutdown()
	{
		DeleteObject(mDemo);
		DeleteObject(mKeyboard);
		DeleteObject(mMouse);
		DeleteObject(mFpsComponent);
		DeleteObject(mCamera);

		ReleaseObject(mDirectInput);

		Game::Shutdown();
	}

	void RenderingGame::Update(const GameTime &gameTime)
	{
		mFpsComponent->Update(gameTime);

		if (mKeyboard->WasKeyPressedThisFrame(DIK_ESCAPE))
		{
			Exit();
		}

		Game::Update(gameTime);
	}

	void RenderingGame::Draw(const GameTime &gameTime)
	{
		mDirect3DDeviceContext->ClearRenderTargetView(mRenderTargetView, reinterpret_cast<const float*>(&BackgroundColor));
		mDirect3DDeviceContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

		Game::Draw(gameTime);
	   
		HRESULT hr = mSwapChain->Present(0, 0);
		if (FAILED(hr))
		{
			throw GameException("IDXGISwapChain::Present() failed.", hr);
		}
	}
}


现在在RenderingGame::Initialize()函数中,初始了成员变量mCamera和mDemo,并把这两个变量添加到components container中。此外,引用了一个ColorHelper类用于初始化背景色。ColorHelper类中包含了一些静态成员用于表示一些常用的颜色值(XMVECTORF32类型),并没有在这里列出来,但是本书的配套网站提供了完整的代码。最后需要注意的是,在Initialize()函数的最后调用了FirstPersonCamera::SetPosition()函数。该函数把camera沿着z轴的正向移动5个单位的距离。由于三角形会创建在坐标原点处,通过调用SetPosition函数移动camera,可以使得在程序启动之后三角形能立即显示出来。这个例子中使用了右手坐标系,所以z轴的正向是朝向屏幕外面。

A TriangleDemo Component

列表14.2列出了TriangleDemo类的声明代码。在该代码中,包含有一个BasicEffectVertex结构体,以及ID3DX11Effect相关的成员变量。前者定义了三角形三个vertices的结构,以及作为shader输入要传递的数据的格式。后者引用了Effects 11库的中数据类型,在第3章“Tools of the Trade”中介绍了该库。如果在工程的external目录中还没有Effects 11库,需要把库解压到该目录中。如果在使用Effects 11库的过程中遇到了问题,可以参考第10章“Project Setup and Window Initialization”中的“Linking Libraries”一节。在Direct3D中,所有渲染都需要使用shaders,因此,无论渲染场景多么复杂,都需要使用ID3DX11Effect相关的成员变量。
此外,还有一个ID3DInputLayout类型的成员变量,定义了用于管线的input-assembler阶段的vetex数据结构。以及一个ID3D11Buffer类型的成员变量,用于表示vertex buffer。接下来列出TriangeleDemo的声明,再讨论这些变量。

列表14.2 The TriangleDemo.h Header File

#pragma once

#include "DrawableGameComponent.h"

using namespace Library;

namespace Rendering
{
	class TriangleDemo : public DrawableGameComponent
	{
		RTTI_DECLARATIONS(TriangleDemo, DrawableGameComponent)

	public:
		TriangleDemo(Game& game, Camera& camera);
		~TriangleDemo();

		virtual void Initialize() override;
		virtual void Update(const GameTime& gameTime) override;
		virtual void Draw(const GameTime& gameTime) override;

	private:
		typedef struct _BasicEffectVertex
		{
			XMFLOAT4 Position;
			XMFLOAT4 Color;

			_BasicEffectVertex() { }

			_BasicEffectVertex(XMFLOAT4 position, XMFLOAT4 color)
				: Position(position), Color(color) { }
		} BasicEffectVertex;

		TriangleDemo();
		TriangleDemo(const TriangleDemo& rhs);
		TriangleDemo& operator=(const TriangleDemo& rhs);

		ID3DX11Effect* mEffect;
		ID3DX11EffectTechnique* mTechnique;
		ID3DX11EffectPass* mPass;
		ID3DX11EffectMatrixVariable* mWvpVariable;

		ID3D11InputLayout* mInputLayout;		
		ID3D11Buffer* mVertexBuffer;

		XMFLOAT4X4 mWorldMatrix;
		float mAngle;
	};
}


The Effects 11 Library

Effects 11库提供了一组C++接口用于处理effect文件格式(在所有shaders中使用的.fx文件格式)。其中,ID3DX11Effect是顶层接口,包含了effect的变量和techniques。使用接口ID3DX11EffectTechnique可以访问一个technique,而一个technique中又包含了一个或多个passes,passes由接口ID3DX11EffectPass表示。一种effect的所有变量都可以使用接口ID3DX11EffectVariable表示,但是对于一些具体shader constant类型,也可以使用ID3DX11EffectVariable的派生类表示。比如,使用ID3DEffectMatrixVariable类型的成员变量TriangleDemo::mWvpVariable,可以访问effect中对应的WorldViewProjection矩阵。Effects 11库中的所有接口类型都包含在d3dx11Effect.h头文件中。

Compiling an Effect

在使用一种effect之前,需要先编译effect文件。可以在编译Visual Studio工程的过程中编译effect,或者在程序运行时编译effect。在下一章将会讲解如何在编译工程的时候编译effects,但是在本节主要使用程序运行时编译effect的方法。调用函数D3DCompileFromFile()可以编译一个effect文件,该函数包含在D3DCompiler.h头文件中,函数原型及参数如下:

HRESULT D3DCompileFromFile(
	LPCWSTR pFileName,
	D3D_SHADER_MACRO* pDefines,
	ID3DInclude* pInclude,
	LPCSTR pEntrypoint,
	LPCSTR pTarget,
	UINT Flags1,
	UINT Flags2,
	ID3DBlob** ppCode,
	ID3DBlob** ppErrorMsgs);


pFileName:包含effect的文件的名称。
pDefines:由D3D_SHADER_MACRO结构体定义的一个shader宏数组。目前的effects不需要使用任何shader宏,可以直接把这个参数设为NULL。
pInclude:一个ID3DInclude类型的指针对象,指明应用程序通过自定义方法打开并关闭 #include 文件。如果该参数设置为NULL,带有#include指令的effect就会编译失败。可以使用D3D_COMPILE_STANDARD_FILE_INCLUDE宏表示默认的include句柄(即默认参数值)。由于该默认句柄表示包含文件位于与当前路径相关联的目录中,因此在使用该参数值编译effects之前,需要把当前工作路径设置为正确的路径。
pEntryPoint:shader中的入口点函数的名称。在编译effect文件时,始终把该参数设为NULL。
pTarget:指定shader编译器的目标版本。在本书所使用的effects中都指定为fx_5_0。
Flags1:由D3DCOMPILE类型的枚举常量按位与运算的结果,用于指定shader的编译方式。比如,常用的编译选项D3DCOMPILE_DEBUG和D3DCOMPILE_SKIP_OPTIMIZATIONS,可以用于调试shaders。要查看完整的选项列表,可以阅读在线文档。
Flags2:由D3DCOMPILE_EFFECT类型的枚举常量按位与运算的结果,用于指定effect文件的编译方式。主要用于一些高级的功能,通常不使用该参数。
ppCode:编译成功的返回值,指向一个ID3DBlob类型的对象。ID3DBlob接口表示一个任意长度的buffer,仅仅包含两个成员函数:ID3DBlob::GetBufferPointer()和ID3DBlob::GetBufferSize(),分别返回一个指向buffer数据的指针以及数据的大小(单位为bytes)。
ppErrorMsgs:用于存储编译返回的错误信息,该参数是可选的。
列表14.3演示了D3DCompileFromFile()函数的使用方法。

14.3 Compiling an Effect

SetCurrentDirectory(Utility::ExecutableDirectory().c_str());

// Compile the shader
UINT shaderFlags = 0;

#if defined( DEBUG ) || defined( _DEBUG )
shaderFlags |= D3DCOMPILE_DEBUG;
shaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endif

ID3D10Blob* compiledShader = nullptr;
ID3D10Blob* errorMessages = nullptr;
HRESULT hr = D3DCompileFromFile(L"Content\\Effects\\BasicEffect.fx", nullptr, nullptr, nullptr, "fx_5_0", shaderFlags, 0, &compiledShader, &errorMessages);
if (FAILED(hr))
{
	char* errorMessage = (errorMessages != nullptr ? (char*)errorMessages->GetBufferPointer() : "D3DX11CompileFromFile() failed");
	GameException ex(errorMessage, hr);
	ReleaseObject(errorMessages);

	throw ex;
}


该代码主要用于编译BasicEffect.fx文件,该文件位于应用程序的可执行文件所在目录的Content\Effects\目录下。变量compiledShader用于存储编译返回的effect,返回的任何错误信息都存储在变量errorMessages中。
BasicEffect.fx文件中的代码主要来自第4章中编写的HelloStructs.fx effect文件。在该effect中,使用一个vertex坐标和颜色作为shader的输入,然后把坐标变换到齐次裁剪空间中,最后输出vertex的颜色,对每一个pixel进行插值。列表14.4列出了完整的effect代码。其中使用了关键字technique11,指定使用一种DirectX 11 technique。

列表14.4 The BasicEffect.fx File

/************* Resources *************/

cbuffer CBufferPerObject
{
    float4x4 WorldViewProjection : WORLDVIEWPROJECTION; 
}

/************* Data Structures *************/

struct VS_INPUT
{
    float4 ObjectPosition: POSITION;
    float4 Color : COLOR;
};

struct VS_OUTPUT 
{
    float4 Position: SV_Position;
    float4 Color : COLOR;
};

RasterizerState DisableCulling
{
    CullMode = NONE;
};

/************* Vertex Shader *************/

VS_OUTPUT vertex_shader(VS_INPUT IN)
{
    VS_OUTPUT OUT = (VS_OUTPUT)0;
    
    OUT.Position = mul(IN.ObjectPosition, WorldViewProjection);
    OUT.Color = IN.Color;
    
    return OUT;
}

/************* Pixel Shader *************/

float4 pixel_shader(VS_OUTPUT IN) : SV_Target
{
    return IN.Color;
}

/************* Techniques *************/

technique11 main11
{
    pass p0
    {
        SetVertexShader(CompileShader(vs_5_0, vertex_shader()));
        SetGeometryShader(NULL);
        SetPixelShader(CompileShader(ps_5_0, pixel_shader()));

        SetRasterizerState(DisableCulling);
    }
}


在使用之前,需要把BasicEffect.fx文件存放到Game或者Library的工程目录中。并确保在工程中添加了正确的prebuild和post-build events,用于把content目录中的文件拷贝到应用程序的可执行文件目录中。第12章详细讲述了这些build events。

Creating an Effect Object

编译完effect文件之后,就可以使用返回的ID3DBlob对象创建一个ID3DX11Effect对象。该创建过程通过调用D3DX11CreateEffectFromMemory()函数执行,函数的原型和参数如下:

HRESULT D3DX11CreateEffectFromMemory(
	LPCVOID pData,
	SIZE_T DataLength,
	UINT FXFlags,
	ID3D11Device *pDevice,
	ID3DX11Effect **ppEffect);


pData:编译effect文件返回的结果数据。
DataLength:结果数据的大小(单位为bytes)。
FXFlags:目前还没有使用effect标志。该参数始终设为0。
pDevice:Direct3D device。
ppEffect:返回创建成功的effect实例。
列表14.5显示一个调用D3DX11CreateEffectFromMemory()函数的示例。其中,在调用D3DX11CreateEffectFromMemory()函数之后立即释放了ID3DBlod类型的对象compiledShader。因为在effect对象创建之后,compilerShader就没用了,因此可以释放对该对象的引用。

列表14.5 Creating an Effect Object

hr = D3DX11CreateEffectFromMemory(compiledShader->GetBufferPointer(), compiledShader->GetBufferSize(), 0, mGame->Direct3DDevice(), &mEffect);
if (FAILED(hr))
{
	throw GameException("D3DX11CreateEffectFromMemory() failed.", hr);
}

ReleaseObject(compiledShader);


Technique, Pass, and Variable Lookup

创建完一个effect对象之后,就可以使用该effect的technique,pass以及相应的variables。要得到这些对象,需要调用与之对应命名的函数:ID3DX11Effect::GetTechniqueByName(),ID3DX11Effect::GetPassByName()以及ID3DX11Effect::GetVariableByName()。这三个函数都只包含一个参数,描述要查找的变量对应的字符串;并返回对应的接口,如果指定标识符的变量没有找到,就返回NULL。
ID3DX11EffectVariable接口中包含一组以“As”开头的函数,用于把该变量转换为一个接口更具体的对象。例如,ID3DX11EffectVariable::AsMatrix()函数就是试图把ID3DX11EffectVariable对象转换为ID3DX11EffectMatrixVariable类型的对象。在转换之后,必须对转换后的对象调用ID3DX11EffectVariable::IsValid()函数测试是否转换成功。
列表14.6演示了调用相关函数查找BasicEffect.fx文件中的main 11 technique,p0 pass以及WorldViewProjection变量的方法。同时,还包含了把WroldViewProjection变量转换为ID3DX11EffectMatrixVariable对象的函数调用示例。

列表14.6 Retrieving Techniques, Passes, and Variables

mTechnique = mEffect->GetTechniqueByName("main11");
if (mTechnique == nullptr)
{
	throw GameException("ID3DX11Effect::GetTechniqueByName() could not find the specified technique.", hr);
}

mPass = mTechnique->GetPassByName("p0");
if (mPass == nullptr)
{
	throw GameException("ID3DX11EffectTechnique::GetPassByName() could not find the specified pass.", hr);
}

ID3DX11EffectVariable* variable = mEffect->GetVariableByName("WorldViewProjection");
if (variable == nullptr)
{
	throw GameException("ID3DX11Effect::GetVariableByName() could not find the specified variable.", hr);
}

mWvpVariable = variable->AsMatrix();
if (mWvpVariable->IsValid() == false)
{
	throw GameException("Invalid effect variable cast.");
}


Creating an Input Layout

下面是BasicEffect.fx文件中的VS_INPUT结构体代码,以及TriangleDemo类中的BasicEffectVertex结构体代码。

struct VS_INPUT
{
	float4 ObjectPosition : POSITION;
	float4 Color : COLOR;
};
typedef struct _BasicEffectVertex
{
	XMFLOAT4 Position;
	XMFLOAT4 Color;
	_BasicEffectVertex() { }
	_BasicEffectVertex(XMFLOAT4 position, XMFLOAT4 color)
		: Position(position), Color(color) { }
} BasicEffectVertex;


这两个结构都表示了同样的vertex数据(一个坐标值和一个颜色值),BasicEffectVertex结构是在C++/CPU中定义的,而VS_INPUT结构体则是在HLSL/GPUN中定义的。在Direct3D中需要使用一种input layout把CPU中的vertex data与GPU中vertex data一一对应。在创建一个input layout之前,需要先配置一个D3D11_INPUT_ELEMENT_DESC结构体类型的数组。该结构体定义如下:

typedef struct D3D11_INPUT_ELEMENT_DESC
{
	LPCSTR SemanticName;
	UINT SemanticIndex;
	DXGI_FORMAT Format;
	UINT InputSlot;
	UINT AlignedByteOffset;
	D3D11_INPUT_CLASSIFICATION InputSlotClass;
	UINT InstanceDataStepRate;
} D3D11_INPUT_ELEMENT_DESC;


SemanticName:与输入数据元素相关联的HLSL semantic(比如,POSITION,COLOR)
SemanticIndex:数据元素的semantic索引值(比如,索引值为1表示semantic为TEXCOORD1,索引值为2表示COLOR2)。除非是把同一个semantic指定给多个输入数据元素,否则不需要索引值。比如,semantic为POSITION等同于POSITION0。
Format:输入元素的数据类型。比如,DXGI_FORMAT_R32G32B32A32_FLOAT表示一种由4个32位浮点数组成的数据通道格式。
InputSlot:根据创建device时所使用的feature level,可以支持16或32个input slots,用于发送vertex data、这些slots包含多个vertex buffer,每一个vertex buffer对应于一个不同的slot。
AlignedByteOffset:两个相邻输入元素之间的偏移量(单位为bytes)。使用D3D11_APPEND_ALIGNED_ELEMENT表示输入元素自动对齐。
InputSlotClass:输入数据的种类,要么是针对每一个vertex,要么是每一个instance。第四部分“Intermediate-Level Rendering Topics”将会讨论hardware instancing。现在,直接使用D3D_INPUT_PER_VERTEX_DATA。
InstanceDataStepRate:该成员与per-instance种类的数据(InputSlotClass成员值为D3D_INPUT_PER_INSTANCE_DATA)同时使用。对于per-vertex种类的数据,该成员总是设为0.

有了D3D11_INPUT_ELEMENT_DESC(input element descriptions)类型的数组之后,就可以调用ID3D11Device::CreateInputLayout()函数,该函数的原型和参数如下:

HRESULT CreateInputLayout(
	const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs,
	UINT NumElements,
	const void *pShaderBytecodeWithInputSignature,
	SIZE_T BytecodeLength,
	ID3D11InputLayout **ppInputLayout);


pInputElementDescs:input element descriptions类型的数组。
NumElements:pInputElementDescs数组中的元素个数。
pShaderBytecodeWithInputSignature:编译成功的shader(包含一个输入标记用于验证input element descriptions)。
BytecodeLength:shader的大小(单位为bytes)。
ppInputLayout:返回创建成功的input layout。
列表14.7演示了三角形示例中创建input layout的过程。

列表14.7 Input Layout Creation

D3DX11_PASS_DESC passDesc;
mPass->GetDesc(&passDesc);

D3D11_INPUT_ELEMENT_DESC inputElementDescriptions[] =
{
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "COLOR",    0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

if (FAILED(hr = mGame->Direct3DDevice()->CreateInputLayout(inputElementDescriptions, ARRAYSIZE(inputElementDescriptions), passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &mInputLayout)))
{
	throw GameException("ID3D11Device::CreateInputLayout() failed.", hr);
}


列表14.7中前面两行代码用于得到一个D3DX11_PASS_DESC结构体类型的对象,该结构体中包含了用于ID3D11Device::CreateInputLayout()函数中的input signature参数。之所以把input signature与一种pass关联起来,是因为一种pass指定了要使用的vertex shader,因此,也就指定了所要使用的shader inputs。接下来创建一个用于表示POSITION和COLOR输入元素的D3D11_INPUT_ELEMENT_DESC数组。最后调用CreateInputLayout()函数创建一个ID3D11InputLayout类型的对象。

Creating a Vertex Buffer

在TriangleDemo类中初始化的最后一步是创建一个vertex buffer。一个vertex buffer中存储了Direct3D图形管线处理的vertices,使用ID3D11Buffer接口类型表示。创建一个vertex buffer之前需要构建一个D3D11_BUFFER_DESC结构体,该结构体定义如下:

typedef struct D3D11_BUFFER_DESC
{
	UINT ByteWidth;
	D3D11_USAGE Usage;
	UINT BindFlags;
	UINT CPUAccessFlags;
	UINT MiscFlags;
	UINT StructureByteStride;
} D3D11_BUFFER_DESC;


ByteWidth:buffer的大小。
Usage:设置buffer的分别针对CPU和GPU的读写权限。如果希望vertex buffer在创建之后就不再被修改,设置为D3D11_USAGE_IMMUTABLE。对于这种不可变的buffers,只有GPU可以访问(可读但不可写),CPU无法访问(既不能读也不能写)。
BindFlags:buffers的具体类型标志,对于vertex buffer,使用D3D11_BIND_VERTEX_BUFFER。
CPUAccessFlags:指定CPU对texture的读写权限,由相关的枚举常量值按位与运算得到。当CPU不需要访问时把该成员设为0。
MiscFlags:一些不常使用的buffer选项标志。
StructureByteStride:当该buffer是一个structured buffer时,其中每一个元素的大小。一个structured buffer中的元素大小都相同。在本书的所示示例中,该成员都设为0。

接下来构建一个D3D11_SUBRESOURCE_DATA结构对象用于设置vertex buffer的初始数据。对于不可变的buffers(Usage指定为D3D11_USAGE_IMMUTABLE),需要在创建buffer时就指定初始数据。对于其他的buffers,则不是必需的。D3D11_SUBRESOURCE_DATA结构体定义如下:

typedef struct D3D11_SUBRESOURCE_DATA
{
	const void *pSysMem;
	UINT SysMemPitch;
	UINT SysMemSlicePitch;
} D3D11_SUBRESOURCE_DATA;


pSysMem:指向buffer初始数据的指针。
SysmemPitch:texture每一行的数据长度(单位为bytes)。该参数只用于2D和3D textures,与vertex buffers无关。
SysMemSlicePitch:从一个深度级别到下一个深度级别的距离(单位为bytes)。该参数只用于3D textures。

创建一个vertex buffer的最后一步是调用ID3D11Device::CrateBuffer()函数。

HRESULT CreateBuffer(
	const D3D11_BUFFER_DESC *pDesc,
	const D3D11_SUBRESOURCE_DATA *pInitialData,
	ID3D11Buffer **ppBuffer);


pDesc:D3D11_BUFFER_DESC(buffer description)结构体。
pInitialData:提供给buffer的初始数据(根据不同情况,该数据是可选的)。
ppBuffer:返回创建完成的buffer。
列表14.8在三角形示例中创建vertex buffer的代码段。

列表14.8 Vertex Buffer Creation

BasicEffectVertex vertices[] =
{
	BasicEffectVertex(XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f), XMFLOAT4(reinterpret_cast<const float*>(&ColorHelper::Red))),
	BasicEffectVertex(XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f), XMFLOAT4(reinterpret_cast<const float*>(&ColorHelper::Green))),
	BasicEffectVertex(XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f), XMFLOAT4(reinterpret_cast<const float*>(&ColorHelper::Blue)))
};

D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory(&vertexBufferDesc, sizeof(vertexBufferDesc));
vertexBufferDesc.ByteWidth = sizeof(BasicEffectVertex) * ARRAYSIZE(vertices);
vertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;

D3D11_SUBRESOURCE_DATA vertexSubResourceData;
ZeroMemory(&vertexSubResourceData, sizeof(vertexSubResourceData));
vertexSubResourceData.pSysMem = vertices;
if (FAILED(mGame->Direct3DDevice()->CreateBuffer(&vertexBufferDesc, &vertexSubResourceData, &mVertexBuffer)))
{
	throw GameException("ID3D11Device::CreateBuffer() failed.");
}

在该代码中,第一段初始化了一个包括3个vertices的数组,这个3个vertices分别具有坐标(-1, 0, 0), (0, 1, 0), (1, 0, 0)以及对应的红,绿,蓝颜色值。使用构建一个D3D11_BUFFER_DESC结构体类型的对象,该对象包含不可变的vertex buffer。最后,创建一个vertex buffer并存储到成员变量mVertexBuffer中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值