Gamebryo—Material System

Material System

一、 简介

GB的材质系统是一种基于NiMesh的渲染状态创建相应NiShader实例的机制。开发者经常需要为多个对象进行同种特效处理,以及当单个NiShader无法满足绘制需求时,与其让一个美术为不同几何体提供不同的NiShader,不如使用单个NiMaterial,它可以在运行时为每一个几何体提供合适的NiShader。

一个简单材质可以查询mesh从而决定它是否进行蒙皮(Skinned)处理。它可以选择不同的蒙皮(被NiShader定义,并从shader library中加载)。一些复杂的材质,可以动态生成着色程序,这些着色程序既可以被添加也可以被减少。在添加系统中可以使用各种高级着色语言程序段,并把他们练到一起从而组成一个完整的着色程序。在减少系统中可以使用预编译处理器命令(compile-time preprocessor commands)进入一个着色程序并删除相应的片段。

NiMaterial VS NiShader

一个NiShader包含了一个几何体绘制时的所有渲染状态。它可以扫描多个render pass从而描述几何体在不同情况下如何被绘制。一个NiShader可以被多个几何体共享。

可以使用NSF或FX Shader来实现NiShader,这些shader可以被用来创建各种特效,但在特定情况下,只有一个被激活。

一个NiMaterial负责根据不同情况决定使用那一个NiShader,以及当NiShader不存在时可以创建。NiMaterial会根据NiMesh以及它的属性,以及当前硬件、用户设定来做出正确的决定。

总之,NiMaterial描述了如何进行使用特效,而 NiShader实现具体的特效。

二、 架构

1. NiMaterial

为开发者提供了一种方法,使得它可以控制特定的渲染对象使用那个NiShader。应用程序在使用材质系统时,首先会询问先前的shader是否继续有效(IsShaderCurrent),如果无效或先前没有shader则会调用GetCurrentShader,即提供一个可用的Material。

clip_image002

NiFragmentMaterial:负责添加高级着色语言系统的基类

NiSingleShaderMaterial:在材质框架中引入NiShader基类。

2. NiFragmentMaterial

该类是处理shader fragment system(着色器程序段)的基类,通常该系统使用hlsl或cg函数去定义一个完整的着色程序,这些函数的输入和输出可以被被连接在一起,从而完成一个完整的着色程序。这些程序段的连接关系形成了一个“shader tree”。

NiFragmentMaterial封装了GB中实现一个shader-tree的框架,这条管线里生成一个编译好的shader需要经过如下步骤:

第一步:生成几何体的材质描述符。该过程生成一个NiMaterialDescriptor对象,它时一个128位,描述了几何体附属于shader-tree的状态信息。GenerateDescriptor封装了这一过程。

第二步:查询NiShader缓冲区决定该材质配置是否已经被处理。如果没则使用GenerateShader,如果处理过了只要重新使用即可。

第三步:搜索失败shader列表,判断该材质配置是否被当前硬件支持,如果否,则返回NULL,此时渲染器会使用内部error shader。

第四步:把材质描述符分解成一个或多个NiGPUProgrammDescriptors。它是一个128位,包含了pass生成所有正确NiGPUProgram的条件。GenerateShaderDescArray封装了这一过程。

第五步:为当前渲染器创建合适的NiShader子类。

第六步:为该shader指定packing requirements。

第七步:遍历材质实例中的每一个NiGPUProgramDescriptors,生成相应的render pass对象。

(1) 决定如何和先前的pass进行混合(SetAlphaOverride)

(2) 位该pass生成GPU程序(GenerateShaderProgram)

A: 生成顶点着色器程序(GenerateVertexShaderProgram)

B: 生成像素着色器程序(GeneratePixelShaderProgram)

C:为每一个生成的程序创建NiShaderConstantMap,同时添加所有变量(AddResourceToShaderConstantMap)。

D:如果发生错误,则返回错误信息。

第八步:如果成功,则初始化新的shader并返回。否则处理回到控制。

2.1 创建一个shader tree

The Pipeline

在上述管线中,会生成一个shade tree并且编译每一个NiGPUProgram。在GB中,shade tree被封装到一个NiMaterialConfigurator对象中。它的子类将查找NiGPUProgramDescriptor中的标识位,从而决定那些统一常量将被关联的对应的树节点上。统一常量被封装在NiMaterialResource中,而树的节点被封装在NiMaterialNode。对应节点输入、输出资源之间以及与其它节点之间的连接,和统一常量一起被封装在NiMaterialResourceBinding中。一旦所有节点之间的连接被建立并且添加到树中,此时NiMaterialConfigurator中Evaluate函数将被调用并生成NiGPUProgram。

NiMaterialFragmentNodes

在需要创建shade tree时,必须创建节点,这些节点拥有一些代码片段。这些节点被称作NiMaterialFragmentNodes。一个指定节点包含一组代码片段,这些代码片段会和不同着色语言、不同硬件模型、不同平台进行通信。如,一个支持低级别shader mode显卡会使用较简单的光照计算但是会更快。而高级显卡中,会使用更新的指令以及更复杂的算法。所有的节点实现必须遵循同一输入、输出资源。Shade tree在所有节点的输入得到满足后建立。通常情况下节点片段会定义默认的输入资源值。

NiMaterialNodeLibraries

NiMaterialNodes可以通过NiMaterialNodeLibrary对象添加到应用程序中。这些对象包含了一组材质节点,这些节点可以被添加到一个shade tree中。这样实现了shade tree的完全数据驱动。有两种方法可以生成这些库—解析一个XML文件,或者把XML文件转换为C++类从而实现和应用程序进行连接。后一种方法在NiStandardMaterial中被使用,NiStandardMaterial是NiFragmentMaterial的子类,它实现了GB默认的渲染管线。在NiMain\StandardMaterialNodeLibrary中一个XML文件包含了材质中使用的所有片段。无论这些片段是被改变、添加、删除一组文件将调用DeveloperTool\NiMaterialNodeXMLLibraryParse执行,从而创建C++类来代表这个库。作为选择,应用程序可以使用NiMaterialNodeXMLLibraryReader来解析这个XML库。

NiMaterialResources

NiMaterialResources可以是多种数据类型,bool、uint、floar、point2s、point3s、point4s、matrix4s、colors、textures。如前所述,每一个节点会拥有一组输入、输出资源。其它资源可以来自于以下:

l 常量。

l 预定义。该类型是GB提供的一个预定义类型。

l 属性。该值是被挂载在对应几何体上,它是per-object的。

l 全局。他是应用程序注册的,如灯光相关、每一天的时间。

l 对象。它是场景中的一个对象,如灯光、纹理特效。

2.2 回调控制

偶尔,一个NiFragmentMaterial会创建shader失败。这种情况经常由于编译GPU程序失败。在这种情况下,材质会重新创建NiGPUProgramDescriptors(第四步)此时会进行简单处理,并试图再建立shader。材质也许会简单使用descriptors把一个特效分解成多个passes,或者减少特定的光照或纹理。

材质会提供相应的回调功能。这些回调函数会使用如下签名:

clip_image004

当回调函数被调用,它将被传入所有的NiMaterialDescriptor,它描述了所有的特效。同时将获得一个ReturnCode,该报告描述了先前为什么失败、失败pass的标识、RenderPassDescriptor(详细描述了该pass)对象数组。获得这些信息,回调函数可以标识问题的地方、以及校正信息。

3. NiMaterialInstance

该类允许一个材质被挂载到一个几何体上,它负责管理材质为渲染器生成一个新shader的方式和时间。该过程是通过保存最近、最合适的shader到指定材质来实现的。

4. NiMaterialLibrary

材质库是一种将新材质加入到GB材质系统和美术管线中的途径。一个新材质库包含一些列材质描述符(material descriptors)以及根据需要生成材质的能力。

DLL-Based Application Usage

如何使用材质库决定于材质库的使用类型,

LIB-Based Application Usage

Adding Material In Art

5. NiSingleShaderMaterial

该类是一种在应用程序中向几何体添加NiShader快速方法,首选生成NiSingleShaderMaterials的方法是NiSingleShaderMaterialLibrary,它通过在NiShaderFactory中搜索所有的NiShaderLibrary来完成的。

NiSingleShaderMaterial的GetCurrentShader方法。

6. NiStandardMaterial

该类实现了GB默认的渲染管线(tdefault Gamebryo rendering pipeline),同时它也包含了一些相关的设置,这些设置使材质和GB的legacy pipeline更为相似。

NiStandardMaterial拥有一定数量的回调函数,这些回调函数都有默认的实现。按顺序有如下几步:

A:SplitPerPixelLights

该方法会把失败pass中的per-pixel灯光一分为二,分别放到两个pass中,这样生成了一个新的pass。一旦失败该函数会一直递归知道每个pass只含有个灯光。

B:SplitPerVertexLights

与A原理类似。

C:SplitTextureMaps

该方法同样把一个失败的pass分成两个pass。在第一个pass中会和光照一起处理base、dark、detail、projected light、shadow贴图。而第二个pass会处理glow、environment贴图。该方法最多执行一次,不会递归执行。

7. NiGPUProgrammCache System

该系统设计的目的使包含那些先前被材质系统编译好的NiGPUProgramms。在一个材质系统中经常含有一些需要重用那些先前编译的GPU程序段的NiShader。由于GPU程序的编译是一非常耗费的操作。该cache可以存储那些先前的编译结果。一旦程序段被编译,它将被插入到该cache中,同时也可以选择被保存到磁盘中。

该类的两个重要接口是FindCachedProgram和GenerateProgram。前者用于搜索一个指定名字的程序段。该方法返回两个对象,即找到的GPU程序段,以及一些列被该程序段使用的资源。GenerateProgram可以完成编译一个使用高级着色语言实现的程序。

每一个种渲染器会实现自身的程序cache类,DX9-NiD3DGPUProgramCache,它可以遍历HLSL程序。如果引用程序不想手动创建程序caches,可以使用NiRenderer:SetDefaultProgramCache方法,获得正确的NiGPUProgramCache,然后再把它设给指定的NiFragmentMaterial。

应用程序可以拥有多个caches。

三、 使用

1. NiMaterial Usage

Resolving Material At Runtime:在加载时,一个美术资源引用了一个材质,此时会检查材质缓冲区(NiMaterial:GetMaterial)。如果该材质不存在,则查询材质库(NiMaterialLibrary::CreateMaterial)。如果还是没有找到,此时渲染器会使用默认的才是绘制对象。

Adding Custom Parameters To Art

2. 使用材质

可以通过NiMaterialInstance来向指定渲染对象添加材质。这允许多个渲染对象共享一个NiMaterial实例。NiMaterialInstance包含:一个自身材质的引用、最近材质使用的NiShader、一个标志(判断当前cached shader是否为最新)、uint类型数(标识使用该shader的时间)。

GB支持同一渲染对象拥有多个材质,但特定时刻只能有一个活动材质。这样可以创建出更多的特效。

渲染对象拥有一些处理材质的方法:

ApplyMaterial添加材质。

SetActiveMaterial激活材质。

ApplyAndSetActiveMaterial,添加并激活材质。

3. Gamebryo Legacy Pipeline

GB保留Legacy Pipeline有两个原因:某些硬件不支持标准材质;保持与老版本引擎的兼容。所有的渲染器都支持Legacy Pipeline。

clip_image006

4. Gamebryo Standard Material

(1)标准材质的特征

clip_image008

clip_image010

5. Renderer Interaction With Material

当绘制一个物体时,首先需要通过NiRenderObject::GetShaderFromMaterial函数来获得一个活动材质的实例—NiMaterialInstance。此时材质实例将查询NiShader用于渲染对象。

NiMaterialInstance

材质实例提供shader时,首先要做的是查询cached shader是否存在。材质实例保存了一个shader指针,从而防止将来被重新使用。

默认材质

如果当前的渲染对象没有材质处于活动状态,此时渲染器会为该渲染对象设置一个默认的材质。

Error Shader

如果渲染对象的当前活动材质不能提供一个可用的Shader,此时渲染器会使用一个Error Shader来绘制物体。

四、 使用扩展

GB的材质系统使用的可扩展设计,所以用户可以根据需要来扩展该材质系统。

用例1:使用标准Shaders

通常情况下,用户要使用简单的预定义shader程序-FX、NSF、或者其它NiShader。此时,用户使用的材质必须继承于NiMaterial。

用例2:扩展标准材质

用例3:创建子材质系统

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章