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)上

申明

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

相关推荐
哈小奇2 小时前
Unity URP管线Linear空间下玻璃效果
unity·游戏引擎
极客柒7 小时前
Unity 大地图高性能砍树顶点动画Shader
unity·游戏引擎
avi911111 小时前
UnityProfiler游戏优化-举一个简单的Editor调试
游戏·unity·游戏引擎·aigc·vibe coding·editor扩展
学嵌入式的小杨同学11 小时前
C 语言实战:动态规划求解最长公共子串(连续),附完整实现与优化
数据结构·c++·算法·unity·游戏引擎·代理模式
学嵌入式的小杨同学14 小时前
顺序表(SqList)完整解析与实现(数据结构专栏版)
c++·算法·unity·游戏引擎·代理模式
程序猿多布14 小时前
HybridCLR热更打包后AOT泛型函数实例化缺失处理
unity·hybridclr·aot generic
平行云15 小时前
实时云渲染支持数字孪生智能工厂:迈向“零原型”制造
人工智能·unity·ue5·云计算·webrtc·制造·实时云渲染
dzj202115 小时前
Unity中使用LLMUnity遇到的问题(一)
unity·llm·llmunity
Howrun77716 小时前
虚幻引擎 C++ 制作“射击FPS游戏“
游戏·游戏引擎·虚幻