Unity ShaderGraph白平衡节点:从色彩科学到实时渲染应用

发布时间:2026/7/10 2:25:36
Unity ShaderGraph白平衡节点:从色彩科学到实时渲染应用 1. 项目概述为什么我们需要白平衡节点在ShaderGraph里鼓捣过一阵子颜色调整的朋友大概率都见过或者用过Color Adjustment分类下的那些节点对比度、饱和度、色相这些概念都比较直观。但“白平衡”White Balance这个节点乍一看可能会让人有点懵——这不是摄影和后期处理里的概念吗怎么跑到实时渲染的着色器里来了其实这正是ShaderGraph强大和贴近实际美术工作流的一个体现。简单来说白平衡节点的核心任务就是模拟真实世界中“光源颜色”对物体固有色的影响并允许我们对其进行校正。想象一下你在钨丝灯暖黄光下看一张白纸纸会显得偏黄在阴天冷蓝光下看纸又会偏蓝。你的大脑会自动进行“白平衡”告诉你“这是一张白纸”。在CG渲染中我们需要手动或自动地完成这个校正过程以确保颜色在不同光照环境下看起来是“正确”的或者说是符合我们艺术预期的。Unity的ShaderGraph提供了这个内置节点让我们能在Shader层面实时、动态地调整画面的色温和色调。这对于营造特定氛围如温暖的黄昏、冷峻的月夜、匹配不同场景的灯光或者简单地修正因环境光设置不当导致的颜色偏差都极其有用。它不是一个简单的颜色叠加而是基于色彩科学模型进行的转换理解其背后的原理能让你从“会调参数”进阶到“懂得为何这么调”。2. 节点接口与参数深度解析打开ShaderGraph找到White Balance节点你会看到它非常简洁只有三个输入端口和一个输出端口。但简洁的背后每个参数都大有讲究。2.1 核心输入端口InTemperatureTintIn (Vector 3)这是待处理的原始颜色输入。通常我们会将主纹理的采样颜色例如Sample Texture 2D节点的RGBA输出中的RGB部分连接到这里。它接受一个三维向量代表RGB颜色空间中的一个颜色。Temperature (Float)色温这是白平衡调整的第一个核心参数。它的命名和效果直接来源于物理学中的色温概念单位是开尔文K。但在ShaderGraph的这个节点中它是一个归一化、无单位的浮点数。参数范围与效果官方文档建议的最佳操作范围是[-1.67, 1.67]。这个范围是怎么来的我们稍后在原理部分会揭秘。你需要记住的是负值-1.67 ~ 0为画面增加蓝色调模拟高色温冷光环境比如阴天、阴影、荧光灯。正值0 ~ 1.67为画面增加黄色/红色调模拟低色温暖光环境比如日出日落、钨丝灯、烛光。值为0不进行色温偏移。实操心得在实际调节时你很少需要用到±1.67这样的极值。对于大多数自然场景的微调[-0.5, 0.5]这个区间的变化已经非常明显和够用。直接从0开始缓慢拖动滑块观察画面中原本应该是白色或灰色的区域如墙壁、衬衫你会直观地感受到色温变化。Tint (Float)色调这是白平衡调整的第二个维度用于补偿色温调整可能无法完全校正的绿色或洋红色偏。参数范围与效果同样最佳范围是[-1.67, 1.67]。负值-1.67 ~ 0为画面增加绿色调。常用于校正某些荧光灯造成的洋红色偏。正值0 ~ 1.67为画面增加洋红色粉红色调。值为0不进行色调偏移。Out (Vector 3)处理后的颜色输出。直接连接到主着色器的Base Color或相应的颜色输入上即可。2.2 参数联动与视觉影响Temperature和Tint并不是完全独立的。在物理上改变光源色温时其光谱分布变化也可能轻微影响绿-品红轴。因此这个节点在内部计算时两者是协同工作的。当你大幅调整Temperature后有时会觉得颜色还是有点“怪”这时候微调一下Tint往往能获得更自然、更“干净”的白色。一个常见的用法是先用Temperature滑块确定画面整体的冷暖基调然后再用Tint进行精细的“去杂色”处理让中性色白、灰显得更纯粹。3. 背后的色彩科学从RGB到LMS如果只是调调参数那这部分可以跳过。但如果你想真正驾驭这个节点甚至未来自己魔改或实现类似功能理解其原理至关重要。Unity官方在节点描述里隐藏了生成的代码这段代码正是关键。白平衡的核心思想是将颜色从设备相关的RGB空间转换到与人类视觉锥体细胞响应更相关的色彩空间通常是LMS空间进行校正然后再转换回RGB空间。1. 什么是LMS空间LMS代表人类视网膜上三种锥体细胞对不同波长光的敏感度长波L 对红色敏感、中波M 对绿色敏感、短波S 对蓝色敏感。这是一个基于人眼生理特性的色彩空间。在白平衡计算中使用LMS空间比直接在RGB空间做乘法更符合视觉感知能产生更物理正确的结果。2. 节点的计算步骤拆解我们结合官方提供的Unity_WhiteBalance_float函数来一步步看步骤一参数预处理float t1 Temperature * 10 / 6; float t2 Tint * 10 / 6;这里将用户输入的[-1.67, 1.67]范围内的参数映射到了大约[-2.78, 2.78]的范围。这个缩放因子10/6很可能是为了适配后续色度坐标计算的敏感度而经验设定的。步骤二计算目标白点的色度坐标xyfloat x 0.31271 - t1 * (t1 0 ? 0.1 : 0.05); float standardIlluminantY 2.87 * x - 3 * x * x - 0.27509507; float y standardIlluminantY t2 * 0.05;这是算法的精髓所在。0.31271是标准光源D65近似平均日光在CIE xy色度图上的x坐标。D65是许多色彩标准如sRGB的基准白点。根据t1处理后的色温的正负以不同的斜率0.1或0.05调整x坐标。这模拟了色温变化时白点在色度图上沿黑体辐射轨迹的移动。暖光正t1x减小向红色区域冷光负t1x增大向蓝色区域。standardIlluminantY是通过一个二次多项式从x计算出的对应y坐标这个多项式描述了黑体轨迹。最后用t2处理后的色调在y方向上进行微调模拟绿-品红偏移。通过这几行代码我们得到了一个目标白点的xy色度坐标(x, y)。步骤三将目标白点转换到LMS空间float Y 1; // 假设亮度为1 float X Y * x / y; float Z Y * (1 - x - y) / y; float L 0.7328 * X 0.4296 * Y - 0.1624 * Z; float M -0.7036 * X 1.6975 * Y 0.0061 * Z; float S 0.0030 * X 0.0136 * Y 0.9834 * Z; float3 w2 float3(L, M, S);这里先将xyY色度坐标转换为CIE XYZ色彩空间然后再通过一个转换矩阵CIEXYZToLMS矩阵系数为0.7328, 0.4296等得到目标白点在LMS空间的值w2。 同时定义了一个参考白点w1 float3(0.949237, 1.03542, 1.08728)这就是D65白点在LMS空间中的值。步骤四计算平衡系数并应用float3 balance float3(w1.x / w2.x, w1.y / w2.y, w1.z / w2.z);计算平衡系数balance。这个系数是关键它的意义是为了将“目标白点w2”校正回“参考白点w1”需要在LMS空间的三个通道上分别乘以的系数。如果目标白点偏黄L、M相对较高那么balance的B通道值就会大于1以增强蓝色来抵消黄色。步骤五颜色空间转换与校正float3x3 LIN_2_LMS_MAT { ... }; // 线性RGB 转 LMS 矩阵 float3x3 LMS_2_LIN_MAT { ... }; // LMS 转 线性RGB 矩阵 float3 lms mul(LIN_2_LMS_MAT, In); // 1. 输入颜色转LMS lms * balance; // 2. 在LMS空间应用白平衡系数 Out mul(LMS_2_LIN_MAT, lms); // 3. 转回线性RGB将输入的线性RGB颜色In通过矩阵LIN_2_LMS_MAT转换到LMS空间。在LMS空间将每个通道乘以对应的balance系数。这一步直接完成了基于人眼感知的颜色校正。将校正后的LMS颜色通过逆矩阵LMS_2_LIN_MAT转换回线性RGB空间并输出。整个过程可以概括为RGB - LMS - (平衡校正) - LMS - RGB。这个流程确保了校正过程符合人眼视觉特性而不仅仅是简单的颜色滤镜。4. 在ShaderGraph中的实战应用与技巧理解了原理我们来看看怎么在项目中用好它。白平衡节点通常不单独使用而是作为颜色处理管线中的一环。4.1 基础连接与场景氛围塑造最直接的用法是将它连在纹理采样之后Fragment着色阶段之前。[Texture Property] - [Sample Texture 2D] (RGB) - [White Balance Node] - [Base Color]通过一个Vector1类型的属性或通过脚本控制来驱动Temperature和Tint参数你就能轻松实现昼夜循环白天使用偏冷的色温如-0.3黄昏使用暖色温如0.7。室内外差异室内钨丝灯用暖黄0.5室外阴天用冷蓝-0.4。特殊效果恐怖场景可以施加轻微的绿色调Tint为负梦幻场景可以施加粉色调Tint为正。4.2 高级用法与后处理栈配合虽然节点可用于物体单独着色但其威力更体现在屏幕后处理Post-processing中。你可以创建一个全屏后处理Shader Graph使用Scene Color节点作为In的输入对整个画面进行白平衡调整。技巧基于画面内容的自动白平衡模拟完全自动的白平衡算法很复杂但我们可以模拟一个简化版。思路是假设画面中央或高亮区域应该是中性色灰白色。使用Scene Color采样屏幕纹理。使用一个Blur节点对特定区域如中心一小块进行模糊得到区域的平均颜色。将这个平均颜色的RGB分量转换到某种亮度-色度空间如简化版的RGB to YCbCr分析其色度Cb, Cr偏离中性值0.5 for某些范围的程度。将这个偏离程度映射为Temperature和Tint的调整值反馈给白平衡节点。这样当镜头对准一个蓝色物体时Shader会认为画面偏蓝并自动增加黄色来补偿虽然不一定完全准确但能实现动态的、基于内容的色彩调整效果。4.3 性能考量与参数优化白平衡节点涉及两次3x3矩阵乘法RGB-LMS转换和一些标量计算属于中等开销的操作。在移动端或对大量物体使用时需注意避免每帧动态剧烈变化除非必要否则将Temperature和Tint设置为静态值或缓慢插值。精度选择在Shader Graph节点设置中可以考虑将精度Precision从Float改为Half对于颜色计算半精度在大多数情况下已经足够能提升性能。后处理优于逐物体如果需要对整个场景调色强烈建议使用后处理方案。一个全屏Pass应用一次白平衡远比每个物体单独计算要高效。5. 常见问题排查与效果调试指南在实际使用中你可能会遇到一些困惑或效果不如预期的情况。5.1 为什么调整参数感觉没效果或效果不对可能原因1颜色空间不正确。白平衡节点必须在线性颜色空间Linear Color Space下工作才能得到正确结果。请确保Player Settings - Player - Other Settings - Color Space 设置为Linear。如果你在使用Gamma空间节点的计算将基于错误的颜色响应曲线导致校正失效或产生奇怪的颜色。可能原因2输入颜色值异常。检查连接到In端口的颜色值。确保其范围大致在[0, 1]的合理区间。如果输入是HDR颜色值远大于1白平衡系数的乘法可能会产生非常夸张的结果。可以考虑先对HDR颜色进行色调映射Tone Mapping或压缩再进行白平衡调整。可能原因3参数超出合理范围。虽然节点允许输入任意浮点数但官方建议的[-1.67, 1.67]是最佳范围。超出此范围内部计算的色度坐标(x, y)可能变得无效如y为负或xy1导致后续计算出现NaN或不可预测的颜色。始终将参数控制在这个范围内。5.2 如何获得“参考白”这是白平衡调试的核心技巧。在场景中放置一个纯白色或中性灰色的物体例如一个Unlit材质、颜色为(0.9, 0.9, 0.9)的球体作为你的“白卡”。将白平衡节点的参数归零。在目标光照下观察这个“白卡”在游戏视图中的颜色。调节Temperature和Tint直到这个“白卡”在屏幕上显示为你想要的、纯净的白色或灰色。此时白平衡参数就是针对当前光照环境的校正值。你可以将这个值应用到整个场景的后处理或相关材质上。5.3 白平衡与Color LookupLUT的区别这是另一个常见困惑。两者都用于调色但有本质区别白平衡节点基于物理模型进行色彩校正。目标是让颜色在不同光照下“还原”或“匹配”到标准观察状态。它是全局性的、算法驱动的调整。Color Lookup (LUT)是一种色彩分级工具。它通过一个预制的颜色映射表将输入颜色替换为输出颜色以实现特定的艺术风格如电影感、复古、赛博朋克。它是整体性的、艺术驱动的映射。通常工作流是先使用白平衡进行色彩校正让颜色看起来“正确”然后再使用LUT或其它工具进行色彩分级让颜色看起来“好看”或“有风格”。5.4 参数动画与平滑过渡如果你需要通过脚本动态控制白平衡例如从室内走到室外直接每帧设置新的Temperature值可能会导致颜色跳跃。建议使用插值方法进行平滑过渡// C# 脚本示例 public float targetTemperature 0.0f; public float currentTemperature 0.0f; public float transitionSpeed 2.0f; void Update() { currentTemperature Mathf.Lerp(currentTemperature, targetTemperature, Time.deltaTime * transitionSpeed); // 将 currentTemperature 传递给Shader }在Shader中使用这个平滑变化的值来驱动节点参数可以获得非常自然的色温过渡效果。掌握白平衡节点意味着你掌握了在实时渲染中控制画面“光色”基础的一把钥匙。它连接了物理光学、色彩科学与艺术表现是ShaderGraph工具链中一个兼具实用性和深度的模块。下次当你想让场景的灯光感觉更真实或者想快速统一不同光源下的色调时不妨优先考虑一下这个强大的节点。