Iron Scene Structure - Materials_环球热文
Iron Scene Structure - Materials
* Graphics
* [Materials](https://github.com/armory3d/armory/wiki/materials)
(资料图)
* Code
* [Find objects in the scene](https://github.com/armory3d/armory/wiki/Find-objects-in-the-scene)
* Engine Development
* [Render Path](https://github.com/armory3d/armory/wiki/renderpath)
Armory 只提供了三个设置材质属性的节点,可以设置 `RGB`, `Value` 和 `Image Texture` 三种值。官方教程 Material parameters 演示了两种材质属性修改方法,一是使用逻辑节点,二是使用 Haxe 脚本,这种方式更灵活,可以实现更多功能。
armory_examples-22.06\material_params\Sources\arm\MyTrait.hx
示例场景中,只含有三个 Cube,它们材质设置如下:
1. Cube.001 设置了一个同名的 **RGB** 节点连接到 Diffuse BSDF 着色器;
2. Cube.002 设置了一个同名的 **Image Texture** 节点连接到 Diffuse BSDF 着色器;
3. Cube.003 设置了一个同名的 **Value** 节点连接到 Checker Texture,再接 Diffuse BSDF着色器;
除了 Object 属性可以留空使用 [owner] 对象,其它都需要提供相应的值。材质可以使用 `Material` 或者使用 `GetMaterialNode` 节点获取。
使用逻辑节点对材质进行编程,这些材质设置的重点是节点名称,可以修改节点名称,只需要在逻辑编辑器中使节点中 **Node** 属性值与材质设置的节点名称一致。并且,要在材质侧栏面板中激活属性才能使用:
Logic Node Editor - Properties - Armory Material Node - Parameter
目前支持的材质节点数量有限,除了以上三个之外,比如 Checker Texture 等等的程序化纹理就不支持。
示例中,在场景属性面板附加了一个 `NodeTree` 逻辑节点树,和一个 `MyTrait` 脚本扩展。逻辑节点树使用了键盘事件 Keyboard,鼠标事件 Mouse,触屏事件 Touch 等事件节点,然后合并到 Merge 节点,只要任何一个事件触发就支执行后续的材质设置逻辑。
默认启用脚本扩展,轮番切换纹理图像。调用 `Time.time()` -> `kha.Scheduler.time()` 获取到引擎当前的已运行时间作为变量。本来预期示例中 REB 和 Value 两个材质属性也应该生效,但是在 Links 回调函数中并没有获取到相应参数的回调。而逻辑节点通过 `UniformsManager` 设置的材质属性却可以生效。多功能节点分组下,还有一个 `Get Application Time` 节点,还可以可以获取前后两帧的时间差:
Iron Uniforms `externalVec3Links` 这些变量是在外部初始化的,具体是 Armory Uniforms 负责。Iron Uniforms API 执行时有一个逻辑,比如说 `setObjectConstant()` 方法中设置材质颜色,参数类型条件是 c.type == "vec3",当它从其中一个回调函数获取到了值,那么就会忽略其它的回调函数。
因此,当材质节点 RGB 可以直接提供数据,那么 `externalVec3Links` 后续的回调函数就被跳过了。但是 RGB 节点本身又不能设置 null,除非可以将用户的回调函数放到材质节点的数据输出之前。其中一个方法就是使用 `UniformsManager`。
为了在 Haxe 脚本中实现颜色的不断更新随机值,以下示例代码的基础上,增加了 color变量,并通过 `UniformsManager` 去注册相应的着色器常量 Links 函数。
掌握了通用的逻辑节点功能后,接下来就要使用它们来操作场景的对象,包括动画、导航、网络,以及对象材质,Trait 扩展设置、物理系统、声音,Canvas 2D 画面绘画,甚至是后期处理,Render Path 深入引擎渲染。
这些操作免不了需要翻开源代码来操作,因为官方提供的 API 文档实是不如直接打开源代码来得更有效果,单看 API 文档,虽然知道它存在目的是做什么,但依然会出现无法理解或琢磨出 API 应该怎么使用的情况。当然,这主要原因是图形学知识点的缺失,通过代码阅读在一定程度上可以弥补。
就如逻辑节点中,设置灯光就需要使用 Iron LightObject 对象,这就涉及光照技术,如果有良好的图形学基础,那么很多功能函数基本上看一眼就知道可以用它来干什么了。比如说以下代码片段提供了注解,那么在掌握 GLSL 着色器编程基础的条件下,很快就可以领悟,这些变量会传递到着色器程序中作为 [uniforms]。
比如,灯光对象 `LightObject` 的构建器中可以看到灯光模型数据保存在 `LightData` 数据属性,包括颜色、强度、类型等等。其中 **rp_shadowmap** 是一个编译命令定义的符号,定义在 khafile.js 脚本,通过它的前缀也可以知道它是 Render Path 相关的功能,这是定制游戏渲染引擎的技术细节。需要有相当的基础才能完全掌握这些代码存在的目的:
又难一点的是设置对象的材质,首先要了解模型由顶点构成的面片组成,顶点除了 3D 空间坐标之外,还有 UV 纹理映射坐标,纹理贴图就是最基本的一种材质属性。面片又可以设置不同材质,这就产生了材质插槽的概念,Material Slots,一个模型中可以有多个插槽,每个插槽设置一种材质,对应应于目标面片。
给一个几何对象设置材质是相对容易的,只需要指定 MesshObject或 DecalObject,再指定材质对象。但是设置指定对象的材质的参数,操作步骤就显得有点繁琐。以下是材质相关的节点介绍:
1. `MaterialNode` 节点只有一个属性用于选定场景中的材质,输出 `MaterialData`;
2. `GetMaterialNode` 节点输出的是指定 `MaterialData`,材质的数据模型;
3. Set Object Material `SetMaterialNode` 节点由新版本 `SetMaterialSlotNode` 取代。
4. `SetMaterialRgbParamNode` 设置对象材质的颜色属性,使用数据类型 `iron.math.Vec4`;
5. `SetMaterialValueParamNode` 设置对象材质的属性值,使用数据类型 `Null<kha.FastFloat>`;
6. `SetMaterialImageParamNode` 设置对象材质的属性值,使用数据类型 `Null<kha.FastFloat>`;
变更全局材质设置,就修改 **Get Scene Root** 输出的场景对象,否则使用节点对象列表中的对象。节点还有一个**Per Object**模式选择,如果激活它,就表示不对全局修改材质设置,只修改指定对象。
在设置参数,`Object` 和 `Material` 分别指要操作的对象和其材质对应的 Solt,但是 `Node` 这个参数就让人难以琢磨,是什么鬼?材质编辑器中,可以给几何体设置任意的材质节点,即对应脚本中材质属性。每个材质节点的标题都会显示节点的名称,也可以在侧栏面板中编辑和复制它。在逻辑节点编辑器中使用材质属性设置节点时,就可以使用这个节点名称。在编程中,`Node` 对应的就是 `MaterialData` 属性名称。
材质属性设置都会使用到 `UniformsManager`,这是一个着色器 uniforms 常量管理工具,它是相当两种编程语言之间的桥梁,Haxe 代码中的数据通过它传递给 GLSL 着色器中的 uniforms 常量。反过来,也可以从着色器中获取相关的值。暂且称之为 **GLSL 常量管理器**。
GLSL 常量管理器和 `Uniforms` 类型搭配使用,注意这个工具类型有两个定义。GLSL 常量管理器和 armory.object.Uniforms和都需要调用 iron.object.Uniforms提供的数据。后者使得了一系列数组存储各种数据链接关系,这些静态数组由 armory.object.Uniforms进行初始化以及管理。在这里可以看到 Haxe 的另一种怪异语法,数组元素是长长的映射路径。另一种怪异的语法是构造函数在只有一条语句的情况下,可以省略花括号,这是一种偷懒语法: HaxeManual/AbstractArrayAccessOrder.hx
在首次遇到这种语法结构时,首选是搜索引擎查资料。但其实作为一个小众编程语言,并没有太多实用的信息,更别说涉及到细节的 Haxe 教程。所以还是在官方的 Haxe Manual 中找资料,最好使用 MD 源文档,搜索的效率更高。如果已经安装 [RunSnippet Sublime Text Plugin](https://github.com/jimboyeah/run-snippet),并且在 Sublime 中阅读此文档,那么直接将光标放在 [Language Features] [Function Bindings] 这些关键字上,按 F9 就可以跳转到主题内容中,根据不用再去搜索。
Haxe 语言中的 Arrow Function 使用 -> 箭头表示匿名函数,如下所示:
Haxe 数组声明中这种连续的 ->会让人有种误解,学习过链表数据结构的人可能直观地认为这就是链表结构,但其实它是函数原型的表达。以下程序演示了如何使用这种怪异的数组声明表达:
所谓 Links,就是一组材质处理函数的参数到材质属性数据的映射关系。Iron Uniforms 提供材质处理函数,`UniformsManager` 或者 Armory Uniforms 提供映射关系,即 Links 静态变量中保存的一组函数,回调函数在 `register()` 方法中设置。并由 Iron Uniforms API 在处理的过程中进行回调,因此**Links** 也可以看作是一组回调函数,它们用来向着色器注入数据。参考官方示例 [Material shaders]。
以上所描述的关系可能显得有点乱,简化一下表达就是:Iron Uniforms 定义了一套材质处理函数的规范,规范中提供一个回调接口,`UniformsManager` 或者 Armory Uniforms 都可以使用这个回调接口,向里面注册回调函数,函数的入口参数都一致使用: Object->MaterialData->String,至于返回什么数据就看是什么类型的 Links 集合。下表显示了它们的对应关系:
Iron Uniforms 有三对核心方法,它们在每一帧都会被 `RenderPath` 调用以更新着色器的数据,并且内部还会对所有已经登记的 Links 函数进行回调。对于每一个材质参数,回调所有类型对应的 Links 函数,需要判断 link 参数传递的值,是否与目标的属性值一致,然后再作处理。如果不一致,则返回 `null` 告诉引擎不需要理会当前值。如果回调函数已经返回一个值,那么同类的其它回调函数就会被跳过。
Armory 引擎的核心就是 [Render Path],这是一个可编程的设计,用户可以创建自己的渲染路径,实现一个 `RenderPathCreator` 类并将代码放置到项目下的目录中:
Libraries\lib_name\Sources\celshade\renderpath\RenderPathCreator.hx
Sources\arm\renderpath\RenderPathCreator.hx
关于驱动接口可以参考:armsdk\armory\blender\arm\api.py
虽然 API 文档中对材质节点中的 Node 参数有说明,但依然让人有一种“材质内部有什么节点?”的疑惑。
@input Node: Name of the parameter.
在设置参数,`Object` 和 `Material` 分别指要操作的对象和其材质对应的 Solt,但是 `Node` 这个参数就让人难以琢磨,是什么鬼?材质编辑器中,可以给几何体设置任意的材质节点,即对应脚本中材质属性。每个材质节点的标题都会显示节点的名称,也可以在侧栏面板中编辑和复制它。在逻辑节点编辑器中使用材质属性设置节点时,就可以使用这个节点名称。在编程中,`Node` 对应的就是 `MaterialData` 属性名称。
这个参数是一个字符串值,会经由 `UniformsManager` API 的 link 参数传入,用于读写材质
的相应属性数据。所以,材质节点中的 **Node** 这个字符串参数就是 `MaterialData` 的属性名称。如果没有理解这层关系,那么根本无法理解材质属性设置节点的使用。
GLSL 常量管理器中管理三种着色器常量,登记在相应的多级 Map 数据容器内,对象作为一级映射的 Key,材质数据对象 `MaterialData` 作为二级映射的 Key,使用字符串作为第三级映射的 Key。这三种数据对应有三个回调方法,它们由 Iron Uniforms 类型进行回调,以获取相应的材质属性数据:
材质节点 Python 代码片段参考:
另外,还需要掌握 `MeshObject` 对象的组织结构,materials 属性就是材质插槽,就是一个向量列表。向量列表中保存的 `MaterialData` 就是材质的数据模型。`DecalObject` 装饰器对象用于表面修饰,也使用到材质的数据模型,但只有一个材质。前缀 T 开头命名的数据类型定义多数都在 SceneFormat.hx,这是 Iron 的场景文件 .arm 格式定义。也就是说,了解 Iron 对象的层次结构,还必须对 .arm 场景文件格式有一定了解:
以材质颜色、纹理属性设置节点为例,`SetMaterialRgbParamNode`和`SetMaterialImageParamNode` 分别调用,`setVec3Value()` 和 `setTextureValue()` 方法将参数依 Links 映射集合声明的 Object->MaterialData->String顺序传入 `UniformsManager`,由它逐级取得材质参数的属性引用,并且将设置值存放到相应的映射容器中。
比如纹理赋值方法中的 **entry** 就是一个 `Map<String, kha.Image>` 容器,是材质属性映射关系。Iron Uniforms `setObjectConstants()` 方法处理这些纹理数据,下表显示了它们的对应关系:
关键词: