Unity URP管线Linear空间丝绸材质
丝绸/URP/Linear/Unity
效果展示
丝绸
简介
在 URP 中实现丝绸材质:各向异性 GGX + 织纹微法线 + Fuzz 边缘绒光的线性工作流实践
丝绸的真实感之所以难做,本质原因不在“颜色像不像”,而在于它的高各向异性高光、织物结构导致的微观法线扰动、以及视角相关的绒毛散射(fuzz / sheen)。传统的金属度/粗糙度 PBR 模型偏向各向同性(isotropic)表面,直接用在丝绸上通常会得到“塑料布”的观感:高光不成条带、缺乏经纬纹理的闪烁(glint),更没有丝织物特有的柔和边缘亮。
本文基于一个 URP ForwardLit 自定义 Shader(Custom/SilkPBR_Linear),拆解其核心结构:各向异性 GGX BRDF、Thread Map 驱动的织纹微结构、Roughness 变异与闪烁增强、以及 Fuzz 对镜面项的抑制,并重点讨论线性/伽马空间处理对最终外观与能量一致性的影响。
总体
图中渲染的是各向异性高光,呈环形,在头发渲染中又称为“天使环”,这里使用的光照模型是Kajiya-Kay Model。在很多游戏中,头发渲染都使用了Kajiya-Kay Model
结构:在 URP Forward Pass 里手写 PBR
该 Shader 使用 URP 的 Core.hlsl / Lighting.hlsl / GlobalIllumination.hlsl,但并没有直接调用 URP 的 UniversalFragmentPBR,而是自己计算:
- 主光/附加光的直接光照(Direct)
- SH 的漫反射环境光(Diffuse GI)
- Reflection Probe 的镜面环境反射(Specular GI)
- AO 在 GI 混合阶段进行整体调制
这类结构的好处是:你可以对丝绸这种“非标准材质”插入更多艺术/物理混合项(如 thread glint、fuzz 权重等),而不受标准 Lit 限制。
核心算法

- BaseMap + BaseColor:基础反照率(漫反射颜色)。
- NormalMap + NormalScale:法线扰动(宏观形状起伏)。
- RoughnessMap(R) + Roughness:感知粗糙度(perceptual roughness)。
- ThreadMap(RGB):织纹结构图(既用来“改变粗糙度”,也用来“做高光 glint”与“微法线扰动”)。
- OcclusionMap(R) + Occlusion:AO 乘到 GI(间接光)上。
- Specular + SpecTint:镜面 F0 的强度与颜色(丝绸会偏有色高光)。
- Aniso + AnisoRotation:各向异性强度与方向旋转(决定高光拉伸方向)。
- FuzzMap(R) + FuzzStrength:绒毛遮罩,主要用来削弱镜面(布料的“雾化/绒感”)。
核心 BRDF:各向异性 GGX(Anisotropic GGX)
float D_GGX_Aniso(float3 N, float3 H, float3 T, float3 B, float ax, float ay)
{
float NoH = saturate(dot(N, H));
float ToH = dot(T, H);
float BoH = dot(B, H);
float denom = (ToH*ToH)/(ax*ax) + (BoH*BoH)/(ay*ay) + NoH*NoH;
return 1.0 / (PI * ax * ay * denom * denom + 1e-6);
}

这里的 ax/ay 本质是在切线 T 与副切线 B 两个方向上使用不同的粗糙度,从而把高光“拉长”。
aspect = sqrt(1 - 0.9 * abs(aniso)) 用于把标量粗糙度 alpha 拆成 ax/ay,并允许 aniso < 0 时交换轴向(把条纹方向旋转 90°),这在“经纬交错”的丝织物调参时很实用。
它用的是各向异性版本的微表面模型:
- 法线分布函数 D:
D_GGX_Aniso(N,H,T,B, ax, ay)
用切线 T/副切线 B 两个方向不同的粗糙度(ax, ay)让高光沿某方向拉长。 - 几何遮蔽项 G:
G_Smith_Aniso(...)
Smith 形式的各向异性遮蔽。 - 菲涅尔 F:
F_Schlick_coat(F0, VoH)
Schlick 近似:F = F0 + (1-F0)*(1-VoH)^5
镜面项大体是:
float3 spec = (D * G) * F / max(1e-4, 4.0 * NoV * NoL) * NoL;
各向异性参数如何来?
先把 roughness 转成 alpha = perceptualRoughness^2
用 _Aniso 计算一个 aspect,再得到:
ax = alpha / aspect
ay = alpha * aspect
_AnisoRotation 会旋转 T/B,用来控制“丝绸高光走向”。
织纹 ThreadMap:做了三件事(这就是“丝绸味道”的关键)
A. 织纹驱动粗糙度变化(让明暗更“布料”)
threadH = average(threadRGB)
roughVar = (threadH*2-1) * _ThreadRoughnessVar
perceptualRoughness = roughMap*_Roughness + roughVar
效果:织纹亮/暗会让局部更光滑/更粗糙,形成织物不均匀反射
B. 织纹导向的“glint”闪点增强(丝绸常见的亮丝)
glint = pow(threadH, 6)
spec *= (1 + glint * _ThreadDirStrength)
效果:threadH 高的地方高光更“闪”、更集中,模拟丝线反射。
C. 用 thread 的屏幕导数做“微法线扰动”(假装有细小凹凸)
dhdx = ddx(threadH); dhdy = ddy(threadH);
N = normalize(N + (-dhdx*T0 - dhdy*B0) * microStrength);
这一步很关键:它不是普通 normal map,而是把 threadH 当高度,用屏幕空间导数近似高度场梯度,给法线增加微小扰动,让高光更碎、更像纤维织纹。
绒毛 Fuzz:主要在“压镜面”,让布料更柔
fuzzMask = fuzzTex * fuzzStrength
specWeight = lerp(1, 0 or 0.75, fuzzMask)
spec *= specWeight
- 主光:
lerp(1, 0, fuzzMask)(压得更狠) - 附加光/环境反射:
lerp(1, 0.75, fuzzMask)(压得没那么狠)
直观结果:越“绒”的区域镜面越少、越像布面泛白/柔和。
光照合成:主光 + 多灯 + GI(SH漫反射 + 环境镜面)
- 直射光:对每个灯计算
- 漫反射:
diff = (1-F) * albedo/PI * NoL - 镜面:各向异性 GGX spec,再叠加 glint、silkSpecBoost、再被 fuzz 抑制
- 漫反射:
- 间接漫反射:
SampleSH(N) * albedo - 间接镜面:
GlossyEnvironmentReflection(R, perceptualRoughness)再乘 Schlick(F0, NoV) - AO:只乘到间接项(diffuseGI+specGI)上
申明
因涉及知识版权等原因,无法上传源材质 请凉解!!!
这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!
更多推荐



所有评论(0)