13 KiB
UShaderLab 说明
UShaderLab 用来在 Unreal Engine 里用接近 HLSL 的文本语法编写材质。使用者编写 .usl 文件,插件会把它解析成材质图,并通过创建 ShaderLab Material Instance 暴露可调参数。
基本工作流
- 在
<Project>/Shaders或某个插件的<Plugin>/Shaders目录下创建.usl文件。 - 文件顶部#include "/Plugin/ShaderLab/ShaderLab.ush",便于 IDE 识别 DSL 宏和
UE_内建函数。 - 用
SL_SETTINGS描述材质设置,用SL_PROPERTY声明实例参数。 - 用
SL_SURFACE、SL_UNLIT、SL_POSTPROCESS等入口编写材质主体。 - 在 Content Browser 里创建
ShaderLab Material Instance,选择对应的 ShaderLab 基材质。 - 在生成的实例上调参数,并把实例赋给网格、UI、后处理或其它承载对象。
推荐在编辑器控制台执行一次:
ShaderLab.IDE.Prepare
它会为 .usl、.uslfunc 准备基础的 HLSL 识别和路径映射,让 IDE 补全更舒服。
VSCode推荐安装shader-validator插件,已经支持该插件的补全
最小示例
下面是一个最简单的 Surface 材质:
#include "/Plugin/ShaderLab/ShaderLab.ush"
SL_SETTINGS(Domain = Surface, BlendMode = Opaque)
SL_PROPERTY(Category = "Surface")
float3 BaseColor = float3(0.8, 0.2, 0.1);
SL_PROPERTY(Category = "Surface", ClampMin = 0, ClampMax = 1)
float Roughness = 0.5;
SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
S.DiffuseAlbedo = BaseColor;
S.Roughness = Roughness;
}
文件名就是这个 ShaderLab 材质的名字来源。在内容浏览器Material ▸ ShaderLab Material Instance创建,选择器里选择它,然后在实例详情面板里调整 BaseColor 和 Roughness。
文件类型
.usl 是可生成材质的 ShaderLab 文件(理解为UMaterial)。一个 .usl 对应一个可被实例化的 ShaderLab 材质。
.uslfunc 是函数库文件(理解为UMaterialFunction)。它不能直接生成材质,只能被 .usl 或其它 .uslfunc 引入,用来复用函数和参数。
#include "/Project/Lib/MyLibrary.uslfunc"
路径通常使用虚拟 shader 路径,例如:
/Project/...
/Plugin/<PluginName>/...
/Engine/...
常用设置
SL_SETTINGS 写在文件顶层,用来描述材质域、混合模式和常见材质设置。
SL_SETTINGS(Domain = Surface, BlendMode = Masked, TwoSided = true, OpacityMaskClipValue = 0.4)
常用 Domain:
Surface:普通表面材质。PostProcess:后处理材质。UI:UI 材质。Decal:贴花材质。Volume:体积材质。LightFunction:灯光函数材质。
常用 BlendMode:
OpaqueMaskedTranslucentAdditiveModulateAlphaCompositeAlphaHoldout
参数声明
输入参数主要有两类:SL_PROPERTY 声明材质实例上的可调参数,SL_COLLECTION 声明来自 Material Parameter Collection 的全局输入。
SL_PROPERTY(Category = "Surface")
float3 Tint = float3(1, 1, 1);
SL_PROPERTY(Category = "Surface", ClampMin = 0, ClampMax = 1)
float Metallic = 0.0;
SL_PROPERTY(Category = "Textures", DefaultTexture = "white")
Texture2D AlbedoTex;
SamplerState AlbedoTexSampler;
SL_PROPERTY(Category = "Switches")
#define UseTexture 1
SL_COLLECTION(Path = "/Game/YourFolder/MPC_Global", Parameter = "Wind")
float3 Wind;
常见类型对应关系:
float:Scalar 参数。float3:Color 参数。float4:Vector 参数。Texture2D、TextureCube:贴图参数。#define Name 0/1:Static Bool 参数,可用于#if。SL_COLLECTION(...) floatN Name;:材质参数集合输入,不是实例旋钮。
常用 specifier:
Category:实例面板里的分类。SortPriority:排序优先级。ClampMin、ClampMax:Scalar 滑条范围。DefaultTexture:贴图默认值,可用white、black、grey、normal或资产路径。CustomPrimitiveData:让 Scalar 或 Vector 从组件的 Custom Primitive Data 读取。
SL_COLLECTION 常用写法:
SL_COLLECTION(Path = "/Game/YourFolder/MPC_Global", Parameter = "Wind")
float3 Wind;
SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
S.EmissiveColor = abs(Wind);
}
Static Bool 可以直接控制预处理分支:
SL_PROPERTY(Category = "Switches")
#define UseTexture 1
SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
#if UseTexture
S.DiffuseAlbedo = float3(1, 1, 1);
#else
S.DiffuseAlbedo = float3(0.2, 0.2, 0.2);
#endif
}
在材质实例里覆盖 Static Bool 后,会按对应排列重新编译。
Surface 材质
普通表面材质最常用 SL_SURFACE。函数参数是 inout FShaderLabSurface,只需要填写你关心的字段,没写的字段会使用默认值。
SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
float2 uv = UE_TextureCoordinate(0);
S.DiffuseAlbedo = Texture2DSample(AlbedoTex, AlbedoTexSampler, uv).rgb * Tint;
S.Roughness = Roughness;
S.EmissiveColor = float3(0, 0, 0);
}
FShaderLabSurface = 18 个 SlabBSDF 着色引脚 加上 材质级(整材质)输出字段,所以简单材质可以在一个 body 里全部写完。常用字段:
- 着色(SlabBSDF 引脚):
DiffuseAlbedo、F0、F90、Roughness、Anisotropy、Normal、Tangent、EmissiveColor、SSSMFP、SecondRoughness、Fuzz*、Glint*… - 材质级:
Opacity、OpacityMask、Refraction、PixelDepthOffset、AmbientOcclusion、SurfaceThickness
材质级字段仅在匹配的 Domain/BlendMode 下生效(建图期按引擎 IsPropertyActive 做契约校验,不匹配则映射 .usl 行报错):OpacityMask 需 Masked;Refraction 需半透明混合 + RefractionMethod = RM_IndexOfRefraction;SurfaceThickness 需 bIsThinSurface = true。Opacity 是覆盖率:Substrate 下引擎的 MP_Opacity 仅对 AlphaComposite 激活,因此半透明/贴花的覆盖率由建图器自动在整材质外包一层 Substrate Weight 节点实现(你只需写 S.Opacity)。
如果需要在 body 里使用依赖材质上下文的变换函数,可以显式声明 Parameters:
SL_SURFACE()
void Surface(FMaterialPixelParameters Parameters, inout FShaderLabSurface S)
{
float3 n = UE_TransformTangentVectorToWorld(Parameters, float3(0, 0, 1));
S.Normal = n;
}
其它材质入口
除了 SL_SURFACE,还可以使用其它入口来表达不同类型的材质。
SL_UNLIT()
void Unlit(inout FShaderLabUnlit U)
{
U.EmissiveColor = float3(1, 0.5, 0.1);
}
常用入口:
SL_UNLIT:无光照材质。SL_HAIR:头发材质。SL_EYE:眼睛材质。SL_WATER:Single Layer Water。SL_CLEARCOAT:清漆材质。SL_TOON:卡通材质。SL_VOLUME:体积材质,需要Domain = Volume。SL_LIGHTFUNCTION:灯光函数材质,需要Domain = LightFunction。SL_POSTPROCESS:后处理材质,需要Domain = PostProcess。SL_UI:UI 材质,需要Domain = UI。
多层材质
多个 SL_SLAB 可以通过 SL_FRONTMATERIAL 组合成一个 Substrate 材质。Slab 是纯 BSDF 层,参数为 inout FShaderLabSlab(只有 18 个 SlabBSDF 引脚,没有 Opacity 等材质级字段)。整材质输出写在单独的 SL_MATERIAL 块里(见下)。
SL_PROPERTY(Category = "Layer", ClampMin = 0, ClampMax = 1)
float Mix = 0.5;
SL_SLAB()
void Base(inout FShaderLabSlab S)
{
S.DiffuseAlbedo = float3(0.1, 0.1, 0.1);
S.Roughness = 0.8;
}
SL_SLAB()
void Coat(inout FShaderLabSlab S)
{
S.DiffuseAlbedo = float3(0.8, 0.2, 0.1);
S.Roughness = 0.2;
}
SL_FRONTMATERIAL(HorizontalMix(Base, Coat, Mix))
常用组合:
VerticalLayer(Top, Base, Thickness):上下层叠。HorizontalMix(Background, Foreground, Mix):横向混合。Add(A, B):相加。Weight(A, Weight):权重。Select(A, B, Threshold):选择。
混合因子可以是浮点字面量、Scalar 参数,或一个 SL_VALUE。
SL_VALUE()
float EdgeMask()
{
return saturate(UE_TextureCoordinate(0).x);
}
SL_FRONTMATERIAL(HorizontalMix(Base, Coat, EdgeMask))
多层材质的整材质输出(单入口 SL_SURFACE 里是 S.*)写在 SL_MATERIAL 块里,填 FShaderLabMaterialOutput——一个 Custom 节点输出全部字段(共享计算)。它与 SL_SURFACE 互斥。
SL_MATERIAL()
void Material(inout FShaderLabMaterialOutput O)
{
O.OpacityMask = Texture2DSample(MaskTex, MaskTexSampler, UE_TextureCoordinate(0)).a;
}
顶点与插值
SL_VERTEX 用于输出顶点阶段数据,比如世界位置偏移。
SL_PROPERTY(Category = "Vertex")
float Offset = 10.0;
SL_VERTEX()
void Vertex(inout FShaderLabVertex V)
{
V.WorldPositionOffset = float3(0, 0, Offset);
}
SL_VERTEX 还能在顶点频率计算 UV,写入 V.CustomizedUV0..7(插值到像素,用 UE_TextureCoordinate(i) 读回)。写 V.CustomizedUV<i> 必须配 SL_SETTINGS(NumCustomizedUVs = N) 且 N > i——引擎只分配前 NumCustomizedUVs 组(默认 0),其余槽位直接透传原始顶点 texcoord;ShaderLab 会把「写了 UV 但 N ≤ i」变成建图错误而非静默失效。示例见 Shaders/Examples/CustomizedUV.usl。
SL_INTERPOLATOR 用于在顶点阶段计算一个值,并在像素阶段读取。
SL_INTERPOLATOR()
float3 VertexNormalColor()
{
return abs(UE_VertexNormalWS());
}
SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
S.DiffuseAlbedo = UE_Interpolator(VertexNormalColor);
}
插值器适合把顶点频率的结果带到像素阶段。不要把只适合像素阶段的采样逻辑放进 SL_INTERPOLATOR。
函数库 .uslfunc
.uslfunc 用来复用材质函数。库里可以声明 SL_FUNCTION,也可以声明自己的 SL_PROPERTY。当一个 .usl 引入库时,库里的参数会提升到这个材质实例上。
库文件:
#include "/Plugin/ShaderLab/ShaderLab.ush"
SL_PROPERTY(Category = "Detail", ClampMin = 0, ClampMax = 1)
float DetailStrength = 0.5;
SL_FUNCTION()
float3 ApplyDetail(float3 color, float2 uv)
{
float stripes = frac(uv.x * 12.0);
return lerp(color, color * stripes, DetailStrength);
}
材质文件:
#include "/Plugin/ShaderLab/ShaderLab.ush"
#include "/Project/Lib/Detail.uslfunc"
SL_PROPERTY(Category = "Surface")
float3 BaseColor = float3(0.8, 0.8, 0.8);
SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
S.DiffuseAlbedo = ApplyDetail(BaseColor, UE_TextureCoordinate(0));
}
如果多个库里有同名参数,可以用 SL_IMPORT 给提升出来的参数加命名空间前缀。
SL_IMPORT(Namespace = "Detail")
#include "/Project/Lib/Detail.uslfunc"
常用 UE_ 内建函数
ShaderLab 通过 UE_ 前缀暴露常见的 UE 材质节点。它们可以在 .usl body 中像普通函数一样调用。
常用类别:
- UV:
UE_TextureCoordinate(0)。 - 时间:
UE_Time()。 - 几何数据:
UE_VertexNormalWS()、UE_PixelNormalWS()、UE_CameraVectorWS()。 - 对象和实例:对象位置、对象半径、Actor 位置、Custom Primitive Data。
- 坐标变换:
UE_TransformVector...、UE_TransformPosition...。 - 噪声和旋转:
UE_Noise(...)、UE_RotateAboutAxis(...)。 - 场景纹理:
UE_SceneTexture(...)、UE_SceneDepth(...)、UE_CustomDepth(...)、UE_CustomStencil(...)。 - 距离场:
UE_DistanceToNearestSurface(...)、UE_DistanceFieldGradient(...)。
示例:
SL_SURFACE()
void Surface(inout FShaderLabSurface S)
{
float t = UE_Time();
float2 uv = UE_TextureCoordinate(0);
float3 normalColor = abs(UE_VertexNormalWS());
S.EmissiveColor = normalColor * (0.5 + 0.5 * sin(t + uv.x * 6.28318));
}
材质实例与使用注意
ShaderLab 材质应该通过 ShaderLab Material Instance 使用。
Usage 不写在 .usl 里。把实例赋给静态网格、骨骼网格、粒子、UI 等对象时,按 UE 的材质实例规则设置或自动记录对应 usage。打包前需要确保实际使用到的 usage 已经在编辑器阶段确定。
.usl 和 .uslfunc 会从项目或插件的 Shaders 目录参与打包。材质实例引用的贴图、MPC、Profile 等资源仍应按正常 UE 资产规则存在于工程内容中。
实现思路
使用者可以把 UShaderLab 理解成三层:
.usl是材质源码。它保持接近 HLSL 的写法,用SL_宏补充材质设置、参数和入口信息。- 插件在编辑器里解析
.usl,把参数、Custom HLSL、Substrate BSDF 和必要的辅助节点组成 UE 材质图。 - 最终给场景使用的是 ShaderLab Material Instance。实例保存参数覆盖,并负责拥有可渲染所需的编译结果。
静态开关 #define 会作为材质实例的 Static Bool 参数暴露。实例覆盖后,相关 #if 会按当前排列生效,所以可以把不同分支写成真正的编译期分支。
基材质主要是模板,日常使用时只需要创建和保存 ShaderLab Material Instance。
