UE4学习笔记:材质系统使用入门
本随笔用于记录作者在学习Unreal Engine 4引擎过程中了解到的基础知识点,作用是方便作者巩固引擎材质系统的基础知识。本随笔内容整理自官方教程视频[1]及官方文档基于物理的材质。
本随笔作者还在学习当中,对UE4引擎的理解和使用还不够透彻,难免在书写或理解上出现差错,若出现类似的问题,欢迎私信或在评论区与我讨论。
- 像素着色器和顶点着色器(Pixel Shader and Vertex Shader)
- HLSL(High Level Shader Language,高级着色器语言)
- 材质中的数据类型和运算
- “基于物理的渲染(Physically Based Rendering)”材质属性
- 材质使用的纹理的基础知识
- 材质表达式(Material Expression)
- 纹理采样(Texture Sample)
- 常量(Constant)
- 纹理坐标(Texture Coordinate)
- 平移(Panner)
- 相乘(Multiply)
- 线性插值(Linear Interpolate,缩写为插值(Lerp))
- 添加(Add)
- 噪点(Noise)
- 幂(Power)
- 组件蒙版(Component Mask)
- 追加(Append Vector)
- 向下取整(Floor,英文直译为“地板”)
- 向上取整(Ceil,英文直译为“装天花板的动作”)
- 小数(Frac)
- 常量偏差比例(Constant Bias Scale)
- 1-x
- 球体蒙版(Sphere Mask)
- Actor位置(Actor Location)
- 绝对世界位置(Absolute World Position,在材质编辑器上下文菜单里显示的是“WorldPosition”)
- 像素深度(Pixel Depth)
- 顶点法向世界场景空间(Vertex Normal World Space,在材质编辑器上下文菜单里显示的是“VertexNormalWS”,缩写“WS”一律为单次“World Space”的缩写)
- 像素法向世界场景空间(Pixel Normal World Space,在材质编辑器上下文菜单里显示的是“Pixel Normal WS”)
- 对象边界(Object Bounds)
- 对象半径(Object Radius)
- 对象面向(Object Orientation)
- 对象位置(Object Position)
- 相机矢量(Camera Vector)
- 反射矢量(Reflection Vector)
- 顶点颜色(Vertex Color)
- 凹凸贴图偏移(Bump Offset)
- 视差遮蔽贴图(Parallax Occlusion Mapping)
- 混合角度纠正法线(Blend Angle Corrected Normals)
- 深度消退(Depth Fade)
- 旋转轴(RotateAboutAxis)
- 材质输入(Material Inputs)
- 材质属性(Material Properties)
- 材质域(Material Domain)
- 材质实例(Material Instance)
- 材质函数(Material Function)
- 材质图层(Material Layer)
- 着色器变体(Shader Permutation)
- 材质参数集合(Material Parameter Collection)
- 高级着色器语言(High Level Shader Language,HLSL)
- 材质工作流的使用方法
- 其他特性
- 材质的性能影响
- 材质经常遇到的问题
- 材质使用的小Tips
- 材质资产的协作流程
像素着色器和顶点着色器(Pixel Shader and Vertex Shader)
“像素着色器(Pixel Shader)”和“顶点着色器(Vertex Shader)”负责计算构成最终画质的顶点和像素,它们是对每个顶点和像素进行一系列运算的程序。这两种着色器通常是由HLSL进行编写的。
HLSL(High Level Shader Language,高级着色器语言)
“高级着色器语言(High Level Shader Language,HLSL)”是UE引擎用于编写着色器而使用的代码语言。
在编写过程中,很多变量都是处于未定义的状态,这些未定义变量就要求需要有外部输入来确定每个变量,再进行一系列的计算之后输出最终结果。
一段HLSL代码如下:
float3 ExampleVariableTexture = Texture2DSample(Tex, Texsample, UV);
float3 ExampleVariableColor = float3(1, 0, 0);
float3 Output;
Output = ExampleVariableTexture * ExampleVariableColor;
return Output;
材质中的数据类型和运算
数据类型
UE4引擎材质系统处理材质表达式的时候,使用的数据类型可以全部都概括为“浮点型(Float)”数值的变体,但是根据材质表达式引脚的不同,使用浮点数值的数量也不同,可以分为Float、Float2、Float3和Float4。
运算
- 材质表达式AppendVector、AppendMany和ComponentMask属于“附加”或“屏蔽”数据通道,例如对两个Float类型的数据使用AppendVector,则AppendVector表达式会返回一个Float2类型的数据,对两个Float2类型数据使用,则会返回一个Float4类型数据;而ComponentMask会对输入的数据类型屏蔽,只输出在ComponentMask表达式细节面板上勾选的通道的数值。
- 材质表达式“加(Add)”、“减(Subtract)”、“乘(Multipoly)”和“除(Divide)”则属于同一类型的数据进行对应通道的运算的操作,例如一个Float2类型的数据只能和另外一个Float2类型的数据进行运算,和其他类型的数据运算的话会发生错误。唯一例外是Float类型的数据,该类型的数据可以和任一类型(Float、Float2、Float3或Float4)进行运算,运算时仅仅需要将Float类型的值与每个通道对应的值进行运算即可。
“基于物理的渲染(Physically Based Rendering)”材质属性
基于物理的渲染(Physically Based Rendering,PBR)基于对光线实际情况的估算而非我们观察到的情况,因此在任何光线模式下都可以完美地工作。
PBR允许你通过调整金属色、高光度和粗糙度属性来估算材质着色及光照与材质的交互效果。
因为引擎里面几乎所有材质都是PBR,因此我们也称PBR为“统一着色(Unifield Shading)”。
基础颜色(Base Color,有时也可称为“漫反射(Diffuse)”)
“基础颜色”定义了材质的总体颜色,基础颜色接受一个Float3类型的数据,并且每个通道都被限制到了0到1的数值。“基础颜色”值越接近0,该材质吸收颜色的能力越强,颜色越接近黑色;该值越接近1,该材质吸收颜色的能力越弱,颜色越接近白色。官方文档里定义了对于某些物体的材质需要使用数值为多少的基础颜色强度。基础颜色是一种没有任何高光或阴影,仅保存着颜色信息的色彩纹理。
自然界中颜色最暗的木炭其基础颜色值为0.02,颜色最亮的雪花其基础颜色值为0.81,不存在纯黑(0)或纯白(1)的物质,因此如果你在项目中使用过白或过黑的基础颜色,会让项目产生曝光问题从而让一些场景过亮或过暗。
粗糙度(Roughness)
粗糙度定义了物体表面是否光滑。使用该输入可以实现在普通材质上添加下过雨后湿润的那种感觉,搭配噪点纹理更能实现土地上小水池塘的效果,以及实现对纹理进行着色的效果。
“粗糙度”值越高,物体表面越粗糙、氧化或非常脏;该值越低,物体表面越光滑。该值的默认值为0.5。
金属感(Metallic)
金属感定义了物体是否看起来像金属。
金属感值越高,物体表面越像一个金属;该值越低,物体越像非金属。
该值的默认值为0。
在使用过程中该值很多情况下就像是一个“布尔值”一样,要么是为1表明该物体是一个金属,要么是为0表明该物体是一个非金属。
高光度(Specular)
高光度定义了物体表面那些地方会存在高光。
高光度值越高,物体的反射能力越强;该值越低,物体的反射能力越弱。
该值的默认值为0.5。
通常情况下我们并不需要使用高光度,除非我们有玻璃、塑料、冰块之类高光非常明显的物体(可能这也解释了为什么Quixel Mixer要把高光纹理单独区分出来的原因)。官方文档里提供了部分物体的高光度应该使用的值。
如果我们的“金属值”已经设置为1的话,那么高光度将不会有任何作用。
通常情况下,“金属值”要么是0(表示物体非金属)要么是1(表示物体为金属),“高光度”不需要我们去进行调整,“基础颜色”就是物体原本的颜色,所以我们大部分时间都在在处理“粗糙度”的数值上。
法线贴图(Normal Texture)
“法线贴图(Normal Texture)”是在网格体上模拟了很多高分辨率的细节,从而让一个低多边形模型可以模拟出在与光照进行作用时候如何展现光照细节。
材质使用的纹理的基础知识
纹理尺寸
- 纹理的长和宽都应该是2的幂次数,例如2*2,1024*1024,8192*8192,UE4引擎目前仅支持最大8192尺寸的纹理。
- 纹理的长和宽可以不一致,如可以是2*8192,也可以是4096*1024,主要是2的幂次数且不超过8192即可。
- 其他数值的纹理依然可以被导进引擎里,但是并不会生成多级渐进纹理。
需要注意的是纹理的分辨率仅仅只会影响内存和带宽,并不会影响渲染性能。
纹理格式
UE引擎支持大多数常见的纹理格式,如bmp、float、jpeg、jpg、pcx、png、psd、tga、(Cubemap or 2D)dds、(HDR)exr、(TIFF)tif等,其中我们需要关注的是psd和tga格式。
PSD格式是Photoshop软件常用的格式,使用这种格式的好处就是不需要将图片转换为其他格式,而是直接导入psd格式文件即可。
TGA格式的纹理也是UE引擎常用的纹理格式,因为TAG纹理在未经过任何压缩的情况下,还支持Alpha通道,因此让其成为了UE引擎使用的首选格式。
一般情况下需要让底色纹理勾选上sRGB选项以在引擎里面更加准确地表现纹理。
导入后纹理的压缩
无论是1GB大小的PSD格式图片还是1MB大小的JPEG格式图片,导入到引擎后都会经过压缩变成同样大小、同样格式的图片。
导入到UE引擎的纹理图片都会被压缩成DXTC或BC格式。
有的时候导入的纹理图片格式是:B8G8R8A8,这就表明该纹理并没有经过压缩,
翻转法线纹理
有时候引擎在导入法线纹理之后发现该纹理表现的法向是错误方向,这个时候就可以在纹理编辑器里面找到选项“翻转绿色通道(Flip Green Channel)”即可将法线的法向给翻转过来。
导入通道遮罩纹理(以及只使用灰度贴图的纹理)
当我们在向引擎导入通道遮罩纹理(也称“合并纹理”,即多个灰度纹理被整合成一个纹理,然后通过不同通道获取不同类型灰度贴图的纹理)需要确保该纹理的压缩设置为“Masks(no sRGB)”,这样的话引擎就不会对该纹理使用颜色纠正,能够使每张灰度纹理能够现实更准确的效果。
纹理流送池(Texture Pool)
纹理流送池(Texture Pool)是虚幻引擎在计算机中为纹理保留一定量的内存空间,这一段内存空间就专门用来储存要用于显示的纹理。
使用命令“r.Steaming.PoolSize X”可以设置引擎的纹理流送池大小,其中X是一个数字,代表着要使用多少MB的内存空间作为纹理池。
多级渐进纹理(Mipmap)
多级渐进纹理(Mipmap)本质上就只是纹理的副本,但是每一级Mipmap纹理分辨率都是上一级的1/4,这样可以更加有效地利用内存空间来流送纹理(因为Mipmap的内存占用比原纹理贴图的要小),并且还能够减少远景处的视觉噪点。
可以在左上角的视口模式里选择“优化视图模式(Optimization Viewmodes)->所需纹理分辨率(Required Texture Resolution)”,之后在视口里选中我们要可视化的Actor,最后在左上角新增的选项里选择我们要浏览的纹理,即可在视口里查看当前纹理的Mipamp。
Mip生成设置(Mip Gen Settings)
一般情况下引擎会自动为纹理设置Mipmap(前提是纹理的长宽尺寸均为2的幂数),但是我们也可以在纹理编辑器里面的“细节层级(Level Of Detal)->Mip生成设置(Mip Gen Settings)”选择引擎生成Mipmap的锐度,例如是要让远处的Mipmap更加“模糊(Blur)”,还是变得更加“锐化(Sharpen)”。该选项一般为“来自纹理组(From Texture Group)”,也就是说该纹理的Mip生成锐度是由“纹理组(Texture Group)”确定的。
(随笔作者注:在这里如果将Mip生成设置选为“锐化10”的话从我个人角度来看可以获得最好的纹理效果,同时也不会产生“NoMipmap”时远处纹理闪烁的问题。)
纹理组(Texture Group)
纹理组(Texture Group)是对纹理进行分类的一种方式,展开该选项即可得到可供用户使用的纹理列表,比如说“世界场景(World)”、“角色(Character)”、“武器(Weapon)”等。默认情况下会将导入的贴图全部设置为“世界场景(World)”或“世界法线贴图(World Normal Map)”。
纹理组的分类可以有效地让用户统一管理纹理资源,例如缩小所有“角色”分组的纹理尺寸但是却保留“世界”分组的纹理尺寸。
默认情况下大部分不同的分组之间没有什么本质的不同,这些分组仅仅只是名字上有区别,但是“UI”是个例外,如果纹理被设置成为了“UI”分组的话,该纹理将不会再生成Mipmap,因为UI会始终显示在玩家的正前方。
纹理组真正的用处是在“窗口(Window)->开发者工具(Developer Tools)->设备描述(Device Profiles)”窗口里,在这个窗口里我们可以针对不同平台的纹理LOD组(Texture LOD Group)进行设置,在这里我们可以设置该平台下的“世界”分组的纹理需要怎样去生成Mipmaps,相当于是去设置每个纹理组的“预设”。
过滤器(Filter)
过滤器(Filter)设置是用来计算当Mip纹理尺寸非常小的时候,纹理细节将以什么样的方式计算出来的。
过滤器设置位于纹理编辑器面板的“纹理(Texture)->过滤器(Filter)”选项,目前的过滤器选项有三种设置:
最近(Nearest)
最近(Nearest)算法过滤器可以为Mip纹理提供最接近于原纹理的细节,该模式的过滤器可以看到每个纹理的每个像素。
双线性(Bi-Linear)和三线性(Tri-Linear)
双线性(Bi-Linear)和三线性(Tri-Linear)过滤器可以提供更加顺滑的Mip纹理,但是因此会让Mip纹理看上去有点“模糊”。
渲染目标(Render Target)
渲染目标(Render Target)本质上是由引擎或编辑器生成的纹理,这些纹理可以由烘焙材质、烘焙镜头视图或一些其他方式来生成。此类纹理用于镜头、复杂材质等一些高级效果。也可以在引擎或编辑器里直接创建纹理,由引擎或编辑器创建的纹理也被称为渲染目标。
虽然编辑器可以通过设置参数来生成对应的材质效果,例如后续提到的材质表达式“噪点(Noise)”,但是这种方法会极大地增加材质的性能损耗,所以我们一般使用基于纹理的材质,也正因如此,作为可以通过引擎或编辑器实时生成的纹理——渲染目标,可以很好地实现一些基于纹理的材质的高级表达方式。
例如可以通过“场景捕获2D(Scene Capture 2D)”的Actor在场景里面将该Actor捕捉到的视图生成到渲染目标上,然后在材质里使用这个渲染目标,就可以实现“监控摄像机”或“镜子”这类对反射精度比较高的物体的效果,调整渲染目标的尺寸就可以提升纹理的精度。
渲染目标除了能够用上述的方式生成纹理外还可以通过蓝图节点“绘制材质到渲染目标(Draw Material To Render Target)”将材质烘焙为贴图。
材质表达式(Material Expression)
材质表达式(Material Expression)本质上就是材质编辑器里面一些可以连接的节点,这些节点构成了在材质编辑器里开展大部分工作时使用的可视化网格。大部分工作都可以通过少部分材质表达式来实现。
需要注意的是不同材质表达式会使用的“指令(Instructions)”数量不同,有些表达式会产生很多的指令,从而增加材质的性能损耗,因此使用材质表达式的时候需要谨慎使用。
常见的材质表达式如下:
纹理采样(Texture Sample)
纹理采样(Texture Sample)保存着在材质中要使用到的纹理。
可以通过在材质编辑器里面按住按键T
然后在编辑器图标空白处点击鼠标左键来快速调出该表达式。
一个材质当中最多支持16个纹理采样器,但是因为其中一些采样器需要供内部使用(例如光照贴图、阴影贴图等),因此一个材质中能够使用的纹理采样器的数量是13个。
采样源(Sample Source)
如果想要突破UE引擎材质只能够使用13个的数量,那么我们可以将纹理采样节点的属性“采样源(Sample Source)”设置为“共享包装(Shared:Wrap)”或“共享限制(Shared:Clamp)”即可,如果不知道这两个的区别那就选共享包装。使用这种方法可以让材质使用的纹理采样节点多达128个。
常量(Constant)
常量(Constant)包括表示单个维度的常量(Constant)、表示两个维度的二维常量(Constant2Vector)、表示三个维度的三维常量(Constant3Vector)和表示四个维度的四维常量(Constant4Vector)。
常量表达式中最常用的表达式是“一维常量”和“三维常量”。
可以通过在材质编辑器里面分别按住按键1
、2
、3
、4
然后在编辑器图标空白处点击鼠标左键来快速调出对应的表达式。
纹理坐标(Texture Coordinate)
纹理坐标(Texture Coordinate)表达式用来控制该材质的UV坐标位置相关的属性。
可以通过在材质编辑器里面按住按键U
然后在编辑器图标空白处点击鼠标左键来快速调出该表达式。
材质顶点的UV分量会被纹理坐标表达式的属性中“U平铺(U Tilling)”和“V平铺(V Tilling)”的值相乘缩放。
参数“坐标索引(Coordinate Index)”描述了我们要缩放的模型的UV集中具体是哪个下标的UV,一般来说该参数的值默认为0即可。
平移(Panner)
平移(Panner)表达式可以用来水平移动或竖直移动(水平和竖直同时移动也可以)纹理。
可以通过在材质编辑器里面按住按键P
然后在编辑器图表空白处点击鼠标左键来快速调出该表达式。
相乘(Multiply)
相乘(Multiply)表达式最主要的功能是用于将两张纹理的内容进行相乘。
如果让一个一维常量与一张纹理进行相乘的话就可以实现控制纹理饱和度的功能。
可以通过在材质编辑器里面按住按键M
然后在编辑器图表空白处点击鼠标左键来快速调出该表达式。
法线纹理相乘
需要注意的是,如果要对法线纹理进行相乘的话,最好使用“BlendAngleCorrectedNormals”材质表达式来替代“Multiply”表达式。
线性插值(Linear Interpolate,缩写为插值(Lerp))
插值(Lerp)可以实现根据“Alpha”输入值对“A”输入值到“B”输入值之间进行一个过度的操作。当“Alpha”值为0时,该表达式输出值为“A”的输入值;当该值为1时,该表达式输出值为“B”的输入值。
使用插值表达式搭配一个灰度纹理图,可以实现通过修改表达式的“输入A”和“输入B”的值就可以更改原本灰度纹理图表达的值的范围。还可以使用该节点搭配上粗糙度贴图或其他灰度贴图来实现对材质上部分区域进行着色的效果,例如实现绿色草地上有部分区域的草显示为黄色的效果来丰富材质。
可以通过在材质编辑器里面按住按键L
然后在编辑器图表空白处点击鼠标左键来快速调出该表达式。
添加(Add)
“添加(Add)”表达式可以将两个输入的值相加并输出相加后的值,一般来说该表达式的作用是用来增减一张纹理(通常是底色纹理)的亮度,纹理加上一个正值就可以让该纹理表现得更加亮(即更加靠近白色);加上一个负值可以让该纹理表现的更加暗(即更加靠近黑色)。
使用该表达式让一张底色纹理与一张噪点纹理相加的话,可以实现类似稀疏云层的效果。
可以通过在材质编辑器里面按住快捷键A
然后在编辑器图表空白处点击鼠标左键来快速调出该表达式。
噪点(Noise)
“噪点(Noise)”表达式提供了程序化的噪点纹理。
但是一般情况下最好不要使用该表达式,因为该表达式会产生近乎100条指令,增大材质的性能损耗,因此可以转为使用噪点纹理来代替噪点表达式。
幂(Power)
“幂(Power)”表达式提供了一种快捷地“自乘”方法,使用幂表达式可以很方便地修改纹理的对比度。
组件蒙版(Component Mask)
“组件蒙版(Component Mask)”表达式又被称为“蒙版(Maske)”,作用是可以把输入的数据类型的指定通道屏蔽掉,可以屏蔽R、G、B或A任意通道甚至全部。
使用“纹理坐标”表达式和该表达式进行组合,并且仅启用“R”或“G”通道,即可实时生成一张渐变纹理,并且这种方法仅仅只有很小的性能损耗。
追加(Append Vector)
“追加(Append Vector)”表达式的作用和“蒙版”表达式的作用正好相反,追加表达式可以为输入的数据类型添加上一个新的通道。
向下取整(Floor,英文直译为“地板”)
“向下取整(Floor)”表达式会将输入的数据类型里浮点数舍弃小数部分变成整型。
向上取整(Ceil,英文直译为“装天花板的动作”)
“向上取整(Ceil)”表达式会将输入的数据类型里含浮点数的部分舍弃并将剩下的整数部分进1。
小数(Frac)
“小数(Frac)”表达式会省略掉数据类型里面的整型数值部分,仅仅只保留小数的部分。
常量偏差比例(Constant Bias Scale)
“常量偏差比例(Constant Bias Scale)”用于处理需要在[0,1]区间值之间使用的值(以取代[-1,1]区间值,因为材质对于负数的处理不是很好)。
该表达式在细节面板里面有两个参数:偏差(Bias)和比例(Scale),该表达式会将输入的值添加上偏差参数的值,然后在乘以比例值,再将结果输出。
使用该表达式可以实现诸如呼吸灯之类的特效。
1-x
“1-x”表达式的作用就和其名字一样,会将输入的数据类型用1去进行相减。
一般来说这个表达式的所用是为了获取到输入数据类型的“相反结果”。
球体蒙版(Sphere Mask)
“球体蒙版(Sphere Mask)”表达式可以用于生成实时的球体蒙版纹理。
其中输入引脚“A”一般都是需要连接到“纹理坐标”表达式上,然后输入引脚“B”指定了球体中心点位于纹理坐标中的UV坐标中哪个位置上,输入引脚“半径(Radius)”指定了球体的半径,输入引脚“硬度(Hardness)”指定了球体边缘渐变的硬度,该值的单位是“百分比”,因此当该值为0时球体边缘最软;为100时球体边缘最硬。
Actor位置(Actor Location)
“Actor位置(Actor Location)”表达式可以获取到被应用材质的Actor的世界坐标位置。
绝对世界位置(Absolute World Position,在材质编辑器上下文菜单里显示的是“WorldPosition”)
“绝对世界位置(Absolute World Position)”表达式获取到的是像素在世界场景中显示的实际位置。
像素深度(Pixel Depth)
“像素深度(Pixel Depth)”表达式获取到的是像素在世界场景中与镜头所在位置之间的距离,该表达式主要用来测试距离。
顶点法向世界场景空间(Vertex Normal World Space,在材质编辑器上下文菜单里显示的是“VertexNormalWS”,缩写“WS”一律为单次“World Space”的缩写)
“顶点法线世界场景空间(Vertex Normal World Space)”表达式可以获取到模型顶点的法线。
像素法向世界场景空间(Pixel Normal World Space,在材质编辑器上下文菜单里显示的是“Pixel Normal WS”)
“像素法线世界场景空间(Pixel Normal World Space)”表达式获取到的是像素的法线。
对象边界(Object Bounds)
“对象边界(Object Bounds)”表达式获取到的是被应用材质的对象的边界框。
对象半径(Object Radius)
“对象半径(Object Radius)”表达式获取到的是被应用材质对象球形边界框的半径。
对象面向(Object Orientation)
“对象面向(Object Orientation)”表达式获取到的是对象局部控件中Z轴在世界空间坐标中的方向。
对象位置(Object Position)
“对象位置(Object Position)”表达式获取到的是对象边界位置中心点在世界坐标中的位置。
相机矢量(Camera Vector)
“相机矢量(Camera Vector)”表达式获取到的是由像素指向摄像机位置的方向向量。
反射矢量(Reflection Vector)
“反射矢量(Reflection Vector)”表达式获取到的是摄像机指向像素发生反射之后反射的方向向量。
顶点颜色(Vertex Color)
“顶点颜色(Vertex Color)”表达式可以获取到被应用模型所有顶点的三通道颜色,该表达式可以配合UE引擎的“绘制模式(Paint Mode)”来实现利用笔刷绘制模型材质的效果。
凹凸贴图偏移(Bump Offset)
“凹凸贴图偏移(Bumo Offset)”表达式会尝试通过偏移部分像素来模拟视差类型的效果。该表达式可以实现卡牌游戏中3D卡面——当视角以卡面为中心进行移动的时候,可以看到卡面更深处的地方就像平面的卡面增加了视觉深度——这样的效果。
视差遮蔽贴图(Parallax Occlusion Mapping)
“视差遮蔽贴图(ParallaxOcclusionMapping)”表达式本质上是一个材质函数,通过输入高度图及一些其他参数,可以生成视差遮蔽贴图相关的纹理并应用到材质输入当中,从而实现视差效果。
混合角度纠正法线(Blend Angle Corrected Normals)
“混合角度纠正法线(Blend Angle Corrected Normals)”表达式作用上就相当于是对法线贴图进行“相乘(Multiply)”的操作,因为法线贴图的特殊性所以直接的相乘会对法线贴图造成错误的结果,如果需要对法线贴图进行相乘操作的时候使用该节点可以避免得出错误的纹理。
修改法线纹理的强度
要想修改法线纹理的强度只需要将法线纹理乘以“FVector3(x,y,1)”类型的数据类型即可,其中R通道的x和G通道的y可以表示任何值,但是B通道的值必须为1,换句话说,法线纹理通过“相乘(multiply)”表达式乘以一个B通道值为1的三维向量,得到的结果可以修改原法线纹理的强度。
深度消退(Depth Fade)
“深度消退(Depth Fade)”表达式主要用于半透明表面且只对半透明表明有效,深度消退表达式支持检测半透明表面与几何体相交的位置,然后根据相交位置淡出,从而实现柔和的边缘效果。(待补充.....................................................................)
旋转轴(RotateAboutAxis)
“旋转轴(RotateAboutAxis)”表达式可以让模型沿着指定的轴进行旋转,这种旋转仅仅只是“材质表达”上的旋转,并不会更改模型在世界场景里面的位置。
该表达式还可以实现让玩家走过一片草地的时候,让草地向着玩家走过的路径的反方向弯曲,形成一种玩家推开了草地的感觉。
一般来说要想让物体旋转可以通过蓝图或时间轴的方式来编写旋转逻辑,但是这种方法将会由CPU来计算旋转需要的值从而增加CPU的负担,如果用“旋转轴”表达式则将这种负担转移到了GPU上,对于一些想要旋转视效但是又不需要产生实际物理碰撞的需求来说用该节点会比直接旋转物体更加节省性能。
材质输入(Material Inputs)
材质输入(Material Inputs)决定了材质最终的表现会是什么样的。材质输入节点一般位于编辑器图表最靠右边的位置。
材质输入引脚的启用和关闭收到后台最终使用的材质模板的影响,也就是说不同材质模板使用的材质输入引脚也不同。
材质输入节点的输入节点是否启用依赖材质属性的设置,一般来说像是“底色(Base Color)”、“金属感(Metallic)”、“粗糙度(Roughness)”、“高光度(Specular)”、“自发光(Emissive Color)”和“世界位置偏移(World Position Offset)”等输入引脚是启用的,前几个输入在上一小节已经介绍过,本小节主要介绍剩下的几个输入。
自发光(Emissive Color)
自发光(Emissive Color)意味着这材质本身不受光照的影响,而是取决于自发光颜色得到的输入参数。
使用自发光输入引脚可以实现让物体本身发出光照,像灯泡、太阳这类物体一样。
世界位置偏移(World Position Offset)
世界位置偏移(World Position Offset)本质上是使用到了引擎的顶点着色器,并使用顶点着色器来偏移模型,实际上就是偏移材质所在的模型。
使用世界位置偏移可以实现诸如被风吹的窗帘等动态效果。如果出现位置偏移太过头(比如说窗帘与物体连接的地方是不会发生位移的)的情况,可以通过将世界位置偏移结果与物体的顶点颜色进行相乘,这样就可以计算出来物体的哪些地方应该偏移,而哪些地方不应该发生偏移。
如果我们拥有材质的高度图的话,使用高度图搭配“相乘”表达式修改高度图强度,然后再与“顶点法向(Vertex Normal WS)”表达式进行“相乘”,最后将结果输入到“世界位置偏移”输入上,就可以实现让平面材质产生凹凸不平的视觉效果。
需要注意的是,模型的顶点越多,那么使用世界位置偏移表达式实现的凹凸效果越好,也就是说如果模型顶点很少的话,那么使用该节点实现的凹凸效果就会较差。
世界置换(World Displacement)
(该输入需要在材质的细节面板里启用“曲面细分(Tessellation)”选项才能使用)
“世界置换(World Displacement)”输入会将网格体和模型加以细分,然后使用纹理将其置换,就能获得有凹凸感的材质。
在使用高度纹理并进行“相乘”操作之后,还必须对结果再进行一次与顶点法线方向(Vertex Normal WS)进行相乘的操作,如果没有这一步操作的话最后的材质效果会向同一个方向进行置换从而产生错误的效果。
曲面细分乘数(Tessellation Multiplier)
(该输入需要在材质的细节面板里启用“曲面细分(Tessellation)”选项才能使用)
“曲面细分乘数(Tessellation Multiplier)”输入用于确定当模型需要曲面细分的时候,实际上需要进行细分的“倍数”,该输入值越高,那么曲面细分的程度就越大。该输入可以搭配“世界置换”输入来一同使用。
该输入会对原模型添加额外的三角形面来实现细分的功能,因此需要注意该材质输入对画面性能的影响。
该输入存在最大值,经过测试该最大值为15。需要注意的是该值是在没有勾选“自适应曲面细分(Adaptive Tessellation)”的情况下测试得出的,如果勾选了“自适应曲面细分”选项的话可能该最大值会更小。
环境光遮蔽(Ambient Occlusion)
“环境光遮蔽(Ambient Occlusion)”输入用于在模型上通过纹理实现环境光遮蔽的效果,
需要注意的是由于GBuffer等原因,这个效果很多时候只能够在“静态光照模型”上产生效果。因此如果我们将环境光遮蔽纹理直接应用到环境光遮蔽输入上但是没有效果的时候,那么我们就需要注意我们当前的模型是否属于静态模型并接受了静态光照。
因为上述限制的原因,因此很多UE引擎制作的游戏实际上并没有使用到“环境光遮蔽”功能。
折射(Refraction)
(该输入需要当材质的混合模式为“半透明(Translucent)”或当着色模型为“单层水面(Single Layer Water)”时才会被启用)
“折射(Refraction)”输入允许定义半透明模型如何反射背后的物体。该输入的默认值为“1”,即没有任何折射效果。
“折射”特性最大的用处就是用来实现水面折射的效果,以及某种涟漪状的扭曲效果。
官方视频里建议使用“菲涅尔(Fresnel)”表达式获取到模型靠边界的位置,然后让靠边界位置的折射值为0.8,让靠内部位置的折射值为1,从而实现一个比较好的折射效果。
“折射”输入搭配“法线”输入可以实现更加复杂也更加美观的折射效果,使用了法线贴图的折射材质可以在法线贴图指定的地方也发生折射,而并不一定是在边缘上。
官方视频里给出了现实中常见物质的折射率:
空气(Air):1.00
水(Water):1.33
冰(Ice):1.31
玻璃(Glass):1.52
钻石(Diamond):2.42
折射输入用于平面模型来模拟水面材质效果的时候,有时候会出现折射的图形整体往下移动的错误,这个时候需要找到材质属性里的“折射(Refraction)”分类,并把“折射模式(Refraction Mode)”从“折射率(Index Of Refraction)”更改为“像素法线偏移(Pixel Normal Offset)”,可能会解决折射错误的问题,也有可能更遭,因此需要我们多去尝试各种各样的组合来确定最终效果。
像素深度偏移(Pixel Depth Offset)
“像素深度偏移(Pixel Depth Offset)”输入用于控制实际渲染中像素偏移的距离。如果搭配上灰度纹理和一个常量使用的话就是会将灰度纹理中白色的部分进行常量个距离的偏移。
“像素深度偏移”具体指的是当我们在渲染一个像素的时候,如果比该像素深上述指定常量值距离时存在着其他像素,那么被偏移的像素在其他像素更深位置上被渲染,表现上将不会看到被偏移的像素。
该项功能大多数是用于毛发或者植物上。例如角色的头发在前额上并不是像一个直线一样分布的,而是在直线的两端进行分布,使用像素深度偏移功能就能让头发的“根”看起来并不是并排并列而是有前后的差别,这样会让毛发显得更真实一些。
像素深度偏移也可以用来实现一个模型和一个模型发生重叠之后接缝处生硬的问题。详见小Tip。像素深度偏移输入特别适合与置换贴图、视差贴图和世界位置偏移功能来实现凸起物体与周边环境重叠时让边缘接缝更好地融合。
材质属性(Material Properties)
材质编辑器里我们最常使用的材质属性分别是“着色模型(Shading Model)”、“混合模式(Blend Modes)”、“双面材质(Two-Side Material)”和“半透明设置(Translucency Settings)”。
每次我们更新这些材质属性的时候都会在后台应用不同的材质着色器模板,从而更新材质输入的输入引脚。
着色模型(Shading Model)
“着色模型(Shading Model)”定义了材质输入最终将会被如何混合从而形成最终的材质。
该属性默认设置为“默认光照(Default Lit)”,将该属性设置为“无光照(Unlit)”模式之后,材质输入的输入引脚就会发生变化。使用该模式可以用来创建魔法效果、火焰等不会与光照发生作用且需要自身发出光照的材质效果。
默认光照(Default Lit)
“默认光照(Default Lit)”着色模型表明该材质会受到场景光照的影响。
无光照(Unlit)
“无光照(Unlit)”着色模型表明该材质不会受到场景光照的影响。如果我们需要仅仅做“自发光”的物体,那么其材质我们可以使用该着色模型来减少性能损耗。
次表面(Subsurface)
“次表面(Subsurface)”着色模型会启用“次表面颜色(Subsurface Color)”和“不透明度(Opacity)”材质输入来实现类似人类皮肤、蜡烛等这类表面颜色下方透露出另外一种次表面颜色的效果。
“不透明度(Opacity)”材质输入定义了允许材质穿透表面的光照量,当不透明度值为0时表示表面颜色几乎透明,光照将全部照亮次表面颜色;值为1时表示表面颜色几乎不透明,光照大部分将照亮表面颜色,而只有少数光照会照亮次表面颜色。
该着色模型仅仅适用于“固定光照”和“可移动光照”,对“静态光照”没有效果。(需要测试该结论是否正确)
次表面配置(Subsurface Profile)
“次表面配置(Subsurface Profile)”着色模型和“次表面”着色模型有着差不多的功能,区别在于“次表面配置”将不使用“次表面颜色”材质输入而是替换成了材质属性面板里面的“次表面配置”资产,该资产可以在项目的资源管理器里创建并使用,次表面配置资产可以设置更加丰富的次表面效果,使用“次表面配置”着色模型实现的皮肤效果也比“次表面”着色模型的要优秀,同时也有着更高的性能开销。
需要注意的是“次表面配置”材质输入是模拟次表面散射,通过把计算得出的效果应用到表面上并加以模糊实现的,而并不像“次表面”材质输入那样实际生成了次表面的效果。“次表面配置”实际上执行的操作是给被应用模型拍下一张照片,然后按照次表面配置资产里面的“散射半径(Scatter Radius)”参数进行偏移使其看上去有点模糊从而虚拟次表面散射。
双面植被(Two Side Foliages)
“双面植被(Two Side Foliages)”允许当一个植物的单面被光源照到的时候,这个植物的另外一面也会被照亮,用于模拟光照透过了植物的叶片。
多年来在游戏开发过程中制作树木或类似的植被过程中都会遇到这样的问题:这类模型基本上都是由许多不同的面片组成的,也就是说树木上的树叶全部都只是一个平面,然后这些平面会彼此相交且朝向、角度、大小都不同,因此就无法生成看上去统一的光照,“双面植被”材质输入就是为了解决这种问题而产生的。
“双面植被”着色模型和“次表面”一样,会启用“不透明度”和“次表面颜色”材质输入。
透明涂层(Clear Coat)
“透明涂层(Clear Coat)”着色模型可以通过第二组法线和第二组粗糙度以供使用,来实现额外的一套模型表面,但是该表面模型像是被镶嵌在了一层平滑透明塑料层里的效果,例如可以在一个切片模型上面实现每个切片都有额外凹凸感的效果。
“透明图层”着色模型会启用“透明涂层(Clear Coat)”材质输入和“透明涂层粗糙度(Clear Coat Roughness)”材质输入,其中“透明涂层”表明了要应用的透明图层的程度,值越大则越明显,“透明涂层粗糙度”则表明透明图层要使用的粗糙度的值,此外“透明涂层”着色模型还需要搭配一个材质输入“透明涂层底部法线(Clear Coat Bottom Normal)”来使用,该输入接受一张法线纹理,用来表明要在透明涂层上显示的图案。
“透明涂层”着色模型很适合用来实现有透明塑料壳包装的产品,或者是车身的效果。
预整合皮肤(Pre-integrated Skin,待补充)
“预整合皮肤(Pre-integrated Skin)”着色模型同“次表面”着色模型类似,适用于实现人类皮肤的效果,尽管在物理效果上并不完美,但是其性能开销比“次表面”着色模型要低。
头发(Hair,待补充)
“头发(Hair)”着色模型用于创建效果自然的毛发,模拟多种高光:一种代表光线的颜色,另外一种代表毛发和光线的混合颜色。
衣服(Cloth,待补充)
“衣服(Cloth)”着色模型创建模仿布料效果最佳的材质,其中包括布料材质的“绒毛”薄层,模拟光线与这类材质的交互。
眼睛(Eye,待补充)
“眼睛(Eye)”着色模型用于模拟眼睛的表面,从而对眼睛的每个生物构成部分进行艺术效果控制。这是一种技术性很高的着色模型,在着色器代码、材质、几何形状及其UV布局之间存在非常强的依赖性,因此Epic官方建议如果用户没有丰富的着色器开发经验,则不应自行构建眼睛的材质,而是从Epic启动器里下载数字人类(Metahuman)项目并提取眼球的几何体并按照原样使用眼球的材质,替换材质的纹理来满足用户的使用要求。
皮肤/头发/衣服/眼睛着色模型的综合示例
官方视频提供了使用这四种着色模型的示例。)
单层水面(Single Layer Water,待补充)
“单层水面(Single Layer Water)”着色模型可以在“不透明(Opaque)混合模式”中实现透明水面的效果,这样可以降低通常使用“透明(Transparent)”混合模式材质的开销和复杂性。
薄半透明(Thin Translucent,待补充)
“薄半透明(Thin Translucent)”着色模型支持基于物理原理的半透明材质类型,可以通过该着色模型创建能够准确处理高光度和背景对象的真实有色或彩色玻璃
来自材质表达式(From Material Expression,待补充)
“来自材质表达式(From Material Expression)”着色模型是一种高级功能,允许通过材质图表中的逻辑将多个着色模型合并到单个材质(或材质实例)中。
混合模式(Blend Modes)
混合模式(BlendModes)定义了材质的最终效果将如何与背景的颜色进行混合。
不透明(Opaque)
默认使用的混合模式,不会存在透明通道(即Alpha通道)。
蒙版(Masked)
在不透明模式的基础上添加了透明通道,可以实现透明材质的效果。用一张带Alpha草地的纹理就可以实现草地的效果。
蒙版混合模式会开启材质输入的“透明度蒙版(Opacity Mask)”引脚,只需要当纹理的Alpha通道连到该材质输入即可实现蒙版的效果。
需要注意的是,蒙版混合模式不支持颜色的过渡,因此并不会像半透明那样处于可见和不可见的范围,就只有要么可见要么不可见两种状态,因此开销与“半透明(Translucent)”混合模式相比要低得多,但是纹理中不可见的部分依然在渲染,并且会比可见部分的开销高,因为不可见部分是以一种看不见的透明类型来进行渲染的。
严格上来说该模式实现的不应该是“透明材质”而是“蒙版材质”。
颤动不透明度(Dither Translucent)
虽然蒙版混合模式不支持透明度的过渡,要么就是可见,要么就是不可见,但是如果我们为“透明度蒙版”材质输入连接“颤动临时抗锯齿(Dither Temporary AA)”表达式就可以实现透明效果,虽然这种透明的效果并不是很好,但是其开销会比半透明混合模式要低,而且还支持PBR的材质输入。
“颤动临时抗锯齿”表达式只是把模型表面细分为大量的小型像素,让一部分像素可见,然后让紧邻的像素不可见,用这种一部分可见一部分不可见的方法而实现半透明的效果。
半透明(Translucent)
“半透明模式”相对于“蒙版模式”来说,最主要的特性就是支持“半透明”的材质效果而不是蒙版类型的材质效果,因此支持材质在可见和不可见范围之间进行过渡。
半透明混合模式会开启材质输入的“不透明度(Opacity)”和“反射(Refraction)”引脚。
因为支持透明程度的过渡,因此半透明模式的混合模式性能损耗特别高。
仅仅使用“底色”和“不透明度”设置出来的材质虽然实现了半透明的材质,但是应用到模型上的话看不到任何反光会显得很奇怪,这时候可以通过修改细节面板上的“半透明(Translucency)”->“光照模式(Lighting Mode)”从性能损耗最低的“体积无方向(Volumetric NonDirection)”改为性能损耗最高表现效果也最好的“表面正向着色(Surface Forward Shading)”。
在半透明混合模式下材质输入的大部分引脚都会被关闭,但是通过更改“光照模式(Lighting Mode)”(该选项会在混合模式为半透明模式时开启)可以开启部分被关闭的引脚,从而实现更多材质效果,也因此会产生更多的性能损耗。
半透明光照(Translucent Lit)
“半透明光照”指的是选择了“半透明混合模式”和“默认光照着色模型”的材质,这种材质是最基础的半透明效果材质,可以通过“基础颜色”材质输入来定义材质的表面颜色,然后通过“ 不透明度”材质输入确定材质的透明程度。
半透明无光照(Translucent Unlit)
“半透明无光照”指的是选择了“半透明混合模式”和“无光照着色模型”的材质,这种材质在透明效果上和“半透明光照”是近乎一致的,并且因为不接受光照,因此性能损耗会比“半透明光照”类型的材质低,但是缺点就是无法使用“基础颜色”材质输入。
半透明高级光照(Translucent Advanced Lit)
“半透明高级光照”指的是选择了“半透明混合模式”和“默认光照着色模型”,并且“光照模式(Lighting Mode)”选择了“表面前向渲染(Surface ForwardShading)”,最后还开启了“屏幕空间反射(Screen Space Reflections)”的材质,该类型的材质提供了最高效果的透明材质,并且支持基础的材质输入和半透明材质所需的材质输入。该类型的半透明效果也有着最高的性能损耗。
半透明设置(Translucent Settings)
“半透明设置”指当材质的混合模式设置成了“半透明”模式之后,在材质的细节面板里面属于“半透明”分类的属性将会被启用,这些关于半透明的参数被称为“半透明设置”。
“应用雾化(Apply Fogging)”设置半透明材质是否会受到“远距离雾化”的影响。
“按像素计算雾化(Compute Fog Per Pixel)”设置雾化是按照模型顶点来计算还是按照像素来计算。当我们有一个非常大的透明平面的时候,如果没有启用这个选项,也就是按照顶点来计算雾化的话,那么我们很有可能看到构成透明平面的三角形。
“于景深后渲染(Render After DOF)”设置用于解决当透明平面存在景深效果,但是在不该模糊的时候模糊或是在该模糊的时候没有模糊时的问题。
“禁用深度测试(Disable Depth Test)”设置可以让被应用材质的模型不去关心渲染时的深度,也就是说可以让模型即便被其它模型挡住的时候,该半透明模型依然渲染。这个特性适合用于创建一些始终显示的图标或对象、以及一些在远方始终渲染的标记。
光照模式(Lighting Mode)
体积非定向(Volumetric NonDirectional)
“体积非定向(Volumetric NonDirectional)”光照模式是默认的半透明光照模式,当半透明物体被照亮的时候实际上在内部将物体的顶点进行了细分,然后将光源颜色根据顶点进行混合和过渡。
我们可以通过Console命令来控制引擎为顶点细分的程度,该控制台命令为“r.TranslucencyLightingVolumeDim x”,其中x代表我们要进行细分的维度,该值越高的话,物体顶点细分得越多,光源在物体表面上移动时的渐变也更顺滑。
体积定向(Volumetric Directional)
“体积定向(Volumetric Directional)”光照模式和非定向版本功能差不多,但是该模式支持法线纹理。
体积逐顶点非定向(Volumetric PerVertex NonDirectional)
“体积逐顶点非定向(Volumetric PerVertex NonDirectional)”光照模式和“体积非定向”光照模式差不多,但是在该光照模式中被照亮的半透明物体是以原本的顶点来计算光照光源的,也就是说会按照顶点的位置与光照发生交互,这样当光照移动的时候,被照亮的半透明物体很可能不会及时被光照照亮的部分。
体积逐顶点定向(Volumetric PerVertex Directional)
“体积逐顶点定向(Volumetric PerVertex Directional)”光照模式和非定向版本差不多,但是该模式支持法线纹理。
叠加(Additive)
“叠加(Additive)”混合模式会将材质的像素颜色与背景的像素颜色执行一个“相加”的操作。“叠加”混合模式可以用于实现火焰、蒸汽等类似的效果。
调制(Modulate)
**“调制(Modulate)”混合模式会将材质的像素颜色与背景的像素颜色执行一个“相乘”的操作。“调制”混合模式可以用于实现泥土、污渍或爆炸痕迹等类似的效果。
材质域(Material Domain)
“材质域(Material Domain)”属性用于控制材质的整体使用场景,例如将该材质用于模型的表面、后期处理、UI或光照等不同的场景。
“表面(Surface)”材质域是材质系统的默认材质域,也是在开发过程中99%会使用到的材质域。
“延迟贴花(Deferred Decal)”材质域的材质主要用于“贴花Actor(Decal Actor)”,用于在其他物体表面添加一层新的材质。该材质域会在材质属性中启用“贴花混合模式(Decal Blend Mode)”属性,确定材质将如何在GBuffer里面使用贴花。
“光照函数(Light Functions)”材质域将使光照(点光照、聚光源等)与材质进行混合,然后再投射到场景里。光照函数材质域的材质最有用的一个做法就是模拟光线照射到泳池上时水面反射出来水面涟漪的焦散效果。如果将光照函数与“定向光源”结合使用的话还能模拟云朵投影到地面上的效果。
“后期处理(Post Process)”材质域是专门用于“后期处理体积(Post Process Volume)”的材质,后期处理材质域类型的材质可以用来改变屏幕的渲染方式。
搭配“场景纹理(SceneTexture)”表达式,“后期处理”材质域可以实现很多特别有意思的效果,例如显示被物体遮挡的玩家、实现卡通渲染、水下效果等渲染相关的功能。“场景纹理”表达式可以获取到当前画面帧中GBuffer某一类型的画面(如当前帧的粗糙度画面或金属度画面)。
“用户界面(User Interface)”材质域是专门用于UI控件的。例如UI的“图片(Image)”控件可以在其属性上为Brush添加一个UI材质,从而在UI上展现材质的内容。
“体积(Volume)”材质域可以让材质的内容以一个“体积”的状态在场景里呈现出来。项目中可以让体积材质域的材质搭配上粒子系统实现烟雾的功能。
材质实例(Material Instance)
“材质实例(Material Instance)”是对现有材质进行修改过后的版本,这个新的版本将继承父级材质版本的功能。材质实例仅仅只能更改主材质中被指定可以更改的属性。
材质实例一方面对性能损耗有缓解的作用,另外一方面是对团队合作有作用,这样的话就可以让一个用户处理主材质,然后其他用户处理材质实例,实现明确的分工合作。
材质实例也给实时运行项目提供了修改材质部分属性的机会,当我们使用节点“Create Dynamic Material Instance”创建了动态材质实例后,可以分别使用“Set Scalar Parameter Value”、“Set Vector Parameter Value”和“Set Texture Parameter Value”节点来设置动态材质实例的标量参数、向量参数和纹理参数。
材质实例对工作流的益处是可以节省制作某一类型材质的时间,例如我们通常会制定多个类型的主材质(一个岩石的主材质,一个植物的主材质,一个玻璃的主材质等等),然后再为每个主材质生成多个材质实例并实现不同的子类型(如为一个岩石主材质生成一个大理石材质实例,一个花岗岩材质实例等等)。
必要时候也可以生成“材质实例的材质实例”,例如一个大理石材质实例可以生成一个光滑大理石材质实例和一个粗糙大理石材质实例,这样当我们只需要修改大理石颜色的时候,我们不需要一个个打开光滑大理石材质实例和粗糙大理石材质实例去更改,而只需要在大理石材质实例里面更改即可。
材质函数(Material Function)
“材质函数(Material Function)”就是将材质表达式或其他的材质函数组合起来的一种方式,组合后的材质函数可以供材质来直接调用,从而省去了复制粘贴的麻烦。
类似代码函数一样,在材质函数图标里面可以通过调用表达式“函数输入(Function Input)”和“函数输出(Function Output)”来实现该材质支持多个输入和多个输出的功能。
材质函数细节面板里的属性“使用预览值作为默认值(Use Preview Value as Default)”可以让材质函数不需要任何输入而使用材质函数定义的值。
需要注意的是如何某个函数被用于上百个材质,这个时候我们更改这个材质并应用之后,材质这上百个材质都需要重新编译,因此材质函数的迭代并不快。在官方视频里视频作者就建议如果有一组表达式是我们需要经常更改的话,那这一组表达式就不应该被设置成函数,反之如果这组表达式一经编写完成之后就几乎不会改变,那么这一组表达式就应该被设置为函数。
材质函数实例(Material Function Instance)
“材质函数实例(Material Function Instance”作用和材质实例差不多,依然是为了让用户能够在不改变材质函数的情况下修改材质函数。
材质图层(Material Layer)
“材质图层(Material Layer)”本质上是一个特殊的材质函数,属于材质函数的变体,但是材质图层并不应该只用来实现一个材质的部分功能,而是使用一个材质图层就应该实现一个完整的材质,在这个函数里面不需要去考虑模型哪些部分需要什么纹理,另外一些部分需要什么纹理,而是直接处理每种纹理对应的表现,然后在主材质里面通过“归并贴图(Merge Map)”来区分出模型的区域,再用表达式“BlendMaterialAttributes”融合两个材质图层并把归并贴图对应的通道指定到该表达式的Alpha通道上,即可实现在一个材质里面对模型上不同区域附加上对应材质的功能。
使用材质图层就可以完善材质工作流。例如,由个人在材质图层(也就是材质函数)里完善一种类型(草地、木质或岩质等)纹理的内容,然后在让一个用户专门编写主材质,在主材质里进行模型表面的区分(例如使用归并贴图等),再由该用户使用“BlendMaterialAttributes”表达式搭配归并贴图来融合多种纹理。
材质图层实例(Material Layer Instance)
“材质图层实例(Material Layer Instance)”的作用和材质实例及材质函数实例差不多,同样是为了在不改变材质图层的情况下修改材质图层。
着色器变体(Shader Permutation)
我们在使用材质的时候会用到一些表达式如“开关(Switch)”等这类允许一个材质在不同情况下(例如被水淋湿或干燥)切换不同的材质表达式,这个时候虽然说我们只是用到了一个材质,但是引擎在后台会根据这些表达式的数量生成对应不同的着色器(例如一个着色器用于渲染淋水后变湿时的材质,另一个着色器用于渲染干燥时候的材质),这些根据特定表达式而生成的同一材质里面的不同着色器,被称为“着色器变体(Shader Permutation)”。
如果我们在处理一些特定场景的项目时,我们可以通过在“项目设置(Project Settings)->渲染(Rendering)->着色器变体缩减(Shader Permutation Reduction)”里关闭一些该场景使用不到的着色器变体类型,这样的话有助于节省一些性能。
材质参数集合(Material Parameter Collection)
“材质参数集合(Material Parameter Collection)”不仅可以作用于某个特定材质或某个特定材质实例,更改材质参数集合将会作用于所有使用了该材质参数集合的材质或材质实例。
材质参数集合相当于是一个“超大型参数”,允许我们在主材质上不再通过“参数(Parameter)”类型的表达式来设置材质,而是通过一个类似与表达式的节点进行引用,这样就省去了硬编码的问题。
使用参数集合对性能只会存在非常非常小的性能影响,因为材质还是需要去查找对应的材质参数集合,但是这种操作的开销很小。
需要特别注意的是在每个材质中最多只能使用两个材质参数集合,使用超过两个材质参数集合之后会获得材质编辑器的警告并且无法编译材质,而每个材质参数集合都能使用1024个参数。
材质参数集合虽然说在开发过程中使用频率不高,但是特别适合于那些几乎要应用到所有材质上的参数,例如植物被风吹动的幅度、下雨时候地面和周围环境的湿润程度等。
高级着色器语言(High Level Shader Language,HLSL)
“高级着色器语言(High Level Shader Language)”是内置于UE4引擎的材质工具,可以使用HLSL这种编程语言来直接实现材质表达式,当材质系统里默认的材质表达式不满足我们需求的时候,我们就可以自己编写HLSL来实现我们自己的需求。引擎里可以提供两种方式来使用HLSL:“自定义表达式”和“全局着色器”。
自定义表达式(Custom Express)
“自定义表达式”支持将有限的HLSL代码注入到标准材质中,在材质图标里面使用“自定义(Custom)”表达式,选中该表达式之后就可以在表达式的细节面板里编写HLSL代码。
虽然很多时候我们使用材质系统的标准表达式即可实现材质需求,但在一些例外情况下标准表达式会无法实现我们的需求,例如我们需要获取到“外部信息(例如表达式“对象边界(Object Bound)”可以获取我们被应用材质对象的边界框大小)”,标准表达式提供的信息不够的时候,我们就需要HLSL来获取到我们需要的外部信息,在这里有一个比较好的例子来使用自定义表达式。
自定义表达式还可以用来实现类似“循环”这种在材质图表里没有提供表达式,但经常使用的功能。
全局着色器(Global Shader)
“全局着色器”也称“自定义全局着色器”,当我们的需求连自定义表达式都不能满足的时候,我们可以通过创建“全局着色器”来实现。
全局着色器在项目里面只能存在一次,因此全局着色器不能用于常规的材质系统里,因为常规材质是需要被多次使用到的,但是也因此全局着色器特别适合于后期处理特效如夜视仪和热成像效果。
将高级渲染效果渲染到“渲染目标”也是全局着色器的另一个使用方法。
全局着色器特别适合制作成插件然后让复数个项目都能共享使用。
材质属性里面的每个“着色模型(Shading Model)”都代表着一个系统的材质着色器。我们也可以为引擎添加新的着色模型,只需要按照方法进行添加然后重新编译整个引擎即可。
如果我们需要实现如《无主之地》那样卡通画渲染的效果,全局着色器是一个特别好的方法;或者如果我们需要让移动设备的运行更加流畅,生成新的材质着色器并对其进行优化也是一个好的方法。
全局着色器的使用方法可以通过官方文档及其他人编写的技术文档来查询。
材质工作流的使用方法
UE4引擎的材质系统提供了多大六种材质相关的工具供用户使用(材质编辑器、材质实例、材质函数、材质图层、材质参数集和HLSL六种),但在实际开发过程中我们并不会同时用到这六种工具,例如很多时候我们会在大量材质实例和大量材质图层之间选择工作流。教程视频为我们提供了三种常用的工作流。
仅使用材质编辑器
仅使用材质编辑器的工作流就是用户直接在材质图标里面使用材质表达式来实现材质的工作流,没有实例、没有函数、也没有图层,仅使用材质编辑器就实现材质的功能。
这种方法最简单直接,不会涉及到额外的知识点,但是仅适用于小型项目,因为当我们的项目变得庞大,需要有20到30个材质的时候,再用这种工作流去编写材质会产生性能和开发效率上的许多问题。
材质函数、材质编辑器和材质实例
这是很常用的一种材质工作流组合,使用材质函数生成可以应用于任何类型材质的材质表现,例如可以使用材质函数生成一个“细节法线”效果或“降雨湿度”效果,这种效果即可以丰富材质表面的细节又可以应用于几乎任何材质,然后在材质编辑器里编写项目需要使用到的极少数主材质,最后再在材质实例里去生成每个主材质需要生成的大量子材质。
材质图层和材质编辑器
我们可以使用材质图层去实现每个效果(如岩石、苔藓等效果)的实现方法,而不需要去考虑模型上哪些部分需要什么效果,很多时候这种工作流也会包含一些材质函数和材质实例。这种工作流会比材质实例更加灵活,因为具体的表现被实现在了材质图层而不是材质实例,因此会减少材质实例的数量,相应地会增加材质图层的数量。
材质图层、材质图层混合、材质实例和材质编辑器(最推荐的)
UE4.27里已经有了非常成熟的材质图层搭配材质图层混合来使用材质系统的工作流,因此我们以后可以使用“材质图层”来实现某一种类型的材质,然后通过“材质图层混合”来将不同的材质混合到一起,最后再使用“材质实例”来调整材质图层、材质图层混合里面的每一个参数。官方文档对材质图层及材质图层混合功能的使用有详尽的描述。
这种工作流可以应对很多需求,但是需要注意的是这种工作流会比直接使用材质编辑器实现的材质的性能开销高出一截,对于小项目来说可能不会产生太多开销,但是对于大型项目来说这就是一个需要权衡的事情。
材质参数集合和HLSL
这是一种使用频率很小的工作流,因为需要我们直接编写HLSL代码,这对于技术掌握要求很高,因此该工作流属于不常用的工作流,但能够解决复杂的问题。
注意事项
- 对于一个通常项目来说,会使用到的主材质数量为1050个,然后每个主材质对应的材质实例数量为1020个,这个数量不是固定的,只是一个大概数值,但是需要确定的是如果项目中主材质数量太少了的话会让主材质显得臃肿;太多了的话会让材质实例失去意义,并且会增加性能开销。
- 项目中存在“特例材质”是很正常的事情。例如某些材质无法使用大量材质实例或大量材质图层来实现,而只能直接通过材质编辑器为其单独编写,这样的材质被称为“独特材质”,项目中允许存在独特材质的存在,只需要注意独特材质的数量不能太多,太多的话会让上述的工作流失去意义,而且很有可能会增加性能开销和降低开发效率。
其他特性
材质系统还包含了一些额外但是很难被分类的功能,这些功能也可以帮我们在编写材质的时候提供帮助。
粒子材质(Particle Material)
在材质编辑器里通过一些特殊的材质表达式(如粒子颜色(Particle Color)、粒子方向(Particle Direction)等)可以让材质获取到外部粒子系统的信息。
自定义UV和顶点差值(Custom UV and Vertex Interpolators)
“自定义UV(Custom UV)”允许用户使用“纹理坐标(Texture Coordinate)”表达式连接到新材质输入“自定义UV(Customized UV,需要在材质属性里面修改“自定义UV数量(Num Customized Uvs))”来更改材质平铺到模型上的UV,其本质上和在纹理取样器的UV用纹理坐标表达式进行更改是一样的效果,但在自定义UV材质输入上更改是全局性的,而在纹理取样器上更改只会影响到该纹理取样器。
“顶点差值(Vertex Interpolator)”表达式接受一个输入,该输入一般为“纹理坐标”表达式的输出,同时该表达式的输出可以直接连到纹理采样器的UV输入上。从表现上来说该表达式没有任何作用,但是从本质上来说该表达式会让一些“像素着色器”执行的操作改为让“顶点着色器”进行操作,从日常开发来说顶点着色器的负担没有像素着色器的大,因此让顶点着色器进行部分渲染有助于减小像素着色器的性能开销。“自定义UV”同样也会做如此的优化。但是需要注意的是该方法对于高多边形的模型并不总是有效,甚至还会起反效果。对于需要从远处观察的模型来说也会起反效果,因为远距离观察模型的时候其需要渲染的像素点数量会变少,但是顶点增多了之后,反而会增加性能开销。
模糊(Bluring)
要想在材质系统里实现模糊纹理的功能,只需要使用“运动模糊(Motion Blur)”或“螺旋模糊(Spiral Blur)”节点即可。
模糊节点本质是只是将纹理进行复制,然后修改其位置和透明度,如此经过数个“步骤(Steps)”再把这些纹理合在一起,就实现了模糊的效果。
“黑体(Black Body)”和“自发光投射(Emissive Light Casting)”
“黑体(Black Body)”是一个材质表达式,该材质表达式接收一个输入表示温度,然后返回一个输出来将温度转换为对应的颜色。
“自发光投射(Emissive Light Casting)”功能可以在Actor的细节面板里勾选上选项“对静态光照使用自发光(Use Emissive For Static Light)”,即可在构建静态光照的时候让自发光的光源与周围环境进行交互。
环境光遮蔽蒙版(Ambient Occlusion Mask)
“预计算环境光遮蔽蒙版(PrecomputedAOMask)”表达式可以获取到Lightmass烘焙后的AO信息,从而可以通过该烘焙AO纹理来对环境中一些“边角”的地方进行处理。
需要首先在“世界设置(World Settings)”里开启“启用环境光遮蔽(Use Ambient Occlusion)”和“生成环境光遮蔽材质蒙版(Generate Ambient Occlusion Material Mask)”选项,就可以让材质表达式“预计算环境光遮蔽蒙版PrecomputedAOMask”生成该模型与环境光遮蔽产生交互的部分。
环境光遮蔽蒙版的功能只能用于静态光照。
地形(Landscape)
材质编辑器中有几组特殊的材质表达式名称是以“Landscape”开头,这一组材质表达式是专门用于地形系统的材质,例如“地形图层混合(LandscapeLayerBlend)”表达式允许用户定义多个图层,之后在地形系统里面可以通过“绘制(Paint)”来绘制不同图层的材质。
“地形可见性蒙版(LandscapeVisibilityMask)”可以直接连接到“蒙版”混合模式材质的“透明度蒙版(Opacity Mask)”材质输入上,这样就可以在地形系统里面使用“可见性(Visibility)”工具在地形上绘制空洞,该空洞的碰撞也会随空洞一样消失。
这一组特殊表达式中有个最特殊的表达式——“地形草地输出(LandscapeGrassOutput)”,该表达式只接收一个输入,通常来说我们可以把草地的地形图层当成唯一的输入。同时点击该表达式,我们可以在细节面板里设置变量“草地类型(Grass Type)”,这个变量可以指定一个“地形草地类型(Landscape Grass Type)”的变量,该变量可以在引擎的资源浏览器里创建并设置,如果我们在“地形草地输出”表达式上指定了“草地类型”变量之后,当我们在地形上绘制草地的地形图层时,引擎会自动将我们制定的草地模型添加到地形上。
距离场(Distance Fields)
网格体距离场用纹理贴图的方式来描述物体和物体之间的距离。
“与最近表面的距离(Distance To Nearest Surface)”表达式就是通过网格体距离场来获取到当前物体表面与其他物体之间的距离。
材质的性能影响
材质的性能影响来自于两个方面:
- 着色器所处理的像素数量和顶点数量。
- 与每个像素或顶点进行计算的指令的数量(也就是指令的复杂度)。
上述两个方面相结合就是一个材质在性能上产生的影响。
材质经常遇到的问题
- “缓冲显示视图模式(BUffer Visualization Mode)”可以帮助我们在大型项目材质出现问题时更好地定位问题材质。
缓冲显示视图模式可以“总览”或是单独显示材质输入中每一种通道的效果,使用这种显示方法就可以很好快速定位具体时间哪个材质的哪个通道出现了问题。
材质使用的小Tips
- 在材质编辑器里编写材质图表的时候,通过使用“Actor位置(Actor Position)”表达式搭配“绝对世界位置(Absolute World Position)”表达式并求这两个表达式之间的距离,可以获得到每个像素到被应用材质的Actor的距离,可以实现然一定高度(或距离)就切换材质的功能,例如可以为山体模型实现自动覆盖雪层的智能材质。
- 在材质编辑器里编写材质图表的时候,通过使用“像素法线世界场景空间(Pixel NormalWS)”表达式可以获取到被渲染物体的像素法线信息,通过对像素法线与Z轴的单位向量(0,0,1)的角度进行对比,可以实现只让模型的顶部进行渲染的功能,例如可以让一块石头只有顶部位置会长苔藓。
- UE引擎的材质系统在处理负值数据和超过1的数据的时候会出现一些意想不到的问题,所以很多时候如果材质里使用到了负数或超过1的数值的话,最好使用“限制(Clamp)”表达式来将这些数值限制到[0,1]的范围之间。
- 对“相机向量(Camera Vector)”和“像素法线世界场景空间(Pixel NormalWS)”执行“乘积(DotProduct)”操作,可以实现类似“菲涅尔效果”那种有轮廓线的材质效果。
- 使用“顶点颜色(Vertex Color)”表达式搭配“插值(Lerp)”表达式,再搭配上两张底色纹理,可以实现在UE引擎里用“绘制模式(Paint Mode)”在场景指定位置上绘制对应纹理的功能。
内容感知缩放
- 使用“对象半径(Object Radius)”除以一个常量,然后让结果与“纹理坐标(Texture Coordinate)”相乘,最后再将输入当做纹理采样器UV的输入,可以实现让模型的材质不会因为模型的尺寸而有所拉伸,特别适合用于实现岩石材质,因为场景里面的岩石可能会用不同大小的同一模型去进行表示,使用这种方法作为纹理采样器的UV输入的话就不会因模型大小的缩放而产生视觉上的瑕疵。
置换(Displacement)、视差(Parallax)和世界坐标偏移(World Position Offset)实现凹凸平面时的区别
- 这三种纹理(功能)都可以实现在平面模型上营造出凹凸不平、细节丰富的模型材质的功能,但是它们也存在以下本质上的区别:
- 置换纹理需要搭配“曲面细分(Tessellation)”功能来使用,因为首先需要使用曲面细分功能来为模型添加更多的三角面,然后才能使用“置换”输入为模型添加置换纹理(即高度图纹理),从而实现凹凸平面的功能。总结来说就是首先要细分,然后再置换。实现的凹凸效果比较好,但是会增加性能损耗,因为增加了模型的面数。
- 视差遮蔽贴图仅仅只需要高度图并在材质函数上设置好对应的参数,就可以获取到材质函数得到的视差遮蔽贴图相关的纹理,应用到对应的材质输入上面就可以实现凹凸平面的效果。总结来说就是首先使用高度图并设置对应参数,就能获取到实现凹凸效果所需要的纹理(如原纹理的新UV、像素偏移等)。实现的凹凸效果一般,但是没有特别大的性能损耗,因为该方法并没有增加模型面熟。
- 世界坐标偏移仅仅只需要一张高度图,对其设置高度纹理的强度和方向,输入到“世界坐标偏移(World Position Offset)”输入上就可以实现凹凸平面的效果,但是凹凸效果的好坏取决于原模型原本顶点的数量,因为只有在高模上使用世界坐标偏移才会有很顺滑的过渡效果,如果原模型的顶点数很少的话,该方法实现凹凸效果就会很局限,因为该方法并不会更改模型的顶点数。实现的效果取决于原模型顶点的数量,模型顶点数量越多,凹凸效果越顺滑;定点越少,则凹凸效果越不明显。
- 但无论使用什么方法来实现凹凸平面的效果,这些凹凸平面都可以正确接受到光照,即使是视差。
如何缓解模型与模型重叠时候接缝处特别生硬的问题
- 使用“Dither Temporary AA”表达式与一维向量进行相乘,然后将结果输出到“像素深度偏移”材质输入上,通过更改一维向量的值就可以修改边缘接缝处的融合程度,从而减少边缘接缝生硬的问题。
使用半透明混合模式时可以遵守的一些优化规则
- 当蒙版和半透明混合模式都能满足需求时,优先使用蒙版混合模式。原因也很简单,因为蒙版混合模式的性能开销比半透明混合模式的开销要低。
- 如果必须使用半透明混合模式,“默认光照”和“无光照”着色模型都能满足需求时,优先使用“无光照”着色模型。同样是因为无光照着色模型的开销低。
- 如果必须使用默认光照着色模型的半透明混合模式,那么“光照模式”应该从最简单的“体积非定向”开始,到最复杂的“正向着色”光照模式,因为这些光照模式的开销是依次上升的。
- 如果必须使用最优效果的半透明材质,那么确保在小范围内使用,而不应该使用超过500个高开销的半透明材质去构建一个玻璃大厅。
- 确保不要存在从一个半透明面透视到另一个半透明面的情况发生,因为当半透明面重叠在一起的时候,半透明开销会变得更高。
自然模型(例如岩石)使用的材质可以让纹理映射在“世界坐标”下来解决接缝的问题。
- 很多时候我们会把岩石之类的自然模型按照不同大小不同方向进行摆放并互相重叠,从而生成一组更大型的自然模型,如果使用普通工作流生成的材质进行摆放的话会让重叠部分产生明显的闪烁,在材质里面让纹理采样器的UV输入接上“绝对世界坐标”,让纹理在世界坐标上进行映射而不是物体的UV坐标上,这样当自然模型之间有重叠的时候会呈现出更自然的效果。
如何解决当我们在远处观看草地材质的时候材质重复平铺的问题
- 使用“像素深度(Pixel Depth)”表达式输出的值作为插值表达式的Alpha引脚输入,然后对一张小尺寸纹理和大尺寸纹理进行插值,这样当像素深度值很大(即像素离屏幕很远)的时候显示材质中大尺寸的纹理;当像素深度值很小(即像素离屏幕很近)的时候显示材质中尺寸较小的纹理,这样就可以让远处的草地纹理不会显示重复平铺。
材质资产的协作流程
自动导入
- 在项目的“编辑器偏好(Editor References)”->“加载&保存(Loading & Saving)”->“自动重导入(Auto Reimport)”里可以让引擎监视我们指定的文件夹(仅限于Content文件夹内),当我们往指定的文件夹内存放新文件如TGA时,引擎会自动监测到文件并提示是否导入到引擎。