PBR Material SystemPBR 材质系统
Material Architecture — Two Authoring Paths材质架构 — 两种创作路径
NeoX materials exist in two forms. The handwritten path uses a clean set of Get*() interface functions in material.hlsl. The material-graph path produces a compiled default.hlsl where all parameters are resolved in one monolithic CalcMaterialParam() call. Both paths feed the same downstream pipeline.
NeoX 材质有两种形式。手写路径在 material.hlsl 中使用一组整洁的 Get*() 接口函数。材质图路径产生编译后的 default.hlsl,所有参数在单个 CalcMaterialParam() 调用中解析。两种路径都向同一下游管线输出。
Texture Slot Convention — The MRO Pack纹理槽位约定 — MRO 打包
All standard PBR materials follow a fixed 4-texture, 3-slot encoding. The third texture packs three physically-based parameters into RGB channels — called MRO (Metalness, Roughness, Occlusion):
所有标准 PBR 材质遵循固定的 4 纹理、3 槽位编码。第三张纹理将三个基于物理的参数打包到 RGB 通道中——称为 MRO(金属度、粗糙度、遮蔽):
| Slot槽位 | Register寄存器 | R | G | B | A |
|---|---|---|---|---|---|
| Tex0 | t0 | Albedo (sRGB → linear)反照率(sRGB→线性) | Opacity不透明度 | ||
| Tex1 | t1 | Normal map (tangent space)法线贴图(切线空间) | — | ||
| Tex2 | t2 | AO / OcclusionAO 遮蔽 | Roughness粗糙度 | Metalness金属度 | varies*视材质* |
* The Alpha channel of Tex2 has material-specific meaning: for skin it carries SSS curvature; for the 4S SSSS material it carries SSS blur intensity; for terrain it may carry blend weights.
* Tex2 的 Alpha 通道有材质特定含义:皮肤材质存储 SSS 曲率;4S SSSS 材质存储 SSS 模糊强度;地形材质可能存储混合权重。
Intensity float4 uniform modulates Tex2 channels: Intensity.x = normal intensity, Intensity.y = roughness scale, Intensity.z = metalness scale, Intensity.w = emissive scale. Final roughness = MakeMaterialRoughness(tex2.g * Intensity.y) = saturate(tex2.g * Intensity.y).
Uniform 调制:Intensity float4 uniform 调制 Tex2 通道:Intensity.x=法线强度,Intensity.y=粗糙度缩放,Intensity.z=金属度缩放,Intensity.w=自发光缩放。最终粗糙度 = MakeMaterialRoughness(tex2.g * Intensity.y) = saturate(tex2.g * Intensity.y)。
Material Interface Functions材质接口函数
Every material file implements the same set of interface functions. The engine's SetupMaterial() calls them in order to fill the Material struct:
每个材质文件实现同一套接口函数。引擎的 SetupMaterial() 按顺序调用它们以填充 Material 结构体:
| Function函数 | Output field输出字段 | Default behavior (material.hlsl)默认行为(material.hlsl) |
|---|---|---|
| GetAlbedo() | mtl.albedo | ToLinear(tex0.rgb) * BaseColor.rgb |
| GetOpacity() | mtl.opacity | tex0.a * BaseColor.a |
| GetNormal() | mtl.normal | TBN decode, xy scaled by Intensity.xTBN 解码,xy 按 Intensity.x 缩放 |
| GetRoughness() | mtl.roughness | saturate(tex2.g * Intensity.y) |
| GetMetalness() | mtl.metalness | saturate(tex2.b * Intensity.z) |
| GetSpecular() | mtl.specular | 0.5 (hardcoded) |
| GetEmissive() | mtl.emissive | EmissiveColor.rgb * Intensity.w |
| GetOcclusion() | mtl.occlusion | tex2.r |
| GetSSSCurvature() | mtl.sss_curvature | empty stub (0)空桩(0) |
| GetSSSColor() | mtl.sss_color | empty stub (float3 0)空桩(float3 0) |
| GetRawPreintergratedSkin() | LUT sampleLUT 采样 | empty stub — overridden in material_skin.hlsl空桩——在 material_skin.hlsl 中覆盖 |
// material.hlsl — Normal decoding (standard TBN path) void GetNormal(Fragment frag, RawData raw, inout Material mtl) { #if LIGHT_ENABLE && NORMAL_MAP_ENABLE float3 n = raw.tex1.xyz * 2.0 - 1.0; n.xy *= Intensity.x; // normal intensity float3x3 TBN = float3x3(frag.tangent_world, frag.binormal_world, frag.normal_world); mtl.normal = normalize(mul(n, TBN)); #else mtl.normal = frag.normal_world; #endif } // material_ssss.hlsl — UE4 normal path (z reconstruction) void GetNormal(...) { #if IMPORT_FROM_UE4 n.z = sqrt(max(1.0 - dot(n.xy, n.xy), 0)); // reconstruct Z #endif // ... rest of TBN decode }
default.hlsl — Material Graph Compiled Shaderdefault.hlsl — 材质图编译着色器
The new engine's default.hlsl is the output of the material graph editor — rather than separate Get*() functions, all parameter logic is centralized in a single CalcMaterialParam() function. This enables features impossible in the handwritten path:
新引擎的 default.hlsl 是材质图编辑器的输出——所有参数逻辑集中在单个 CalcMaterialParam() 函数中,而非独立的 Get*() 函数。这使得手写路径无法实现的功能成为可能:
| Macro宏 | Feature功能 |
|---|---|
| IMPORT_FROM_UE4 | Use UE4-style normal map (Z reconstructed from XY; swizzled G channel)使用 UE4 风格法线贴图(Z 从 XY 重建;G 通道翻转) |
| EMISSIVE_TEXTURE | Sample Tex3 as dedicated emissive map; multiply by EmissiveColor * Intensity.w采样 Tex3 作为专用自发光贴图;乘以 EmissiveColor * Intensity.w |
| SHADER_CUSTOMIZED_UV0..3 | Per-UV transform hooks — allows each texture to use independently transformed UVs逐 UV 变换钩子——每张纹理可使用独立变换的 UV |
| SHADER_LOCAL_OFFSET | Vertex position offset in object/local space (driven by texture)物体/局部空间的顶点位置偏移(由纹理驱动) |
| SHADER_WORLD_OFFSET | Vertex position offset in world space (WPO equivalent)世界空间的顶点位置偏移(相当于 WPO) |
| SHADER_REFRACTION | Screen-space refraction: distort UVs by depth buffer to simulate glass/water bending屏幕空间折射:通过深度缓冲扭曲 UV 模拟玻璃/水折射 |
| USE_CLOUDGI_BAKE | Cloud GI baking reference mode云 GI 烘焙参考模式 |
// default.hlsl — Emissive with EMISSIVE_TEXTURE macro float3 emissive_color; #if EMISSIVE_TEXTURE emissive_color = GetColorTextureValue(Tex3, s3, uv0) * EmissiveColor.rgb * Intensity.w; #else emissive_color = tex2.a * albedo_color * EmissiveColor.rgb; // tex2.a carries emissive mask #endif // Refraction path — depth-aware UV distortion #if SHADER_REFRACTION float2 refract_uv = screen_uv + normal.xy * RefractionStrength; float refract_depth = SampleDepth(mgDepthBufferSampler, refract_uv); // fall back to unrefracted if behind surface refract_uv = refract_depth > input_depth ? refract_uv : screen_uv; #endif
default.hlsl but with different structure. material.hlsl in both engines is functionally identical except the new engine uses ToLinear() instead of srgb2linearf3() for albedo conversion — these are equivalent. The old default.hlsl did not have IMPORT_FROM_UE4, SHADER_REFRACTION, or per-UV customization macros.
旧引擎(res/shader)差异:旧引擎也有 default.hlsl,但结构不同。两个引擎的 material.hlsl 功能上相同,只是新引擎用 ToLinear() 替代 srgb2linearf3()——这两者等价。旧版 default.hlsl 没有 IMPORT_FROM_UE4、SHADER_REFRACTION 或逐 UV 自定义宏。
Specialized Material Types专用材质类型
material_skin.hlsl — Pre-Integrated Skinmaterial_skin.hlsl — 预积分皮肤
Adds two LUT textures for pre-integrated skin shading. Metalness is hardcoded to 0 (skin is always dielectric). The key additions are the LUT samplers and SSS curvature readback:
为预积分皮肤着色添加两张 LUT 纹理。金属度硬编码为 0(皮肤始终是电介质)。关键增加是 LUT 采样器和 SSS 曲率回读:
// Additional textures: t3 = pre-integrated LUT, t4 = shadow color LUT Texture2D Tex3 : register(t3); // sampled at (0.5*NoL+0.5, curvature) Texture2D Tex4 : register(t4); // sampled at (shadow, curvature) SamplerState s1; // Trilinear_Clamp for LUTs float GetSSSCurvature(...) { return 1.0 - raw.tex2.b; } // inverted metalness channel float3 GetRawPreintergratedSkin(float2 uv) { return Tex3.Sample(s1, uv).rgb; } float3 GetRawSkinShadowColor(float2 uv) { return Tex4.Sample(s1, uv).rgb; }
material_hair.hlsl — Anisotropic Flow Mapmaterial_hair.hlsl — 各向异性流向图
Uses a 4th texture as a hair flow map (Tex3) that encodes the tangent direction along hair strands. The flow map drives the anisotropic tangent stored in mtl.tangent:
使用第 4 张纹理作为发丝流向图(Tex3),编码沿发丝方向的切线方向。流向图驱动存储在 mtl.tangent 中的各向异性切线:
// Tex3.xy = flow direction in [0,1] → [-1,1] // Tex3.z = blend weight between flow normal and geometric normal float3 flow = raw.tex3.xy * 2.0 - 1.0; float3 flow_tangent = normalize(mul(float3(flow, 0), TBN)); float3 final_tangent = normalize(flow_tangent + raw.tex3.z * Intensity.w * world_normal); mtl.tangent = final_tangent; // Hack: stash original world normal in sss_color for the hair shading model mtl.sss_color = original_world_normal;
material_toon.hlsl — Toon Step Parametersmaterial_toon.hlsl — 卡通阶梯参数
Toon materials hijack the sss_color field to pass step-function parameters to the toon.hlsl shading model. This avoids adding new fields to the Material struct:
卡通材质借用 sss_color 字段向 toon.hlsl 着色模型传递阶梯函数参数。这避免了向 Material 结构体添加新字段:
// Uniforms: toon_threshold, toon_range (the Lerp3 breakpoints) void GetSSSColor(...) { mtl.sss_color = float3(toon_threshold, toon_range, 0); // packed as color } // GetEmissive() uses albedo * intensity (glow from base color) mtl.emissive = mtl.albedo * Intensity.w * EmissiveColor.rgb;
material_ssss.hlsl — 4S Dual-Lobe SSSmaterial_ssss.hlsl — 4S 双波瓣次表面散射
The most parameter-rich material, supporting dual-lobe specular (for realistic skin highlight splitting) and physically-based SSS transmittance. Key extra uniforms:
参数最丰富的材质,支持双波瓣高光(实现真实皮肤高光分裂)和基于物理的 SSS 透射。关键额外 Uniform:
| UniformUniform | Content内容 |
|---|---|
| DualLobeSpecularParams | r=roughness0, g=roughness1, b=mix factor for two specular lobesr=粗糙度0,g=粗糙度1,b=两高光波瓣混合系数 |
| FalloffColor | SSS falloff color (per-channel absorption when light passes through skin)SSS 衰减颜色(光线穿过皮肤时的逐通道吸收) |
| TransmissionParams | r=SSS distribution width, g=normal scale, b=thickness quality, a=dielectric IORr=SSS 分布宽度,g=法线缩放,b=厚度质量,a=电介质 IOR |
| TransmissionTintColor | Tint color for transmitted (backlit) light透射(背光)光的着色颜色 |
Material File Map材质文件地图
- materials/material_common.hlslSingle helper:
MakeMaterialRoughness()=saturate(r); defines NEOX_IS_IGL default单一辅助函数:MakeMaterialRoughness();定义 NEOX_IS_IGL 默认值 - materials/material.hlslHandwritten standard PBR — 3 textures, separate Get*() functions手写标准 PBR — 3 张纹理,独立 Get*() 函数
- ../default.hlsl + default.surfMaterial-graph compiled PBR — monolithic CalcMaterialParam(), 4 textures, macro features材质图编译 PBR — 单体 CalcMaterialParam(),4 张纹理,宏功能
- materials/material_skin.hlslPre-integrated skin — 5 textures (adds 2D LUTs for subsurface), metalness = 0预积分皮肤 — 5 张纹理(添加次表面 2D LUT),金属度=0
- materials/material_hair.hlslAnisotropic Marschner hair — 4 textures (adds flow map), custom tangent pipeline各向异性 Marschner 头发 — 4 张纹理(添加流向图),自定义切线管线
- materials/material_toon.hlslToon/cel-shaded — packs step params into sss_color; includes toonedge_helper.hlsl卡通着色 — 将阶梯参数打包到 sss_color;包含 toonedge_helper.hlsl
- materials/material_toonedge.hlslSilhouette outline pass — vertex extrusion along normal by SilhouetteEdgeWidth轮廓描边 Pass — 沿法线按 SilhouetteEdgeWidth 挤出顶点
- materials/material_ssss.hlsl4S separable SSS — dual-lobe spec, FalloffColor, TransmissionParams, UE4 normal import4S 可分离 SSS — 双波瓣高光,衰减颜色,透射参数,UE4 法线导入
- materials/material_subsurface.hlslSimple single-sided SSS — curvature from tex2.a, color derived from albedo简单单面 SSS — 从 tex2.a 读取曲率,颜色从反照率导出
- materials/material_multi_layered_pbr.hlsl1-coat layered PBR — 5 textures (+FGD LUT t4, coat normal t5)1 涂层分层 PBR — 5 张纹理(+FGD LUT t4,涂层法线 t5)
- materials/material_multi_layered_pbr2.hlsl2-coat layered PBR — 6 textures (+2 coat normals t5-t6)2 涂层分层 PBR — 6 张纹理(+2 涂层法线 t5-t6)
- materials/material_multi_layered_pbr3.hlsl3-coat layered PBR — 7 textures (+3 coat normals t5-t7)3 涂层分层 PBR — 7 张纹理(+3 涂层法线 t5-t7)
- materials/material_terrain.hlslTerrain blend material — multi-layer splat mapping地形混合材质 — 多层溅射贴图
- materials/material_glint.hlslGlint material setup — feeds GlintDictionary texture and slope-space params to glint.hlsl闪光材质设置 — 向 glint.hlsl 提供 GlintDictionary 纹理和斜率空间参数
default.hlsl replaces the old version — adding IMPORT_FROM_UE4, SHADER_REFRACTION, per-UV customization macros. material.hlsl is nearly identical between both directories (only srgb2linearf3() → ToLinear() rename). New material files added in res_upgrade/: material_ssss.hlsl (4S dual-lobe), material_subsurface.hlsl, material_multi_layered_pbr*.hlsl (3 variants), material_glint.hlsl. All old materials in res/shader/built_in/materials/ remain accessible via fallback.
新版替换:新引擎 default.hlsl 替换旧版——新增 IMPORT_FROM_UE4、SHADER_REFRACTION、逐 UV 自定义宏。material.hlsl 在两个目录间几乎相同(仅 srgb2linearf3()→ToLinear() 重命名)。res_upgrade/ 中新增材质文件:material_ssss.hlsl(4S 双波瓣)、material_subsurface.hlsl、material_multi_layered_pbr*.hlsl(3 个变体)、material_glint.hlsl。res/shader/built_in/materials/ 中的所有旧材质通过回退机制仍然可用。