ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ShaderGraph法线向量节点深度解析:从原理到高级应用

ShaderGraph法线向量节点深度解析:从原理到高级应用 1. 项目概述为什么法线向量节点是ShaderGraph的基石在Unity ShaderGraph的世界里如果你问我哪个节点最容易被新手忽略却又在几乎所有高级材质效果中扮演着“无名英雄”的角色我会毫不犹豫地说是法线向量节点。乍一看这个节点平平无奇它不像那些花哨的PBR Master节点或者复杂的数学运算节点那样引人注目。但当你真正开始制作一个需要与光照、视角互动的材质时——无论是让岩石表面在斜射阳光下产生逼真的凹凸感还是让丝绸在角色移动时产生柔滑的光泽变化——你就会发现离开了对法线向量的精确控制一切都将无从谈起。这个节点就是ShaderGraph中连接“几何信息”与“光照计算”的桥梁。它输出的是当前着色点或者说你屏幕上的每一个像素点在模型表面上的朝向信息。这个信息是一个三维向量直接决定了光线如何从这个点反射出去进而决定了我们眼睛看到的颜色和明暗。今天我们就来彻底拆解这个看似简单、实则核心的Normal Vector Node。我会结合我过去在多个项目从风格化卡通到写实PBR中积累的经验不仅告诉你它是什么、怎么用更会深入分享那些官方文档里不会写的“骚操作”和“避坑指南”。无论你是刚接触ShaderGraph的新手还是想深化理解的老鸟这篇文章都能让你对法线向量的掌控力提升一个档次。2. 法线向量的核心原理从几何定义到Shader中的“方向代言人”2.1 什么是法线向量一个三维空间的“指南针”让我们先抛开代码和节点用最直观的方式来理解。想象你用手指轻轻触摸一个光滑的乒乓球。在指尖接触的那个点上球面是完美弯曲的。如果你在那个点放上一根无限小的、垂直于球面的牙签这根牙签所指的方向就是该点的法线方向。在三维计算机图形学中我们用一个从该点出发、长度为1的向量来表示这个方向这就是法线向量。它的三个分量 (X, Y, Z) 分别代表了在模型局部空间或世界空间中这个“垂直方向”在三个坐标轴上的投影。在ShaderGraph中Normal Vector节点就是获取这个信息的入口。它通常提供几种空间下的法线数据Object Space物体空间法线方向相对于模型自身的坐标系。模型无论怎么移动、旋转这个向量在模型本地是不变的。早期固定管线Shader常用现在更多用于一些特定计算。World Space世界空间法线方向相对于整个游戏世界的全局坐标系。这是最常用的模式因为光照计算如平行光、点光源通常在世界空间中进行。你需要知道表面在世界中“朝哪”才能算出光怎么照过来。View Space视图空间/相机空间法线方向相对于摄像机。常用于一些与视角相关的特效如边缘光Rim Light的简化计算。Tangent Space切线空间这是法线贴图技术的核心。这个空间下的法线是相对于三角形表面本身定义的Z轴大致是原始法线方向XY轴在三角面平面上。我们存储在法线贴图里的扰动信息就是切线空间下的法线向量。Normal Vector节点在Tangent Space模式下通常返回的就是经过法线贴图扰动后的最终法线这是实现凹凸细节的关键。注意很多新手会混淆“顶点法线”和“像素法线”。模型文件如.fbx中存储的是每个顶点的法线。在光栅化阶段这些顶点法线会被插值传递到每个像素或片元。ShaderGraph中的Normal Vector节点在片段着色器阶段执行因此它输出的是经过插值后的、当前正在处理的像素的法线向量。理解这一点对后续处理法线贴图至关重要。2.2 Normal Vector节点在ShaderGraph中的接口与行为在ShaderGraph面板中搜索并添加“Normal Vector”节点你会看到一个非常简洁的界面。它通常只有一个下拉菜单用于选择空间Space以及一个输出端口Output。这个输出端口的数据类型是Vector 3。它的核心行为可以概括为根据你选择的空间输出当前片段像素在该空间下的归一化长度为1法线向量。这里有一个极其重要的实操心得这个节点的输出默认是归一化的吗在Unity的URP通用渲染管线和内置管线的标准PBR Master节点连接中通常是的渲染管线内部会处理。但如果你是在自己构建一个自定义的光照模型或者将法线向量用于非光照计算比如用作方向遮罩我强烈建议你在使用前手动连接一个Normalize节点。因为法线向量在插值或经过某些数学运算后其长度可能会略微偏离1这会导致光照计算出现细微但可能被察觉的瑕疵尤其是在高光计算中。这是一个经典的“理论上应该没问题但实践中必须加保险”的环节。// 一个良好的习惯对从Normal Vector节点获取的向量进行归一化确保计算稳定。 float3 worldNormal normalize(NormalVector_WorldSpace);3. 核心应用场景拆解不止于光照很多人认为法线向量只和光照有关这大大低估了它的价值。下面我结合几个实际项目案例展示它的多元化应用。3.1 场景一PBR光照计算的基石这是最经典的应用。当你将World Space下的法线向量连接到PBR Master节点的Normal输入口时你就为整个基于物理的渲染方程提供了最关键的表面方向数据。过程详解获取基础法线从Normal Vector节点World Space获取初始世界法线。应用法线贴图扰动这才是展现功力的地方。你不会直接把基础法线连过去而是会先通过一个Normal From Texture节点采样法线贴图得到切线空间下的扰动向量。然后你需要一个Transform节点将切线空间向量转换到世界空间。关键步骤TBN矩阵重建这个转换需要模型的**切线Tangent和副切线Bitangent**信息。在ShaderGraph中你可以通过Tangent Vector节点同样选择World Space获取世界空间的切线。副切线可以通过叉乘cross(Normal, Tangent)计算得到。用这三个向量构建一个TBN矩阵然后用它去变换法线贴图采样的向量。不过更常见的做法是直接使用Transform节点选择From Tangent Space到To World SpaceShaderGraph会自动帮你完成这些矩阵运算。混合与连接将转换后的、带有细节扰动的世界空间法线连接到Master节点。避坑技巧法线贴图通常被标记为“Normal Map”类型Unity会对其进行正确的sRGB和压缩处理。在ShaderGraph中采样时务必使用Sample Texture 2D节点并将其Type设置为Normal。这样采样出来的向量才是正确的范围各分量在-1到1之间。如果你用普通的Sample Texture 2D可能会得到错误的结果。3.2 场景二构建自定义的菲涅尔与边缘光效果菲涅尔效应Fresnel Effect描述了物体表面在视线 grazing angle掠射角反射率增强的现象比如水池边缘看起来更亮。它的计算核心是视线方向与表面法线的点积。操作步骤获取View Direction视图方向从表面指向相机和World Normal。用Dot Product节点计算两者的点积。这个值在表面正对相机时接近1在边缘时接近0。用One Minus节点反转1 - dot(N, V)。这样值就在中心为0边缘为1。用一个Power节点对这个值进行幂运算来控制边缘效果的“硬度”或“过渡范围”。幂次越高边缘效果越锐利、范围越窄。最后用这个值去调制颜色或自发光强度。// 核心逻辑示意 float3 viewDir normalize(_WorldSpaceCameraPos - worldPosition); float3 worldNormal normalize(NormalVector_WorldSpace); float fresnel 1.0 - saturate(dot(viewDir, worldNormal)); fresnel pow(fresnel, _FresnelPower); // _FresnelPower是控制参数 float3 rimColor _RimColor.rgb * fresnel * _RimIntensity; // 将rimColor加到自发光或最终颜色上个人经验单纯用法线和视线做菲涅尔有时在复杂模型上显得平淡。一个高级技巧是在计算点积前先用一个Normal Strength节点就是你提供的参考资料中的那个节点轻微地“扭曲”一下法线。比如将强度设为0.8或1.2这会让菲涅尔效果的分布产生微妙而有趣的变化打破机械感让边缘光更“有机”。这正是Normal Strength节点的用武之地它通过调整法线贴图RG通道代表XY方向的扰动的强度来改变表面微观细节的“陡峭”程度从而影响宏观的光照和视觉效果。3.3 场景三基于法线的遮罩与区域划分这是法线向量在非光照领域非常强大的应用。你可以根据表面朝向的不同分配不同的材质属性。经典案例雪材质获取世界法线。我们只关心“朝上”的程度即法线向量的Y分量。使用Split节点将法线向量的Y值分离出来。对Y值进行Remap重映射。例如将[-1, 1]映射到[0, 1]。这样值越接近1表面朝上积雪应该越厚。用这个重映射后的值作为混合因子去Lerp线性插值岩石的基础颜色和雪的颜色或者混合粗糙度、法线贴图等。另一个案例三平面投影Triplanar Projection 当给地形或不规则低模贴图时UV可能扭曲。三平面投影技术分别从世界坐标的X、Y、Z轴方向投影纹理然后根据法线方向哪个轴的分量绝对值最大来混合这三个投影结果。这里法线向量直接决定了混合权重。// 三平面投影混合权重的简化计算 float3 weights abs(worldNormal); weights pow(weights, _BlendSharpness); // 提高对比度使混合更锐利 weights weights / (weights.x weights.y weights.z); // 归一化使权重和为1 // 然后分别用weights.x, weights.y, weights.z去混合从X, Y, Z方向投影采样的纹理颜色3.4 场景四顶点偏移与动态变形的方向依据在顶点着色器中我们有时需要沿着法线方向移动顶点来模拟膨胀、收缩或皮毛效果。虽然ShaderGraph的顶点阶段功能有限但在一些支持自定义顶点变形的场景如通过Position节点法线向量可以作为偏移方向。操作思路在Vertex通道中获取Object Space的法线向量因为顶点变换通常在物体空间开始。根据你的需求比如一张噪声图计算一个偏移量一个标量。将这个偏移量乘以法线向量得到一个方向和大小的偏移向量。将这个偏移向量加到原始的Position节点物体空间上。注意事项这种做法会改变模型的轮廓可能影响碰撞检测和阴影。通常用于视觉特效而非核心 gameplay 元素。4. 深度解析Normal Strength节点的妙用与内部机制你提供的资料提到了Normal Strength节点这正好是法线向量处理中一个至关重要的工具节点值得展开深入探讨。它绝不仅仅是“调整法线贴图强度”那么简单。4.1 节点功能精讲如其描述根据输入的Strength值调整输入法线贴图的强度。Strength为1时返回原值为0时返回“空白法线贴图”即(0,0,1)代表没有扰动的垂直向上法线。关键在于它如何定义“强度”看其生成的代码void Unity_NormalStrength_float(float3 In, float Strength, out float3 Out) { Out float3(In.rg * Strength, lerp(1, In.b, saturate(Strength))); }这段代码揭示了全部秘密In.rg * Strength它只缩放法线向量XY分量即切线空间下的水平扰动方向。这非常合理因为法线贴图的蓝色通道Z通常存储的是垂直分量扰动主要发生在XY平面。lerp(1, In.b, saturate(Strength))对于Z分量蓝色通道当Strength为0时它使用1即垂直向上当Strength为1时使用原始的In.b在中间值则进行插值。saturate保证了Strength被限制在[0,1]区间防止插值出错。这意味着什么这意味着Normal Strength节点在减弱强度时并不是简单地把整个向量缩短那会得到非单位向量而是智能地将法线朝着垂直方向(0,0,1)进行“拉回”。增强强度时Strength 1则是放大XY方向的扰动让表面在视觉上显得更“崎岖”。4.2 实战应用场景调和不同来源的法线细节你有一张高细节的石材法线贴图和一张低细节的砖墙法线贴图想混合它们。直接混合可能导致细节冲突。你可以先使用Normal Strength将其中一张的强度略微调低如0.7再进行混合这样主次会更分明混合结果更自然。距离相关的细节淡化LOD在远处过于强烈的法线细节会产生奇怪的闪烁高频细节的走样。你可以根据像素到相机的距离动态降低法线贴图的Strength让远处模型表面看起来更平滑这是一种性能与质量兼顾的简易LOD策略。制作可调节的“风化”或“磨损”效果用一个灰度图控制Strength参数。在磨损严重的区域灰度图白色将法线强度降低模拟表面被磨平的效果在保护完好的区域黑色保持原有法线强度。一个高级技巧有时法线贴图来自第三方资源其强度可能不符合你的项目艺术风格。与其让美术重做法线贴图不如在Shader中全局性地用Normal Strength进行微调。例如卡通风格项目可能需要更弱的法线强度来保持色块感而写实项目可能需要增强强度来突出材质质感。5. 常见问题排查与性能优化指南即使理解了原理在实际操作中仍会踩坑。下面是我总结的“血泪经验簿”。5.1 法线看起来“不对”或“很平”这是最常见的问题。请按以下清单排查问题现象可能原因解决方案模型表面完全没有凹凸感像塑料。1. Normal Vector节点未连接到Master节点的Normal输入口。2. 使用了法线贴图但贴图未正确标记为“Normal Map”。3. 法线贴图采样节点的Type未设置为Normal。1. 检查连接线。2. 在Project面板选中法线贴图在Inspector中确保Texture Type为Normal map并勾选Create from Grayscale如果是灰度高度图。3. 在ShaderGraph中设置正确。凹凸感存在但光照方向错乱比如该暗的地方亮。1. 法线空间错误。最常见的是该用世界空间法线却用了物体空间。2. 在应用法线贴图时TBN矩阵构建或空间转换错误。3. 模型本身的顶点法线有问题“坏法线”。1. 确保Normal Vector节点和光照计算在同一空间通常为World Space。2. 使用ShaderGraph内置的Transform节点进行Tangent to World转换避免手动计算。3. 在3D建模软件中检查并重新计算平滑组或顶点法线。在Unity中可以尝试在模型导入设置中勾选Calculate Normals或Smooth Normals。在特定角度下表面出现难看的深色或紫色条纹。法线向量未归一化。插值或计算后的法线长度不为1导致点积结果超出[-1,1]范围光照计算出错。在关键计算如dot product with light direction前务必对法线向量进行Normalize。这是一个必须养成的好习惯。5.2 性能考量与优化建议空间选择有成本Normal Vector节点在不同空间下的计算成本不同。Object Space成本最低World Space和View Space需要一次矩阵乘法Tangent Space如果涉及从贴图采样则更复杂。只在必要时使用高成本空间。例如如果只是做一个基于法线Y分量的简单雪地遮罩用Object Space可能就够了。慎用分支避免使用基于法线如if(N.y 0.5)的硬性分支。GPU不喜欢分支尤其是在片段着色器中。尽量使用smoothstep、lerp或clamp等平滑函数来创建过渡或者将条件计算转换为数学运算。减少冗余计算如果你在Shader中多处需要世界法线应该只计算一次存储在一个临时变量中然后复用。在ShaderGraph中这意味着你可以通过创建一个Custom Function节点或巧妙地使用Branch节点来组织逻辑避免重复连接和计算Normal Vector节点。法线贴图压缩与精度移动平台请使用压缩格式如ASTC、ETC2。注意过于强烈的法线细节XY分量值很大在压缩后可能产生精度损失导致可见的色带或噪点。使用Normal Strength适当控制强度在合理范围内比如不要超过2.0有助于缓解此问题。5.3 关于“双面渲染”的法线陷阱当启用双面渲染如树叶、布料时背面的法线方向会与正面相反。这可能导致背面光照计算错误例如本该被照亮的部分变暗。标准的解决方案是在片段着色器中根据当前渲染的是正面还是背面对法线向量或视线/光线向量进行翻转。在ShaderGraph中你可以使用Is Front Face节点来判断当前片段是否属于正面。然后通过一个Branch节点或乘法来对法线向量进行条件性翻转。// 概念性操作在ShaderGraph中用节点连接实现 bool isFrontFace IsFrontFace; float3 worldNormal NormalVector_WorldSpace; // 如果是背面翻转法线或翻转光照计算中的其他向量如光线方向 if (!isFrontFace) { worldNormal -worldNormal; } // 后续使用翻转后的worldNormal进行光照计算处理双面法线是制作高质量透明或镂空材质的关键一步容易被忽略但效果提升显著。法线向量节点这个ShaderGraph中的基础元件其深度和广度远超第一眼印象。它不仅是光照的钥匙更是驱动各种视觉魔术的引擎。掌握它意味着你掌握了在三维表面上“绘画”光与影、定义质感与形态的基础能力。从我个人的经验来看花时间深入理解并熟练运用法线向量及其相关节点如Normal Strength是Shader学习从“模仿连接”到“自主创造”的关键转折点。下次当你面对一个材质效果无从下手时不妨先问问自己“这里的法线我该怎么去控制和利用”答案往往就在其中。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进