第十六章 Materials
本章主要开发一组用于封装各种effects的类,以简化应用程序与effects的交互。然后通过编写一些示例程序用于演示这些类函数。最后再创建一个可复用的组件用于渲染skyboxes。Motivation
如果你仔细阅读前面编写的几个示例代码(triangle,cube,model,textured model示例),就会发现在这几个示例中一些重复的代码,比如,编译shader文件,创建effect对象,查询technique、pass和variable,创建input layout,以及创建vertex和index buffer,所有这些操作的代码都是重复的。如果你有同样的想法,应该会想要把这些代码模块化。此外,用于与BasicEffect shader进行交互的相关类型和成员变量(比如BasicEffectVertex结构体类型,成员变量mEffect,mTechnique,mPass以及mWvpVariable),在每一个示例中都重复使用了。BasicEffect shader是目前能够使用的effects中最简单的一种。思考一下如何在应用程序中使用之前所编写的更复杂的shaders。在一个复杂的shader中可能包含十几或更多个shader变量,以及多种techniques和passes。显然,不能在每次使用shaders时都重复定义这些数据,以及相关的初始化操作。回想一下在NVIDIA FX Composer中用于描述一种effect实例的术语:material(材质)。我们将采用这个术语创建一组数据结构用于支持与一个给定的effect进行交互。在这个系统结构中Material类是一个顶层结构。在此过程中,把编译shaders和创建vertex buffers部分进行模块化,并学习如何在应用程序编译期编译shaders。
材质系统结构由以下几个类组成:Effect,Technique,Pass,Variable和Material。其中,Effect,Technique,Pass和Variable类分别是Effects 11中对应类型的简单封装(增加相关的函数,并简化Effects 11库的使用)。而一个Materail对象则指向一种Effect。接下来的几节内容将详细讲解这些类型。
The Effect Class
Effect类封装了ID3DX11Effect接口类型(Effects 11库的顶层接口),并对外提供了获取techniques和shader变量的接口,其中使用了C++标准模板库(STL)中的map容器存储这些变量。Effect类中还包含了用于编译和加载shaders的代码。本质上,Effect类只是一个辅助类,让使用effects变得更容易一点。列表16.1列出了Effect类的声明。列表16.1 The Effect.h Header File
#pragma once
#include "Common.h"
#include "Technique.h"
#include "Variable.h"
namespace Library
{
class Game;
class Effect
{
public:
Effect(Game& game);
virtual ~Effect();
static void CompileEffectFromFile(ID3D11Device* direct3DDevice, ID3DX11Effect** effect, const std::wstring& filename);
static void LoadCompiledEffect(ID3D11Device* direct3DDevice, ID3DX11Effect** effect, const std::wstring& filename);
Game& GetGame();
ID3DX11Effect* GetEffect() const;
void SetEffect(ID3DX11Effect* effect);
const D3DX11_EFFECT_DESC& EffectDesc() const;
const std::vector<Technique*>& Techniques() const;
const std::map<std::string, Technique*>& TechniquesByName() const;
const std::vector<Variable*>& Variables() const;
const std::map<std::string, Variable*>& VariablesByName() const;
void CompileFromFile(const std::wstring& filename);
void LoadCompiledEffect(const std::wstring& filename);
private:
Effect(const Effect& rhs);
Effect& operator=(const Effect& rhs);
void Initialize();
Game& mGame;
ID3DX11Effect* mEffect;
D3DX11_EFFECT_DESC mEffectDesc;
std::vector<Technique*> mTechniques;
std::map<std::string, Technique*> mTechniquesByName;
std::vector<Variable*> mVariables;
std::map<std::string, Variable*> mVariablesByName;
};
}
Effect类中最值得关注的成员是CompileFromFile(),LoadCompiledEffect()和Initialize()函数。列表16.2中列出这些函数的实现代码。为了简洁,Effect类的实现代码中,一些只有一行代码的函数,类的析构函数,以及非静态的用于编译和加载effects的函数并没有列出来。在本书的配套网站上可以找到完全的实现代码。
列表16.2 The Effect Class Implementation (Abbreviated)
void Effect::CompileEffectFromFile(ID3D11Device* direct3DDevice, ID3DX11Effect** effect, const std::wstring& filename)
{
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(filename.c_str(), 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;
}
hr = D3DX11CreateEffectFromMemory(compiledShader->GetBufferPointer(), compiledShader->GetBufferSize(), NULL, direct3DDevice, effect);
if (FAILED(hr))
{
throw GameException("D3DX11CreateEffectFromMemory() failed.", hr);
}
ReleaseObject(compiledShader);
}
void Effect::LoadCompiledEffect(ID3D11Device* direct3DDevice, ID3DX11Effect** effect, const std::wstring& filename)
{
std::vector<char> compiledShader;
Utility::LoadBinaryFile(filename, compiledShader);
HRESULT hr = D3DX11CreateEffectFromMemory(&compiledShader.front(), compiledShader.size(), NULL, direct3DDevice, effect);
if (FAILED(hr))
{
throw GameException("D3DX11CreateEffectFromMemory() failed.", hr);
}
}
void Effect::Initialize()
{
HRESULT hr = mEffect->GetDesc(&mEffectDesc);
if (FAILED(hr))
{
throw GameException("ID3DX11Effect::GetDesc() failed.", hr);
}
for (UINT i = 0; i < mEffectDesc.Techniques; i++)
{
Technique* technique = new Technique(mGame, *this, mEffect->GetTechniqueByIndex(i));
mTechniques.push_back(technique);
mTechniquesByName.insert(std::pair<std::string, Technique*>(technique->Name(), technique));
}
for (UINT i = 0; i < mEffectDesc.GlobalVariables; i++)
{
Variable* variable = new Variable(*this, mEffect->GetVariableByIndex(i));
mVariables.push_back(variable);
mVariablesByName.insert(std::pair<std::string, Variable*>(variable->Name(), variable));
}
}
关于Effect::CompileEffectFromFile()函数的实现代码,我们之前已经见过。该函数主要是在运行时编译shader,并通过一个类对象存储对应的ID3DX11Effect对象。一个全新的函数是Effect::LoadCompiledEffect(),通过把一个已经编译好的shader目标文件( compiled shader object)加载到内存中,然后调用同样的D3DX11CreateEffectFromMemery()函数初始化一个effect对象。一个编译好的shader目标文件是编译过程中的输出文件,并保存在一个以.cso为后缀的文件中。那么如何生成一个.cso文件用于创建effect呢?
首先,把BasicEffect.fx文件添加到Visual Studio工程中,要么添加到Library工程要么是Game工程中。然后打开对应工程的属性页,并转到 Configuration Properties-->HLSL Compiler-->General。如图16.1所示,设置 Shder Type属性值为Effect(/fx), Shder Model属性值为Shader Model 5(/5_0)。确保在debug和release配置中都进行设置。
图16.1 Visual Studio’s property pages for HLSL compiler parameters.
现在,再编译Visual Studio工程时,就会针对工程包含的effects文件编译生成对应的.cso文件。默认情况下,生成的.cso文件与工程的库文件或可执行文件存放在同一个目录中(Visual Studio的$(OutDir)宏所对应的路径)。如果你倾向于存放在一个不同的目录中,只需要修改Configuration Properties-->HLSL Compiler-->Output Files属性页中的Object File Name属性值。本书的工程中使用如下设置:
$(OutDir)Content\Effects\%(Filename).cso
另外,如果在Library工程中包含原始的.fx文件,需要把生成的所有.cso文件拷贝到应用程序的可执行文件可以访问的目录中。可以使用一个post-build events完成。
关于Effect类的最后一个要讨论的是Initialize()函数(见列表16.2)。该函数中首先获取D3DX11_EFFECT_DESC结构体对象,然后遍历所有可用的techniques和shader variables,并初始化对应的封装类型的对象。
The Technique Class
Technique类是所有Effects 11封装类型中最简单的一个,仅仅是对外提供了获取technique以及对应passes名称的函数接口。列表16.3列出了Technique类的声明代码。列表16.3 The Technique.h Header File
#pragma once
#include "Common.h"
#include "Pass.h"
namespace Library
{
class Game;
class Effect;
class Technique
{
public:
Technique(Game& game, Effect& effect, ID3DX11EffectTechnique* technique);
~Technique();
Effect& GetEffect();
ID3DX11EffectTechnique* GetTechnique() const;
const D3DX11_TECHNIQUE_DESC& TechniqueDesc() const;
const std::string& Name() const;
const std::vector<Pass*>& Passes() const;
const std::map<std::string, Pass*>& PassesByName() const;
private:
Technique(const Technique& rhs);
Technique& operator=(const Technique& rhs);
Effect& mEffect;
ID3DX11EffectTechnique* mTechnique;
D3DX11_TECHNIQUE_DESC mTechniqueDesc;
std::string mName;
std::vector<Pass*> mPasses;
std::map<std::string, Pass*> mPassesByName;
};
}
在Technique类中只有一个成员需要重点讨论:类的构造函数,在该函数中通过遍历technique的passes,初始化每一个Pass类对象,并添加到STL容器中。列表16.4列出了构造函数的实现代码。
列表16.4 The Technique Class Constructor
Technique::Technique(Game& game, Effect& effect, ID3DX11EffectTechnique* technique)
: mEffect(effect), mTechnique(technique), mTechniqueDesc(), mName(), mPasses(), mPassesByName()
{
mTechnique->GetDesc(&mTechniqueDesc);
mName = mTechniqueDesc.Name;
for (UINT i = 0; i < mTechniqueDesc.Passes; i++)
{
Pass* pass = new Pass(game, *this, mTechnique->GetPassByIndex(i));
mPasses.push_back(pass);
mPassesByName.insert(std::pair<std::string, Pass*>(pass->Name(), pass));
}
}
The Pass Class
Pass类的结构模式与Effect和Technique类基础相同,封装了Effects 11中对应的ID3DX11EffectPass接口类型,并增加了个别函数。具体地说,Pass类中包含了创建input layout的函数。之所以使用一个Pass类对象创建input layout,是因为Pass类中包含了input signature(与vertex shader对应的shader inputs的定义)。列表16.5列出了Pass类的声明代码。列表16.5 The Pass.h Header File
#pragma once
#include "Common.h"
namespace Library
{
class Game;
class Technique;
class Pass
{
public:
Pass(Game& game, Technique& technique, ID3DX11EffectPass* pass);
Technique& GetTechnique();
ID3DX11EffectPass* GetPass() const;
const D3DX11_PASS_DESC& PassDesc() const;
const std::string& Name() const;
void CreateInputLayout(const D3D11_INPUT_ELEMENT_DESC* inputElementDesc, UINT numElements, ID3D11InputLayout **inputLayout);
void Apply(UINT flags, ID3D11DeviceContext* context);
private:
Pass(const Pass& rhs);
Pass& operator=(const Pass& rhs);
Game& mGame;
Technique& mTechnique;
ID3DX11EffectPass* mPass;
D3DX11_PASS_DESC mPassDesc;
std::string mName;
};
}
Pass中值得重点讨论的是类的构造函数以及CreateInputLayout函数(如列表16.6所示)。
列表16.6 The Pass Class Implementation (Abbreviated)
#include "Pass.h"
#include "Game.h"
#include "GameException.h"
namespace Library
{
Pass::Pass(Game& game, Technique& technique, ID3DX11EffectPass* pass)
: mGame(game), mTechnique(technique), mPass(pass), mPassDesc(), mName()
{
mPass->GetDesc(&mPassDesc);
mName = mPassDesc.Name;
}
Technique& Pass::GetTechnique()
{
return mTechnique;
}
ID3DX11EffectPass* Pass::GetPass() const
{
return mPass;
}
const D3DX11_PASS_DESC& Pass::PassDesc() const
{
return mPassDesc;
}
const std::string& Pass::Name() const
{
return mName;
}
void Pass::CreateInputLayout(const D3D11_INPUT_ELEMENT_DESC* inputElementDesc, UINT numElements, ID3D11InputLayout **inputLayout)
{
HRESULT hr = mGame.Direct3DDevice()->CreateInputLayout(inputElementDesc, numElements, mPassDesc.pIAInputSignature, mPassDesc.IAInputSignatureSize, inputLayout);
if (FAILED(hr))
{
throw GameException("ID3D11Device::CreateInputLayout() failed.", hr);
}
}
void Pass::Apply(UINT flags, ID3D11DeviceContext* context)
{
mPass->Apply(flags, context);
}
}
The Variable Class
Variable类中包含了ID3DX11EffectVariable类型的对象,并增加了一些友好的语法(<<运算符重载)用于更新一个shader变量。前面已经讲过,ID3DX11EffectVariable接口可以表示任何类型的shader变量。因此,在Variable类中对外提供了返回D3DX11_EFFECT_VARIABLE_DESC和D3DX11_EFFECT_TYPE_DESC类型对象的接口,用于详细表示一个variable的semantic,annotations以及variable内部的真实数据类型。列表16.7 The Variable.h Header File
#pragma once
#include "Common.h"
namespace Library
{
class Effect;
class Variable
{
public:
Variable(Effect& effect, ID3DX11EffectVariable* variable);
Effect& GetEffect();
ID3DX11EffectVariable* GetVariable() const;
const D3DX11_EFFECT_VARIABLE_DESC& VariableDesc() const;
ID3DX11EffectType* Type() const;
const D3DX11_EFFECT_TYPE_DESC& TypeDesc() const;
const std::string& Name() const;
Variable& operator<<(CXMMATRIX value);
Variable& operator<<(ID3D11ShaderResourceView* value);
Variable& operator<<(FXMVECTOR value);
Variable& operator<<(float value);
private:
Variable(const Variable& rhs);
Variable& operator=(const Variable& rhs);
Effect& mEffect;
ID3DX11EffectVariable* mVariable;
D3DX11_EFFECT_VARIABLE_DESC mVariableDesc;
ID3DX11EffectType* mType;
D3DX11_EFFECT_TYPE_DESC mTypeDesc;
std::string mName;
};
}
在Variable类中需要重点讲述的是这些<<运算符重载函数。这些函数借助于C++ iostream库的实现思想,提供了类型的语法用于更新shader的变量值。例如,使用下面的代码可以更新BasicEffect shader中的WorldViewProjection投影矩阵值:
XMMATRIX worldMatrix = XMLoadFloat4x4(&mWorldMatrix);
XMMATRIX wvp = worldMatrix * mCamera->ViewMatrix() *
mCamera->ProjectionMatrix();
mBasicMaterial->WorldViewProjection() << wvp;
在这段示例代码中,mBasicMaterial::WorldViewProjection()函数返回一个Variable对象的引用。列表16.8列出这些运算符重函数的代码。
列表16.8 Overloaded Operators for the Variable Class
#include "Variable.h"
#include "GameException.h"
namespace Library
{
Variable::Variable(Effect& effect, ID3DX11EffectVariable* variable)
: mEffect(effect), mVariable(variable), mVariableDesc(), mType(nullptr), mTypeDesc(), mName()
{
mVariable->GetDesc(&mVariableDesc);
mName = mVariableDesc.Name;
mType = mVariable->GetType();
mType->GetDesc(&mTypeDesc);
}
Effect& Variable::GetEffect()
{
return mEffect;
}
ID3DX11EffectVariable* Variable::GetVariable() const
{
return mVariable;
}
const D3DX11_EFFECT_VARIABLE_DESC& Variable::VariableDesc() const
{
return mVariableDesc;
}
const D3DX11_EFFECT_TYPE_DESC& Variable::TypeDesc() const
{
return mTypeDesc;
}
ID3DX11EffectType* Variable::Type() const
{
return mType;
}
const std::string& Variable::Name() const
{
return mName;
}
Variable& Variable::operator<<(CXMMATRIX value)
{
ID3DX11EffectMatrixVariable* variable = mVariable->AsMatrix();
if (variable->IsValid() == false)
{
throw GameException("Invalid effect variable cast.");
}
variable->SetMatrix(reinterpret_cast<const float*>(&value));
return *this;
}
Variable& Variable::operator<<(ID3D11ShaderResourceView* value)
{
ID3DX11EffectShaderResourceVariable* variable = mVariable->AsShaderResource();
if (variable->IsValid() == false)
{
throw GameException("Invalid effect variable cast.");
}
variable->SetResource(value);
return *this;
}
Variable& Variable::operator<<(FXMVECTOR value)
{
ID3DX11EffectVectorVariable* variable = mVariable->AsVector();
if (variable->IsValid() == false)
{
throw GameException("Invalid effect variable cast.");
}
variable->SetFloatVector(reinterpret_cast<const float*>(&value));
return *this;
}
Variable& Variable::operator<<(float value)
{
ID3DX11EffectScalarVariable* variable = mVariable->AsScalar();
if (variable->IsValid() == false)
{
throw GameException("Invalid effect variable cast.");
}
variable->SetFloat(value);
return *this;
}
}
使用列表16.8所示的语法,可以充分利用运行时类型检查的好处,但是你可以在Effect类中存储这些通用的Variable对象。另外,需要注意的是列表16.8中并没有包含所有可能的数据类型的重载函数,只是包含了后面几个示例中将要使用的函数。
The Material Class
在Effect类中包含了Variable和Technique对象,一个Technique对象中又包含了Pass对象。在这个系统类型中,还需要一个类用于把所有这些类集合到一起:这就是Material类。在Material类中,包含一个Effect指针类型的成员变量,以及用于创建与该Effect对象相关联的input layout和vertex buffers的成员函数。Material类的设计目的是作为一个基类,由相应的派生类表示一种具体的effect。例如,在即将要编写的示例程序中,会创建BasicMaterial和SkyboxMaterial类,分别用于表示BasicEffect.fx和Skybox.fx effects。这两个派生类缓存了effect的变量,并对外提供公有函数访问这些变量。通过使用一组宏定义可以简单地完成创建新的materials。实际上,可以创建一个通用的工具,用于读取一个material的.fx文件并生成对应的.h和.cpp文件。列表16.9列出了Material类的声明代码
列表16.9 The Material.h Header File
#pragma once
#include "Common.h"
#include "Effect.h"
namespace Library
{
class Model;
class Mesh;
class Material : public RTTI
{
RTTI_DECLARATIONS(Material, RTTI)
public:
Material();
Material(const std::string& defaultTechniqueName);
virtual ~Material();
Variable* operator[](const std::string& variableName);
Effect* GetEffect() const;
Technique* CurrentTechnique() const;
void SetCurrentTechnique(Technique* currentTechnique);
const std::map<Pass*, ID3D11InputLayout*>& InputLayouts() const;
virtual void Initialize(Effect* effect);
virtual void CreateVertexBuffer(ID3D11Device* device, const Model& model, std::vector<ID3D11Buffer*>& vertexBuffers) const;
virtual void CreateVertexBuffer(ID3D11Device* device, const Mesh& mesh, ID3D11Buffer** vertexBuffer) const = 0;
virtual UINT VertexSize() const = 0;
protected:
Material(const Material& rhs);
Material& operator=(const Material& rhs);
virtual void CreateInputLayout(const std::string& techniqueName, const std::string& passName, D3D11_INPUT_ELEMENT_DESC* inputElementDescriptions, UINT inputElementDescriptionCount);
Effect* mEffect;
Technique* mCurrentTechnique;
std::string mDefaultTechniqueName;
std::map<Pass*, ID3D11InputLayout*> mInputLayouts;
};
#define MATERIAL_VARIABLE_DECLARATION(VariableName) \
public: \
Variable& VariableName() const; \
private: \
Variable* m ## VariableName;
#define MATERIAL_VARIABLE_DEFINITION(Material, VariableName) \
Variable& Material::VariableName() const \
{ \
return *m ## VariableName; \
}
#define MATERIAL_VARIABLE_INITIALIZATION(VariableName) m ## VariableName(NULL)
#define MATERIAL_VARIABLE_RETRIEVE(VariableName) \
m ## VariableName = mEffect->VariablesByName().at(#VariableName);
}
在实例化一个Material类对象之后,需要调用Material::Initialize()函数初始化该对象。初始化操作主要是把effect赋值给material的相关变量,并设置当前要使用的technique(通过赋值给成员变量Material::mCurrentTechnique)。如果没有其他特定情况,mCurrentTechnique就是在渲染过程中使用的effect technique。在Material::Initialize()函数中通过成员变量Material::mDefaultTechniqueName(变量的初始值由Material类对象实例化时提供)设置当前的technique。成员变量Material::mInputsLayouts是一个STL map容器类型的变量,在Material::CreateInputLayout()函数中进行赋值。该map类型的变量中,使用Pass类对象作为map的键,然后使用与该键对应的ID3D11InputLayout对象值作为ID3DX11DeviceContext:;IASetInputLayout()函数的参数。如下代码演示了这种方法:
Pass* pass = mBasicMaterial->CurrentTechnique()->Passes().at(0);
ID3D11InputLayout* inputLayout = mBasicMaterial->InputLayouts().
at(pass);
direct3DDeviceContext->IASetInputLayout(inputLayout);
Material类有两个Material::CreateVertexBuffer()重载函数,一个是从一个Model类对象中创建一组vertex buffers,另一个是从一个Mesh类对象创建单个vertex buffer。后者是一个纯虚函数,必须在每一个派生类中重写。之所以这么做是因为每一种effect(由material表示)都有一个唯一的输出标识。重载函数Material::CreateVertexBuffer()的model-version(通过Mode对象创建vertex buffers)通过遍历调用mesh-version(通过Mesh对象创建vertex buffer)的函数创建了一个vertex buffers的容器。
另外一个纯虚函数Material::VertexSize()的目的是返回一个与material兼容的vertex的数据大小(单位为bytes)。
最后,分析一下列表16.9所示的代码中最后四个宏定义
MATERIAL_VARIABLE_DECLARATION
MATERIAL_VARIABLE_DEFINITION
MATERIAL_VARIABLE_INITIALIZATION
MATERIAL_VARIABLE_RETRIEVE
这些宏主要是在Material类的派生类中使用,通过公有函数对外提供访问Variable对象的接口。很快就会演示这些宏的用法。
列表16.10列出了Material类的完整实现代码。
列表16.10 The Material.cpp File
#include "Material.h"
#include "GameException.h"
#include "Model.h"
namespace Library
{
RTTI_DEFINITIONS(Material)
Material::Material()
: mEffect(nullptr), mCurrentTechnique(nullptr), mDefaultTechniqueName(), mInputLayouts()
{
}
Material::Material(const std::string& defaultTechniqueName)
: mEffect(nullptr), mCurrentTechnique(nullptr), mDefaultTechniqueName(defaultTechniqueName), mInputLayouts()
{
}
Material::~Material()
{
for (std::pair<Pass*, ID3D11InputLayout*> inputLayout : mInputLayouts)
{
ReleaseObject(inputLayout.second);
}
}
Variable* Material::operator[](const std::string& variableName)
{
const std::map<std::string, Variable*>& variables = mEffect->VariablesByName();
Variable* foundVariable = nullptr;
std::map<std::string, Variable*>::const_iterator found = variables.find(variableName);
if (found != variables.end())
{
foundVariable = found->second;
}
return foundVariable;
}
Effect* Material::GetEffect() const
{
return mEffect;
}
Technique* Material::CurrentTechnique() const
{
return mCurrentTechnique;
}
void Material::SetCurrentTechnique(Technique* currentTechnique)
{
mCurrentTechnique = currentTechnique;
}
const std::map<Pass*, ID3D11InputLayout*>& Material::InputLayouts() const
{
return mInputLayouts;
}
void Material::Initialize(Effect* effect)
{
mEffect = effect;
assert(mEffect != nullptr);
Technique* defaultTechnique = nullptr;
assert(mEffect->Techniques().size() > 0);
if (mDefaultTechniqueName.empty() == false)
{
defaultTechnique = mEffect->TechniquesByName().at(mDefaultTechniqueName);
assert(defaultTechnique != nullptr);
}
else
{
defaultTechnique = mEffect->Techniques().at(0);
}
SetCurrentTechnique(defaultTechnique);
}
void Material::CreateVertexBuffer(ID3D11Device* device, const Model& model, std::vector<ID3D11Buffer*>& vertexBuffers) const
{
vertexBuffers.reserve(model.Meshes().size());
for (Mesh* mesh : model.Meshes())
{
ID3D11Buffer* vertexBuffer;
CreateVertexBuffer(device, *mesh, &vertexBuffer);
vertexBuffers.push_back(vertexBuffer);
}
}
void Material::CreateInputLayout(const std::string& techniqueName, const std::string& passName, D3D11_INPUT_ELEMENT_DESC* inputElementDescriptions, UINT inputElementDescriptionCount)
{
Technique* technique = mEffect->TechniquesByName().at(techniqueName);
assert(technique != nullptr);
Pass* pass = technique->PassesByName().at(passName);
assert(pass != nullptr);
ID3D11InputLayout* inputLayout;
pass->CreateInputLayout(inputElementDescriptions, inputElementDescriptionCount, &inputLayout);
mInputLayouts.insert(std::pair<Pass*, ID3D11InputLayout*>(pass, inputLayout));
}
}