新闻详情

新闻详情

首页 / 资讯中心 / 详情

Unity PBR实战:丝绸材质各向异性高光Shader实现

发布时间:2026/9/19 11:12:11来源:尧图网络
Unity PBR实战:丝绸材质各向异性高光Shader实现
1. 丝绸材质为什么是PBR渲染里的硬骨头做渲染的朋友应该都有体会调一个普通的塑料、金属、石头材质用标准PBR流程很快就能出效果但一旦碰上丝绸、缎面、头发丝这类东西标准PBR那套“漫反射高光”的公式就明显不够用了。核心问题就出在各向异性高光上。丝绸表面不是光滑一致的它由无数根细长的纤维平行排列组成光线打在纤维上会沿着纤维方向发生定向散射呈现出一条垂直于纤维走向的细长高光。这种“高光被拉成一条线”的效果是标准PBR模型比如GGX各向同性分布完全模拟不出来的。你硬要用普通高光去调最多调出一个“泛着油光的塑料布”而不是丝绸那种随着角度变化、光泽像流水一样滑动的高级感。这篇文章就围绕“Unity PBR实战”这个方向讲清楚怎么从零实现丝绸材质的各向异性高光。内容包括各向异性PBR的物理原理、方案选型HDRP的Lit自带各向异性以及URP/Built-in管线手写Shader、实际Shader代码和参数调优、还有我在项目里踩过的坑。适合对Unity Shader有一定基础、想做写实布料或角色衣服效果的开发者也适合渲染管线的初学者了解背后“为什么要这么做”。我先把结论放在前面丝绸材质能不能出彩高光形状的判断比颜色重要得多。你不需要把漫反射调得有多复杂关键是把各向异性高光这根“线”立起来、动起来材质质感立刻就会不一样。下面我们一块块拆开讲。1.1 从“哑光丝绸”误区讲起高光形状才是灵魂新手调丝绸最容易掉进去的坑是把它当成“稍微亮一点的粗糙布料”。于是先拉低粗糙度、加一点高光强度再让颜色偏灰白看起来确实有点丝滑但本质上还是个普通材质。这种材质放到场景里光照一变高光区域一团糊完全没有丝绸纤维反光的纹理感。真正的丝绸从微观结构看就是大量平行排列的超细纤维。每根纤维都是一个圆柱体光在圆柱面上会发生单向反射顺着纤维方向看反射光散开成一条亮线垂直纤维方向看反光则被压得很窄。这条亮线的朝向和宽度就是各向异性高光的关键控制项。所以调丝绸时你要关注的不是“高光圆不圆、亮不亮”而是“高光是不是一条线、这条线跟随表面弯曲怎么流动”。有些花瓣裙、连衣裙角色只要给高光增加一点点各向异性拉伸材质立刻就从“棉布”变成了“绸缎”。这一步的性价比非常高。1.2 为什么标准PBR模型处理不了丝绸标准PBR我们常说的基于物理的渲染Physically Based Rendering默认假设表面微表面是统计学上各向同性的——也就是说不管从哪个方向看微表面的法线分布都一样。这种假设对金属、塑料、石块很准但对丝绸、头发、拉丝金属这类有明确方向性的材料就不成立了。在渲染方程里这体现在法线分布函数Normal Distribution FunctionNDF上。标准GGX法线分布只有一个粗糙度参数高光形状是圆形的而各向异性GGX把粗糙度拆成了两个维度一个沿切线方向、一个沿副切线方向于是高光就能被拉成椭圆甚至细线。这就是“策略”上的差别同样调用PBR管线但需要针对不同材质选择不同的微表面分布模型。换句话说丝绸材质至少需要三样东西才能成立——明确的切线方向Tangentin、两个方向不同的粗糙度、一个能处理细长高光的NDF。这三样缺一不可缺了任何一样效果都会退化成普通PBR或假丝绸。2. 各向异性高光的核心原理与方案选型2.1 各向异性GGX管线的数学直觉各向异性GGX其实并不复杂。在标准GGX里高光形状由粗糙度α控制α越大高光越扩散。各向异性版本把α拆成两个数值沿切线方向的粗糙度和沿副切线方向的粗糙度。如果前者远小于后者高光就会沿着切线方向拉伸形成细长亮线。这里给一个直觉类比想象一把刷子刷过墙面刷毛的走向就是切线方向。垂直于刷毛方向看墙面很粗糙光线到处乱弹沿着刷毛方向看墙面又相对平滑光线集中。各向异性高光做的就是这件“方向感知”的事。在实际Shader代码中各向异性GGX的NDF通常长这样HLSL伪代码简化// T: 切线方向, B: 副切线方向, H: 半角向量 float anisotropicGGX(float NoH, float3 H, float3 T, float3 B, float ax, float ay) { float ToH dot(T, H); float BoH dot(B, H); float a2 ax * ay; float3 v float3(ay * ToH, ax * BoH, a2 * NoH); float v2 dot(v, v); float w2 a2 / v2; return a2 * w2 * w2 / PI; }这个函数的核心就是分别把半角向量投影到切线方向和副切线方向乘上对应的粗糙度系数最后算出细长分布。你只要理解了这一层后面调参就很容易ax控制高光沿切线拉伸的程度ay控制垂直切线方向的收拢程度。丝绸一般ax值小0.05~0.15ay值大0.3~0.6高光就会呈现明显的“丝线感”。2.2 引擎方案选型HDRP Lit / URP自写 / Built-in自写知道了原理还得选落地路径。三个方面我实际都试过简单说下适用场景。HDRP Lit内置各向异性HDRP的Lit材质面板自带Anisotropy选项打开就能调各向异性强度和方向。适合快速验证、风格化写实项目省去了写Shader的功夫。缺点是比较吃性能HDRP管线本身对移动端不太友好且粒子、UI等系统都要做适配。URP自写ShaderUnity 2021以后URP成为主流自写一套带各向异性高光的Lit Shader工作量适中可以精确控制效果和性能适合多平台发布。后文中我会给出一个可用的URP版本。Built-in管线自写老项目还在用内置管线的话也能写但光照模型要自己接效果不如URP/HDRP顺手。如果一开始能迁移到URP我建议直接迁移。从我在项目里的经验来看如果做PC或主机端的写实角色直接上HDRP最省心。如果做移动端、小程序或者需要高度定制URP手写是更优解。手写的好处不只是性能可控还能把丝绸的“纤维层”效果做得比内置更精致这个下面一章细讲。3. 实操过程手写一个各向异性丝绸Shader3.1 环境准备与基础Shader结构我用URP为例环境是Unity 2022.3 LTS URP 14。新建一个标准Shader命名为SilkAnisotropic然后从URP的Lit模板改起保留PBR通用的漫反射和间接光部分只把高光BRDF替换成各向异性GGX。Shader基础结构大致包括Shader Custom/SilkAnisotropic { Properties { _BaseMap (Base Texture, 2D) white {} _BaseColor (Base Color, Color) (0.9, 0.85, 0.8, 1) _SmoothnessA (Smoothness Along Tangent, Range(0.01, 1)) 0.8 _SmoothnessB (Smoothness Along Bitangent, Range(0.01, 1)) 0.3 _AnisotropyTex (Anisotropy Control (R), 2D) white {} _TangentRotation (Tangent Rotation, Range(-1, 1)) 0 _SpecularColor (Specular Color, Color) (1, 1, 1, 1) } SubShader { Tags { RenderType Opaque RenderPipeline UniversalPipeline } Pass { HLSLPROGRAM // ... 顶点着色器、片元着色器 ENDHLSL } } }要特别说明一下这里的_SmoothnessA和_SmoothnessB它们对应前面说的粗糙度两个方向。命名上我习惯用“沿切线”“沿副切线”而不是“U向”“V向”因为后两者容易和UV坐标混淆。材质面板上把沿切线方向调大高光会像丝绸一样呈现细线如果两个值接近材质就退化回普通高光。3.2 切线方向控制与切线扰动各向异性材质最容易被忽略的就是切线方向。对于丝绸织物的纤维走向就是切线方向。理想情况下一张平铺的丝绸切线方向应该是模型UV的U或者V方向。但现实中的衣服UV展开往往不是横平竖直直接取模型切线可能和布纹对不上。我这里有两种常用控制方法基于UV方向重建切线在顶点着色器里用float3 tangent normalize(float3(0, 0, 1))这类固定方向适合平面布料。对曲面物体可以从UV导数的叉积重建但需要额外的网格数据支持。用切线贴图控制方向在切线空间里把方向扰动写进贴图然后乘以模型切线得到世界空间方向。这种方式最适合丝绸这类方向受裁剪、缝线影响的布料。贴图R通道存方向偏移量数值0表示沿原始切线1表示旋转到副切线方向中间值做平滑插值。另外强烈建议加一个_TangentRotation参数方便在材质面板直接旋转高光方向。这个参数可以这样用把切线向量和副切线向量做一次旋转混合float3 rotatedTangent normalize(lerp(tangentWS, bitangentWS, saturate(_TangentRotation)));这比改变贴图坐标方向直观得多。美术在调衣服材质的时候不用回去改贴图直接在面板上拖旋转就能看到高光在丝面上滑动效率很高。3.3 各向异性GGX高光实现代码接下来是重头戏片元着色器里的各向异性高光实现。这里我用URP自带Lighting.hlsl的结构提取主光源和半球光照替换BRDFfloat3 DirectBDRF_Anisotropic(float3 lightDir, float3 viewDir, float3 normalWS, float3 tangentWS, float3 bitangentWS, float ax, float ay, float3 specColor) { float3 halfVec normalize(lightDir viewDir); float NoH saturate(dot(normalWS, halfVec)); float NoV saturate(dot(normalWS, viewDir)); float NoL saturate(dot(normalWS, lightDir)); float ToH dot(tangentWS, halfVec); float BoH dot(bitangentWS, halfVec); // 各向异性NDF float a2 ax * ay; float3 v float3(ay * ToH, ax * BoH, a2 * NoH); float v2 dot(v, v); float D a2 * a2 * a2 / (PI * v2 * v2); // 可见性项简化版Smith float ToV dot(tangentWS, viewDir); float BoV dot(bitangentWS, viewDir); float ToL dot(tangentWS, lightDir); float BoL dot(bitangentWS, lightDir); float lambdaV NoL * length(float3(ax * ToV, ay * BoV, NoV)); float lambdaL NoV * length(float3(ax * ToL, ay * BoL, NoL)); float V 0.5 / (lambdaV lambdaL 1e-5); return D * V * specColor * NoL; }这段代码里有几个地方值得说明。首先是NDF里的a2 * a2 * a2如果你把各向异性公式的推导展开会发现分子是ax * ay的三次幂原因在于正常的GGX分子是α²而各向异性版本中α变成了两个方向参数的乘积再乘一个归一化系数。我直接写成立方是为了配合分母的归一化如果觉得记不住就按这个公式抄实测效果和知名开源实现一致。其次是可见性项V我这里用的是简化版Smith高度相关公式没有上完整的G2因为在大多数光照条件下简化和完整版的视觉差异很小性能却能省不少。如果你做的是离线渲染、追求极致物理正确可以换上完整版但实时渲染里这一步是值得优化的。最后是漫反射部分丝绸的漫反射其实也有一点各向异性但视觉贡献远不如高光大所以我直接沿用了URP自带的漫反射模型没有额外做文章。如果你想让丝绸的底色也有细微的丝光变化可以在漫反射上叠加一点基于切线方向的菲涅尔项但这是后话。3.4 通过纹理贴图控制高光拉伸只靠两个粗糙度参数整块布料的高光形态是固定的这在真实衣服上会导致一个问题袖子、领口、裙摆这些区域的纤维走向不一样高光方向应该跟着布纹走。解决办法就是给材质加一张各向异性控制贴图我习惯简称为AO贴图或者方向贴图存进_AnisotropyTex。具体做法是R通道控制各向异性的强度0表示纯各向同性1表示完全各向异性。G通道控制切线旋转量0表示保持原切线0.5表示旋转45度1表示转90度。B通道用来控制丝绸表面的纤维噪波可以叠加到粗糙度上模拟细小的纤维高低起伏。这样贴图不仅能控制高光的拉伸程度还能控制高光在布料表面“流动”的路径。在裙摆和袖口接缝处配合手绘贴图把G通道改成旋转量高光就会跟着衣纹走看起来很自然。下面是我在材质面板里用到的三张贴图建议贴图通道实际用途数值范围调试建议R各向异性强度0~1丝绸0.6-1.0缎面0.2-0.5G切线方向旋转0~10为不变0.5为45度偏转B纤维噪波叠加0~1叠加到粗糙度B增加高光条带感3.5 参数整理与质感调优写到这里Shader主体已经能跑起来了。我再给一组实用参数是我在角色衣服和布料道具上实测比较稳妥的起始值参数范围丝绸推荐值缎面推荐值备注Base Color0~1(0.85, 0.8, 0.75)(0.9, 0.85, 0.8)丝绸颜色偏暖灰Smoothness沿切线0.01~10.80.75控制高光沿纤维方向长度Smoothness垂直切线0.01~10.250.45控制高光细宽度Anisotropy强度0~10.90.5越大丝绸感越强Specular Color0~1白色/淡金白色金属丝线可调成金色Tangent Rotation-1~1按需按需旋转高光方向如果高光看起来像玻璃一样的“硬边”多半是垂直切线方向的Smoothness开太高了。丝绸的高光虽然是一条线但这条线边缘应该柔和有丝绒般的过渡。解决办法是把垂直切线方向的Smoothness降低到0.2~0.3同时把沿切线方向的Smoothness控制在0.75~0.9之间高光才会是一条“柔和的光带”。4. 常见问题与排查技巧实录4.1 高光方向错乱 / 法线贴图导致切线异常这是做各向异性材质最常见的问题。明明切线方向看着没问题渲染出来高光却像被挠乱的毛线一样一条一条到处乱指。这种情况十有八九是法线贴图把切线扰动了。法线贴图在Shader里会通过tangentToWorld这个矩阵把法线从切线空间变换到世界空间但很多实现没有同步修正切线方向。当你同时对同一个模型使用法线贴图和各向异性高光时需要保证法线采样后的切线空间矩阵是一致的。否则高光的参考坐标系和法线不一致高光方向自然就乱了。我的解决办法是在Shader里额外传入一份原始切线不参与法线贴图扰动单独用来算各向异性方向。这样哪怕法线贴图微调了表面法线各向异性高光的方向依旧稳定。具体做法是给顶点着色器增加一个TEXCOORD1用来存放未扰动的切线方向片元着色器里直接用这个方向计算ToH和BoH。如果你不想改Shader还有一个笨但有效的办法把法线贴图对切线的影响压到最低或者在美术资源层面把法线贴图勾选“Mip Streaming”和“sRGB”等选项调为正确状态减少因贴图压缩产生的切线扰动。4.2 高光过于离散或出现条带另一个常见问题是高光看起来像一条一条断裂的线而不是连续的光带。这种情况通常不是数学公式的问题而和采样精度、Mipmap、半精度浮点有关。各向异性GGX的NDF对数值特别敏感尤其当粗糙度很小、高光很尖锐时半精度(float16)会在大尺度光照下产生明显的条带化。解决方法是优先在移动端使用half类型时把ax和ay的最小值限制在0.05以上。在片元着色器里关键计算尤其是v2和D使用float精度不要用half。计算高光时把视角方向和半角向量做一次normalize避免累积误差。另外如果高光在远距离观察时出现闪烁或摩尔纹需要给高光增加一个固定宽度的阈值下限。比如当NoH非常接近0时直接返回0避免极窄高光造成的亚像素闪烁。4.3 性能优化与移动端注意事项各向异性高光的计算量比标准PBR高一些因为多算了一组切线、副切线相关的向量点积。移动端如果大面积使用丝绸材质需要注意几点减少额外纹理采样各向异性控制贴图可以复用现有材质贴图的某个通道不用额外增加一张新贴图。控制动态分支不要在片元着色器里根据光照动态切换各向异性和各向同性分支建议通过材质属性在编译期决定。使用带Static关键字的贴图采样器把贴图绑定为SamplerState减少运行时状态切换。顶点着色器里的切线方向变换尽量在对象空间预计算避免每帧在世界空间做矩阵运算。我实测过同款效果在手机上的差异一个PBR丝绸Iphone 12级别的设备可以稳定60帧但开了动态阴影后高光部分会有明显性能抖动。如果项目确实需要高光表现优先级应为“高光质量 阴影质量 后处理”因为丝绸材质的表现力主要靠高光阴影稍微粗糙一点不影响观感。4.4 从无PBR时代迁移过来的材质适配思路最后聊一下没有PBR之前老项目里丝绸材质是怎么做的。这个思路对迁移老项目的人很有用。那时候最常用的是Blinn-Phong高光再叠加一个“Anisotropic高光偏移”就是在计算高光时把半角向量稍微向切线方向偏移一点。效果上确实能模拟出“高光被拉长”的观感但问题在于光照变化时高光形状容易扭曲而且很难和金属、光滑度统一调节。从老材质迁移到PBR各向异性时最核心的变化是之前你调的是“高光偏移量”现在要调成“两个方向粗糙度”。美术习惯上很不一样。我的建议是迁移时先保持旧版视觉参数做一个对照测试旧材质参数新PBR各向异性参数备注高光强度Specular Color亮度直接对应高光偏移量Tangent Rotation旋转方向高光锐度Smoothness沿切线高光长度高光宽度Smoothness垂直切线高光细度布纹贴图强度Anisotropy强度各向异性权重按这个对照表来调美术不用推翻重来通常半小时内就能把老项目的丝绸材质迁移到新PBR管线效果还能比旧版好不少。我在实际项目中还发现一个小技巧把各向异性强度做成可一张黑白贴图R通道控制衣服褶皱处多画一些深色区域高光在这些地方就会稍微变圆润模拟纤维被挤压后散乱的效果。这个细节让丝绸材质在静态效果图上看起来特别“贵”很多demo截图里能用上。最后再分享一个我自己踩过的坑各向异性方向和高光必须跟着最终光照方向走如果材质给的是点光源或聚光灯高光的形状会比平行光柔和很多。做场景布置时想让丝绸材质展示出最迷人的效果最好用方向光配合环境反射探头而不是单点强光源。否则再精细的各向异性计算也救不回来一团死板的高光。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

MDPI投稿状态全解析:11个状态含义、时间线与催稿技巧 2026/9/19 14:48:43

MDPI投稿状态全解析:11个状态含义、时间线与催稿技巧

1. 投稿状态到底在说什么第一次往MDPI旗下期刊投论文的人,十有八九会被投稿系统里那一串状态搞得心里七上八下。Submitted、Under Review、Pending Decision、Accepted……每个词都认识,但连在一起就不知道到底进展到哪一步了。更让人焦虑的是&#xff0…

阅读更多 →
SpringBoot+Android民宿预订系统从零到答辩全指南 2026/9/19 14:48:43

SpringBoot+Android民宿预订系统从零到答辩全指南

简介:一份基于Spring Boot与Android平台的民宿预订系统毕业论文文档,面向计算机相关专业毕业生以及需要完成课程设计或毕业设计的开发者。内容系统阐述了民宿预订系统的设计目的、需求分析、总体架构与实现方案,重点涉及Spring Boot框架选型、…

阅读更多 →
ValidX校验库集成指南:Maven/Gradle构建与镜像配置 2026/9/19 14:48:43

ValidX校验库集成指南:Maven/Gradle构建与镜像配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
RocksDB 文档站深度指南:docs 目录 Jekyll 站点的结构、配置与定制方法 2026/9/19 14:48:43

RocksDB 文档站深度指南:docs 目录 Jekyll 站点的结构、配置与定制方法

RocksDB 文档站深度指南:docs 目录 Jekyll 站点的结构、配置与定制方法 【免费下载链接】rocksdb A library that provides an embeddable, persistent key-value store for fast storage. 项目地址: https://gitcode.com/gh_mirrors/ro/rocksdb 本文围绕 Ro…

阅读更多 →
x64dbg serun/sego 命令详解:吞掉异常并继续运行调试器 2026/9/19 14:48:43

x64dbg serun/sego 命令详解:吞掉异常并继续运行调试器

x64dbg serun/sego 命令详解:吞掉异常并继续运行调试器 【免费下载链接】x64dbg An open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis. 项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg 导读 ser…

阅读更多 →
塑料检测标准体系与实操方法:从ISO/ASTM到数据追溯全解析 2026/9/19 14:45:43

塑料检测标准体系与实操方法:从ISO/ASTM到数据追溯全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞