Unity URP管线Linear空间丝绸材质

效果展示

丝绸

简介

在 URP 中实现丝绸材质:各向异性 GGX + 织纹微法线 + Fuzz 边缘绒光的线性工作流实践

丝绸的真实感之所以难做,本质原因不在"颜色像不像",而在于它的高各向异性高光织物结构导致的微观法线扰动 、以及视角相关的绒毛散射(fuzz / sheen)。传统的金属度/粗糙度 PBR 模型偏向各向同性(isotropic)表面,直接用在丝绸上通常会得到"塑料布"的观感:高光不成条带、缺乏经纬纹理的闪烁(glint),更没有丝织物特有的柔和边缘亮。

本文基于一个 URP ForwardLit 自定义 Shader(Custom/SilkPBR_Linear),拆解其核心结构:各向异性 GGX BRDFThread 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)上

申明

因涉及知识版权等原因,无法上传源材质 请凉解!!!

相关推荐
天人合一peng1 天前
unity 生成标记根据背景色标记变色
unity·游戏引擎
天人合一peng2 天前
unity 生成标记根据背景色变色为明显的颜色
unity·游戏引擎
魔士于安2 天前
Unity 超市总动员 超市收银台 超市货架 超市购物手推车 超市常见商品
游戏·unity·游戏引擎·贴图·模型
CandyU22 天前
Unity —— 数据持久化
unity·游戏引擎
zh路西法2 天前
【Unity实现Oneshot胶卷显形】游戏窗口化与Win32API的使用
游戏·unity·游戏引擎
迪捷软件2 天前
显控系统虚拟仿真的工程化路径
游戏引擎·cocos2d
凡情2 天前
android隐私合规检测
android·unity
小贺儿开发2 天前
Unity3D 本地 Stable Diffusion 文生图效果演示
人工智能·unity·stable diffusion·文生图·ai绘画·本地化
Swift社区2 天前
传统游戏引擎 vs 鸿蒙 System 架构
架构·游戏引擎·harmonyos
mxwin3 天前
Unity Shader 半透明物体为什么不能写入深度缓冲?
unity·游戏引擎·shader