Filament 将实体(Entity)属性看成组件(component)的形式,实体可以是相机,光源,可绘制实体, 全局光照(IBL)实体等类型。一个场景包含最多、最主要的是可绘制实体对象。可绘制实体主要包含两种属性 可绘制属性Renderable 和 变换属性Transform,Renderable 与 Transform 被看作两种组件。Filament 的对象和组件都是通过各自的Manager创建和管理的, Filament 有自己的内存管理模块,类实例通过句柄 handle 可以映射到对应的内存地址上。组件在内部以实例instance的方式存在。
创建一个可渲染对象主要经过以下三个过程:
(1)创建 Enitity 对象
Entity 对象只是包含一个全局唯一标识符的对象,相当于实例对象的句柄, 下面给出 Entity 类定义:
class Entity {
public:
// this can be used to create an array of to-be-filled entities (see create())
Entity() noexcept = default;
// Entities can be copied
Entity(const Entity& e) noexcept = default;
Entity(Entity&& e) noexcept = default;
Entity& operator=(const Entity& e) noexcept = default;
Entity& operator=(Entity&& e) noexcept = default;
// Entities can be compared
bool operator==(Entity e) const { return e.mIdentity == mIdentity; }
bool operator!=(Entity e) const { return e.mIdentity != mIdentity; }
// Entities can be sorted
bool operator<(Entity e) const { return e.mIdentity < mIdentity; }
bool isNull() const noexcept {
return mIdentity == 0;
}
uint32_t getId() const noexcept {
return mIdentity;
}
explicit operator bool() const noexcept { return !isNull(); }
private:
friend class EntityManager;
friend class EntityManagerImpl;
friend struct std::hash;
using Type = uint32_t;
explicit Entity(Type identity) noexcept : mIdentity(identity) { }
Type mIdentity = 0;
};
Filament 创建 Entity 示例:
Entity rootEntity;
EntityManager::get().create(1, &rootEntity);
(2)准备渲染所需的对象资源 VertexBuffer, IndexBuffer, Material
// 构建 Builder 并描述所需 VBO 状态
VertexBuffer::Builder vertexBufferBuilder = VertexBuffer::Builder()
.vertexCount((uint32_t)asset.positions.size())
.bufferCount(4)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::HALF4)
.attribute(VertexAttribute::TANGENTS, 1, VertexBuffer::AttributeType::SHORT4)
.normalized(VertexAttribute::TANGENTS);
// Builder 通过 build 方法创建 VertexBuffer
mVertexBuffer = vertexBufferBuilder.build(mEngine);
// 设置 VBO 数据
mVertexBuffer->setBufferAt(mEngine, 0,
VertexBuffer::BufferDescriptor(ps->data(), ps->size(), State::free, ps));
mIndexBuffer = IndexBuffer::Builder().indexCount(uint32_t(is->size())).build(mEngine);
mIndexBuffer->setBuffer(mEngine,
IndexBuffer::BufferDescriptor(is->data(), is->size(), State::free, is));
Filament 基于物理渲染, 支持自定义shader , 材质以脚本形式定义,并通过材质解析器读取JSON 格式的材质文件进行解析的。我们可将Material 看做是材质模版, 具体到渲染对象使用材质时是通过材质对象创建instance实例绑定到实体的Renderable组件来描述渲染对象的外观。
Material 材质模版的定义:
class FMaterial : public Material {
public:
FMaterial(FEngine& engine, const Material::Builder& builder);
~FMaterial() noexcept;
class DefaultMaterialBuilder : public Material::Builder {
public:
DefaultMaterialBuilder();
};
void terminate(FEngine& engine);
// return the uniform interface block for this material
const UniformInterfaceBlock& getUniformInterfaceBlock() const noexcept {
return mUniformInterfaceBlock;
}
// return the uniform interface block for this material
const SamplerInterfaceBlock& getSamplerInterfaceBlock() const noexcept {
return mSamplerInterfaceBlock;
}
// Create an instance of this material
FMaterialInstance* createInstance() const noexcept;
bool hasParameter(const char* name) const noexcept;
FMaterialInstance const* getDefaultInstance() const noexcept { return &mDefaultInstance; }
FMaterialInstance* getDefaultInstance() noexcept { return &mDefaultInstance; }
FEngine& getEngine() const noexcept { return mEngine; }
backend::Handle getProgramSlow(uint8_t variantKey) const noexcept;
backend::Handle getSurfaceProgramSlow(uint8_t variantKey) const noexcept;
backend::Handle getPostProcessProgramSlow(uint8_t variantKey) const noexcept;
backend::Handle getProgram(uint8_t variantKey) const noexcept {
backend::Handle const entry = mCachedPrograms[variantKey];
return UTILS_LIKELY(entry) ? entry : getProgramSlow(variantKey);
}
backend::Program getProgramBuilderWithVariants(uint8_t variantKey, uint8_t vertexVariantKey,
uint8_t fragmentVariantKey) const noexcept;
backend::Handle createAndCacheProgram(backend::Program&& p,
uint8_t variantKey) const noexcept;
bool isVariantLit() const noexcept { return mIsVariantLit; }
const utils::CString& getName() const noexcept { return mName; }
backend::RasterState getRasterState() const noexcept { return mRasterState; }
uint32_t getId() const noexcept { return mMaterialId; }
Shading getShading() const noexcept { return mShading; }
Interpolation getInterpolation() const noexcept { return mInterpolation; }
BlendingMode getBlendingMode() const noexcept { return mBlendingMode; }
BlendingMode getRenderBlendingMode() const noexcept { return mRenderBlendingMode; }
VertexDomain getVertexDomain() const noexcept { return mVertexDomain; }
MaterialDomain getMaterialDomain() const noexcept { return mMaterialDomain; }
CullingMode getCullingMode() const noexcept { return mCullingMode; }
TransparencyMode getTransparencyMode() const noexcept { return mTransparencyMode; }
bool isColorWriteEnabled() const noexcept { return mRasterState.colorWrite; }
bool isDepthWriteEnabled() const noexcept { return mRasterState.depthWrite; }
bool isDepthCullingEnabled() const noexcept {
return mRasterState.depthFunc != backend::RasterState::DepthFunc::A;
}
bool isDoubleSided() const noexcept { return mDoubleSided; }
bool hasDoubleSidedCapability() const noexcept { return mDoubleSidedCapability; }
float getMaskThreshold() const noexcept { return mMaskThreshold; }
bool hasShadowMultiplier() const noexcept { return mHasShadowMultiplier; }
AttributeBitset getRequiredAttributes() const noexcept { return mRequiredAttributes; }
bool hasSpecularAntiAliasing() const noexcept { return mSpecularAntiAliasing; }
float getSpecularAntiAliasingVariance() const noexcept { return mSpecularAntiAliasingVariance; }
float getSpecularAntiAliasingThreshold() const noexcept { return mSpecularAntiAliasingThreshold; }
size_t getParameterCount() const noexcept {
return mUniformInterfaceBlock.getUniformInfoList().size() +
mSamplerInterfaceBlock.getSamplerInfoList().size();
}
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
uint32_t generateMaterialInstanceId() const noexcept { return mMaterialInstanceId++; }
private:
// try to order by frequency of use
mutable std::array, VARIANT_COUNT> mCachedPrograms;
backend::RasterState mRasterState;
BlendingMode mRenderBlendingMode;
TransparencyMode mTransparencyMode;
bool mIsVariantLit;
Shading mShading;
BlendingMode mBlendingMode;
Interpolation mInterpolation;
VertexDomain mVertexDomain;
MaterialDomain mMaterialDomain;
CullingMode mCullingMode;
AttributeBitset mRequiredAttributes;
float mMaskThreshold = 0.4f;
float mSpecularAntiAliasingVariance;
float mSpecularAntiAliasingThreshold;
bool mDoubleSided;
bool mDoubleSidedCapability = false;
bool mHasShadowMultiplier = false;
bool mHasCustomDepthShader = false;
bool mIsDefaultMaterial = false;
bool mSpecularAntiAliasing = false;
FMaterialInstance mDefaultInstance;
SamplerInterfaceBlock mSamplerInterfaceBlock;
UniformInterfaceBlock mUniformInterfaceBlock;
SamplerBindingMap mSamplerBindings;
utils::CString mName;
FEngine& mEngine;
const uint32_t mMaterialId;
mutable uint32_t mMaterialInstanceId = 0;
MaterialParser* mMaterialParser = nullptr;
};
Material 定义了各种渲染属性,双面光照,背面剔除,图元插值方式(smooth, flat), 混合模式,反走样,纹理采样,着色器及uniform buffer对外界口。Filament 常量的交互是通过组织成uniform buffer 的形式,着在现代图形引擎中使用UBO更高效。Filament 支持 Vulkan , OpenGLES 类型的 shader, 对材质的转换也提供了相应的工具 matc (mateiral converter), shader 压缩工具 glslminifier。
MaterialInstance 是 Material 的实例和接口类,负责设置shader参数与渲染状态参数。当Material 被创建时,默认会创建一个相关的实例对象, 每一个可渲染对象 Renderable 都可以 关联一个材质实例对象 materialInstance。
VBO , EBO, Material 数据将用来设置Renderable组件,Renderable 组件管理渲染对象几何数据 与 材质。
(3)创建 Renderable 组件实例 Instance,将渲染数据关联到 Instance,最后通过build 方法将 Instance 与 Entity 实例关联
RenderableManager::Builder(1)
.boundingBox({{ 0, 0, 0 },
{ 1, 1, 1 }})
.material(0, mMaterialInstanceSolid)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, mVertexBuffer, mIndexBuffer, 0, 3*2*6)
.priority(7)
.culling(culling)
.build(engine, entity);
RenderableManager::Builder 的成员函数 geometry 是渲染数据设置接口, 我们可以看到对一个可渲染的数据,包括包围盒设置、材质设置、图元设置、优先级设置、剔除设置, build 成员方法将 Renderable 组件 与 entity 关联。在引擎内部, 组件都是以实例(instance)的形式存在,通过map 关联起来。
(4)创建 Transform 组件实例 Instance, 通过TransformManager.create(utils::Entity entity, Instance parent = {}, const math::mat4f& localTransform = {}) 方法关联到实体对象上。Transform组件是可选的,如果对象不需要矩阵变换可以不创建, 默认是单位矩阵。
TransformManager& tcm = Engine.getTransformManager();
//Add transform instance to entity
tcm.create(entity, TransformManager::Instance{}, mat4f());
Transform 组件 与 Renderable 组件的关联方式不一样,它是通过create方法创建instance并与entity 关联的。
(5)将渲染实体添加到场景图中
Filament 的 scene 负责场景对象的组织和管理,scene 的创建工作由 Engine 负责。
FScene& scene = view.getScene();
void FScene::addEntity(Entity entity) {
mEntities.insert(entity);
}
void FScene::addEntities(const Entity* entities, size_t count) {
mEntities.insert(entities, entities + count);
}
// 创建 Scene、添加渲染实体、并由 view 管理 scene.
mScene = mEngine->createScene();
mScene->addEntity(cameraCube->getWireFrameRenderable());
view->setScene(mScene);