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() 调用中解析。两种路径都向同一下游管线输出。

+===========================================================+ | NeoX Material Pipeline | +===========================================================+ | | | Artist-facing path | | +-----------------+ +------------------+ | | | .surf (XML) | | Material Graph | | | | LightingModel | | (editor nodes) | | | | Macro switches | +--------+----------+ | | | Texture slots | | compile | | +-------+---------+ v | | | default.hlsl | | | (CalcMaterialParam) | | v | | | material.hlsl (or) | | | GetAlbedo() material_skin.hlsl | | GetNormal() material_hair.hlsl | | GetRoughness() material_toon.hlsl | | GetMetalness() material_ssss.hlsl | | GetEmissive() material_terrain.hlsl | | GetOcclusion() ... | | GetSSSCurvature() | | | | | | | +------------+-------------+ | | v | | SetupMaterial(frag, raw, mtl) | | Material struct filled | | v | | Lighting() --- shading_models/isotropy.hlsl | | shading_models/skin.hlsl | | shading_models/hair.hlsl ... | +===========================================================+

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寄存器RGBA
Tex0t0Albedo (sRGB → linear)反照率(sRGB→线性)Opacity不透明度
Tex1t1Normal map (tangent space)法线贴图(切线空间)
Tex2t2AO / 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 模糊强度;地形材质可能存储混合权重。

Uniform modulation: The 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.albedoToLinear(tex0.rgb) * BaseColor.rgb
GetOpacity()mtl.opacitytex0.a * BaseColor.a
GetNormal()mtl.normalTBN decode, xy scaled by Intensity.xTBN 解码,xy 按 Intensity.x 缩放
GetRoughness()mtl.roughnesssaturate(tex2.g * Intensity.y)
GetMetalness()mtl.metalnesssaturate(tex2.b * Intensity.z)
GetSpecular()mtl.specular0.5 (hardcoded)
GetEmissive()mtl.emissiveEmissiveColor.rgb * Intensity.w
GetOcclusion()mtl.occlusiontex2.r
GetSSSCurvature()mtl.sss_curvatureempty stub (0)空桩(0)
GetSSSColor()mtl.sss_colorempty 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_UE4Use UE4-style normal map (Z reconstructed from XY; swizzled G channel)使用 UE4 风格法线贴图(Z 从 XY 重建;G 通道翻转)
EMISSIVE_TEXTURESample Tex3 as dedicated emissive map; multiply by EmissiveColor * Intensity.w采样 Tex3 作为专用自发光贴图;乘以 EmissiveColor * Intensity.w
SHADER_CUSTOMIZED_UV0..3Per-UV transform hooks — allows each texture to use independently transformed UVs逐 UV 变换钩子——每张纹理可使用独立变换的 UV
SHADER_LOCAL_OFFSETVertex position offset in object/local space (driven by texture)物体/局部空间的顶点位置偏移(由纹理驱动)
SHADER_WORLD_OFFSETVertex position offset in world space (WPO equivalent)世界空间的顶点位置偏移(相当于 WPO)
SHADER_REFRACTIONScreen-space refraction: distort UVs by depth buffer to simulate glass/water bending屏幕空间折射:通过深度缓冲扭曲 UV 模拟玻璃/水折射
USE_CLOUDGI_BAKECloud 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
Legacy engine (res/shader) difference: The old engine also has 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_UE4SHADER_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:

UniformUniformContent内容
DualLobeSpecularParamsr=roughness0, g=roughness1, b=mix factor for two specular lobesr=粗糙度0,g=粗糙度1,b=两高光波瓣混合系数
FalloffColorSSS falloff color (per-channel absorption when light passes through skin)SSS 衰减颜色(光线穿过皮肤时的逐通道吸收)
TransmissionParamsr=SSS distribution width, g=normal scale, b=thickness quality, a=dielectric IORr=SSS 分布宽度,g=法线缩放,b=厚度质量,a=电介质 IOR
TransmissionTintColorTint color for transmitted (backlit) light透射(背光)光的着色颜色

Material File Map材质文件地图

New vs replaced: The new engine's 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_UE4SHADER_REFRACTION、逐 UV 自定义宏。material.hlsl 在两个目录间几乎相同(仅 srgb2linearf3()ToLinear() 重命名)。res_upgrade/ 中新增材质文件:material_ssss.hlsl(4S 双波瓣)、material_subsurface.hlslmaterial_multi_layered_pbr*.hlsl(3 个变体)、material_glint.hlslres/shader/built_in/materials/ 中的所有旧材质通过回退机制仍然可用。