中EN
概念页

材质图到 HLSL 的翻译

Material Graph to HLSL Translation

材质编辑器点 Apply 之后,引擎先把节点图翻译成 HLSL,再把 HLSL 编译成 GPU 字节码。翻译以 MaterialTemplate.ush 为模板、由 FHLSLMaterialTranslator 做文本替换与常量折叠;内置节点能被折叠,Custom 材质表达式 则不能。每个材质还会按特性/平台/用途生成多份着色器排列。

两步:翻译 vs 编译

点 Apply 时引擎收集混合模式、着色模型、用途标记,以及描述表面的全部节点,填进模板,得到「着色器配方」(HLSL)。之后再把 HLSL 编成 GPU 字节码。Epic KB 强调本轨只需弄清图→HLSL;字节码与排列爆炸的细节可另见 Matt Pettineo《The Shader Permutations Problem》等(文中外链,本库未收录)。(How the Unreal Engine Translates a Material Graph to HLSL)

在编辑器中:Window → Shader Code → HLSL Code 可只读查看生成代码,再复制到带 HLSL 高亮的编辑器里拆读。(How the Unreal Engine Translates a Material Graph to HLSL、Essential Material Concepts)

Essential Concepts 说得更直白:节点图在幕后被静默翻译成 HLSL;用户能看、不能在编辑器里直接改那段 HLSL。(Essential Material Concepts)

模板与调用链

生成的 HLSL 基于 Engine/Shaders/Private/MaterialTemplate.ush:结构体与函数体里是 %s 占位符,运行时做文本替换。填好的函数由像素着色器 BasePassPixelShader.usf 调用,真正画每个像素。(How the Unreal Engine Translates a Material Graph to HLSL)

FMaterialCompiler 是接口;标准路径实现是 FHLSLMaterialTranslator(另有 Lightmass、Proxy)。调用路径:

flowchart TB
  A["FMaterial::CacheShaders"] --> B["BeginCompileShaderMap"]
  B --> T["FHLSLMaterialTranslator::Translate"]
  B --> E["GetMaterialEnvironment"]
  B --> S["GetMaterialShaderCode"]
  B --> C["FPartialShaderMap::Compile"]
步骤职责
Translate为每个材质输入针脚准备子图;补齐顶点插值、UV、Material Parameter Collection、虚拟纹理采样等;对非法特性组合报错(例如逐像素着色模型配 SingleLayerWater)
GetMaterialEnvironment写入最终 HLSL 里用的 #define(着色模型与特性常用 #ifdef 切换)
GetMaterialShaderCode把编译好的数据打印进模板字符串
Compile翻译成功后进入字节码编译

(How the Unreal Engine Translates a Material Graph to HLSL)

一个材质通常对应多份着色器(渲染特性 × 平台 × Usage),不是「一材质一着色器」。(How the Unreal Engine Translates a Material Graph to HLSL)这与 着色频率与材质系统 里「材质模板 → 着色器变体」以及静态参数导致的排列爆炸(见 材质系统)是同一工程问题的不同侧面。

节点如何变成表达式:UMaterialExpression::Compile

每个彩色节点继承 UMaterialExpression,关键虚函数是 Compile(另有 GetCaption、GetKeywords)。声明在各自头文件,实现集中在 MaterialExpressions.cpp;节点可自带常量与 FExpressionInput。(How the Unreal Engine Translates a Material Graph to HLSL)

Compile 返回 int32:已编译结果在翻译器内部数组中的下标(逻辑上的「指针」)。若输入针脚已连线,先递归 Compile 子树;否则用节点自带常量(如 Compiler->Constant(ConstA))。以 Add 为例,最终落到 FHLSLMaterialTranslator::Add。(How the Unreal Engine Translates a Material Graph to HLSL)

常量折叠:内置节点的核心优化

Add 并不只是拼 "%s + %s",而是按操作数「是否整次 draw call 恒定」分支:

A / B翻译结果
双方都是常量CPU 预先算好,着色器里写字面量
一方常量 + 一方参数生成隐藏参数,在 CPU 侧把常量加进参数,GPU 只看到一个固定数
双方都非常量发出 A + B

这就是常量折叠 (Constant Folding):避免 GPU 反复算「整次绘制都不变」的值。(How the Unreal Engine Translates a Material Graph to HLSL)同文在 Custom 节点文档中强调:Custom 阻止常量折叠,指令数往往明显高于等价内置节点图——见 Custom 材质表达式。(Custom Material Expressions)

示例图在 CalcPixelMaterialInputs 中的典型落点(便于对照 HLSL Code 窗口):

材质输入生成形态
Roughness(两常量相加)PixelMaterialInputs.Roughness = (1.0 + 2.0);
Metallic(常量+参数已折叠)Material.ScalarExpressions[0].z(均匀表达式打成 float4 打包)
Base Color(TexCoord + 参数)MaterialFloat Local1 = (...); PixelMaterialInputs.BaseColor = Local1;

非常量中间结果写成 LocalN;子图会做哈希,相同计算复用同一 LocalN,避免重复算。(How the Unreal Engine Translates a Material Graph to HLSL)

编辑器版 Emissive 的特殊 lerp

即使 Emissive 针脚空着,编辑器编译出的材质仍可能出现「用户自发光」与某个 VectorExpression 的 lerp,系数由引擎控制——用于选中高亮;Cook 后的着色器会优化掉。(How the Unreal Engine Translates a Material Graph to HLSL)读 HLSL Code 时不要把这段当成运行时必付成本。

与图形学轨的对应

UE 机制本库理论 / 练习页
节点 = HLSL 片段的可视化可编程着色器与 GPU 管线、像素着色器
图→HLSL→字节码通识「着色器编译」;常量折叠另见 构建配置与编译优化(CPU 编译器侧同类思想)
手写片元语法(GLSL)片元着色器与 GLSL 基础(BoS / LearnOpenGL);UE 侧手写入口是 Custom 节点(HLSL)
材质变体 / 排列着色频率与材质系统、材质系统 静态参数
  1. How the Unreal Engine Translates a Material Graph to HLSL Epic KB Matt Oztalay / Joe Radak(2022;更新至 2026-04)
  2. Essential Material Concepts UE 5.8 文档
  3. Custom Material Expressions UE 5.8 文档
  4. Materials (landing) UE 5.8 文档

相关页面

游戏开发 · UE5材质系统Material SystemUE 材质 (Material) 是用节点图描述「表面如何与光交互」的着色器生成器。它有三个正交维度:着色模型 (Shading Model) 决定光照模型(Default Lit、次表面、清漆、毛发等),混合模式 (Blend Mode) 决定与背景的合成公式(Opaque/Masked/Translucent…),参数 + 材质实例 (Material Instance) 让美术在不重编译着色器的前提下做变体。PBR 输入(Base Color/Roughness/Metallic/Specular)直接对应 RTR4 的金属度-粗糙度工作流;最大的工程坑是静态参数导致的着色器排列爆炸。游戏开发 · UE5Custom 材质表达式Custom Material ExpressionCustom 节点允许在材质图里直接写 HLSL:任意数量输入、指定输出类型,可选额外输出、define 与外部 .ush/.usf 包含路径。它是「内置节点做不到」时的逃生口;代价是无法常量折叠、输入仅函数局部可见、且必须是合法 HLSL。优先用内置节点与材质函数,只在必要时上 Custom。图形学着色频率与材质系统Shading Frequency & Material Systems实现着色时要按"计算频率"划分工作:整个 draw call 不变的放到应用端作为 uniform,其余放在相应的可编程阶段。实践中大多数着色在像素级完成,顶点着色器负责变换和变形。材质系统把面向美术的材质与底层着色器组织起来,处理海量着色器变体的组合问题。图形学可编程着色器与 GPU 管线Programmable Shaders & GPU PipelineGPU 把第 2 章的概念管线实现为一组硬件阶段,分为完全可编程、可配置但不可编程、完全固定三类。现代 GPU 采用统一着色器架构:各类着色器共用同一套编程模型和核心,由 GPU 自行分配负载。着色器用 HLSL、GLSL 等类 C 语言编写,输入分为 uniform 和 varying 两类。图形学片元着色器与 GLSL 基础Fragment Shaders & GLSL Basics片元着色器(fragment shader)是在每个像素(片元)上并行执行的小程序:输入位置等信息,输出颜色。GLSL 是 OpenGL / OpenGL ES / WebGL 共用的着色语言;本书用全屏 billboard 上的像素着色练习,建立程序化图形的基础心智模型。图形学像素着色器Pixel Shader像素着色器(OpenGL 里叫片元着色器)对光栅化产生的每个片元运行,用插值后的顶点数据算出颜色,也可以输出不透明度、修改深度或直接丢弃片元。多渲染目标让它一次输出多组数据,催生了延迟着色。它通常只能写自己所在的像素,但能通过 2×2 quad 拿到相邻像素的导数。游戏开发 · UE5UE5 渲染架构UE5 Rendering ArchitectureUE5 的渲染器是独立的 Renderer 模块,在渲染线程 (Rendering Thread) 上以「场景代理」镜像游戏线程的组件状态,每帧按固定顺序执行深度预通道、G-buffer 基础通道、光照、雾、半透明和后处理。现代渲染代码用渲染依赖图 (Render Dependency Graph, RDG) 声明通道与资源,由 RDG 自动排屏障、复用瞬态内存、并行录制;最终命令经 RHI 线程翻译成 D3D12/Vulkan 等图形 API 调用。默认走延迟着色 (Deferred Shading),也可切换前向着色 (Forward Shading)。游戏开发 · UE5UE5 性能分析UE5 ProfilingUE5 性能分析的基本流程是:先用 stat unit 判断瓶颈在游戏线程、渲染线程还是 GPU,再用对应的 stat 组、profilegpu/GPU 统计或 Unreal Insights 的 Timing/Memory 等视图向下钻取;渲染特性(RDG、VSM、TSR)各自提供作用域和可视化。测量时要避开会扭曲数据的设置(调试构建、异步计算、可视化模式)。游戏开发构建配置与编译优化Build Configurations & Compiler OptimizationC++ 游戏代码从源文件到可执行文件要经过预处理、编译、链接。游戏项目通常不止 Debug/Release 两种构建配置 (build configuration),而是 Debug、Development、Ship,有时再加混合构建;配置越多测试越难。编译器优化分为局部优化和全局优化(链接时优化 LTO、剖析引导优化 PGO),它们让程序更快,但也让调试器“说谎”。所以要学会调试优化过的构建,并用统计式或插桩式性能分析器 (profiler) 找出最耗时的代码。游戏开发 · UE5Substrate 材质Substrate MaterialsSubstrate 用一种更有表现力、更模块化的框架,取代旧材质系统里固定的一组着色模型和混合模式(Default Lit、Clear Coat 等)。材质被看作一块块「物质板 (Slab)」,每块是用有明确单位的物理量参数化的原则化 BSDF,再通过混合、垂直分层等运算组合成图。代价是光照评估更贵,所以提供参数混合和两种 GBuffer 格式(Blendable / Adaptive)来在速度和画质之间取舍。新项目默认启用 Substrate;升级到 5.7 及以后的老项目默认不启用。

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原