第十六章 Materials

第十六章 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));
    }
}



  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值