找回密码
 立即注册
首页 业界区 业界 【光照】UnityURP为什么要[Gamma矫正]?

【光照】UnityURP为什么要[Gamma矫正]?

米嘉怡 昨天 22:35
【从UnityURP开始探索游戏渲染】专栏-直达
伽马校正的定义与原理

伽马校正是对颜色值进行非线性变换的过程,其核心是通过幂函数(γ函数)调整亮度值,使人眼感知更均匀。数学表达式为:输出 = 输入^γ,其中γ=0.45用于编码(sRGB到线性空间),γ=2.2用于解码(线性空间到sRGB)。
人眼对亮度的感知是非线性的——对暗部变化敏感,对亮部变化不敏感。例如从1根蜡烛增加到2根蜡烛的变化很容易察觉,而从100根增加到101根则难以察觉。
为什么需要伽马校正

伽马校正主要解决三个问题:

  • 存储优化‌:8位色深(0-255)下,通过伽马编码为暗部分配更多值域,亮部分配较少值域,更符合人眼感知特性。
  • 显示一致性‌:补偿早期CRT显示器电压-亮度非线性关系(γ≈2.2),现代显示器通过硬件模拟保持兼容。
  • 渲染准确性‌:在线性空间计算光照和混合(如PBR),避免亮度计算错误。
历史发展

伽马校正起源于CRT时代,当时显示器物理特性导致输入电压与亮度呈γ≈2.2的幂关系。随着LCD等新技术出现,虽然物理特性改变,但为保持兼容性仍沿用该标准。现代图形管线(如URP)已将其整合为标准化流程。
Unity URP中的实现机制

URP默认使用线性空间(Linear Space),其工作流程为:

  • 输入转换‌:对sRGB纹理自动应用γ=2.2转换到线性空间。
  • 计算阶段‌:所有光照和混合在线性空间执行。
  • 输出转换‌:最终输出应用γ=0.45转换回sRGB空间。
实现原理是通过着色器内置的GammaToLinearSpace()和LinearToGammaSpace()函数完成转换。URP强制使用线性空间是因为:

  • 物理正确性:光照计算符合能量守恒
  • 混合准确性:如半透明叠加效果更真实
  • 跨平台一致性:避免不同设备显示差异
实际应用示例

示例1:手动伽马校正
  1. csharp
  2. // 在Shader中手动校正
  3. float3 linearColor = pow(sRGBColor, 2.2);// sRGB转线性
  4. float3 processedColor = DoLightingCalculation(linearColor);
  5. float3 gammaCorrected = pow(processedColor, 1/2.2);// 线性转sRGB
复制代码
示例2:Unity颜色空间设置
  1. csharp
  2. // 检查当前颜色空间
  3. if (QualitySettings.activeColorSpace == ColorSpace.Linear) {
  4. // 在线性空间下自动处理伽马校正
  5.     material.color = Color.red;// Unity会自动处理转换
  6. }
复制代码
示例3:解决PS与Unity混合差异

当PS(Gamma空间)与Unity(线性空间)混合结果不一致时:

  • 在PS中工作于线性空间(编辑→颜色设置→RGB工作空间改为"显示器RGB")
  • 或Unity中临时切换至Gamma空间(不推荐)
完整伽马校正的示例

代码与示例


  • Shader部分‌:

    • 包含完整的URP Shader结构,使用HLSL语法
    • 通过pow(processedColor, 1.0/_GammaValue)实现伽马校正
    • 自动处理sRGB纹理到线性空间的转换

  • 脚本部分‌:

    • 提供运行时伽马值调整
    • 检查线性空间设置
    • 可选的后处理实现方式

  • Unity设置‌:

    • 在Project Settings > Player > Other Settings中:

      • 将Color Space设为Linear
      • 确保URP Asset的Post Processing开启

    • 对非颜色纹理(如法线贴图)取消sRGB选项

  • GammaCorrection.shader
    1. Shader "Custom/GammaCorrection"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _GammaValue ("Gamma Value", Range(0.1, 3.0)) = 2.2
    7.     }
    8.     SubShader
    9.     {
    10.         Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" }
    11.         Pass
    12.         {
    13.             HLSLPROGRAM
    14.             #pragma vertex vert
    15.             #pragma fragment frag
    16.             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    17.             struct Attributes
    18.             {
    19.                 float4 positionOS : POSITION;
    20.                 float2 uv : TEXCOORD0;
    21.             };
    22.             struct Varyings
    23.             {
    24.                 float2 uv : TEXCOORD0;
    25.                 float4 positionHCS : SV_POSITION;
    26.             };
    27.             TEXTURE2D(_MainTex);
    28.             SAMPLER(sampler_MainTex);
    29.             float _GammaValue;
    30.             Varyings vert(Attributes IN)
    31.             {
    32.                 Varyings OUT;
    33.                 OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
    34.                 OUT.uv = IN.uv;
    35.                 return OUT;
    36.             }
    37.             half4 frag(Varyings IN) : SV_Target
    38.             {
    39.                 // 采样纹理(自动处理sRGB到线性转换)
    40.                 half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
    41.                 // 手动伽马校正(线性空间计算)
    42.                 half3 linearColor = col.rgb;
    43.                 half3 processedColor = linearColor * 2.0; // 示例光照计算
    44.                 // 应用伽马校正输出
    45.                 half3 gammaCorrected = pow(processedColor, 1.0/_GammaValue);
    46.                 return half4(gammaCorrected, col.a);
    47.             }
    48.             ENDHLSL
    49.         }
    50.     }
    51. }
    复制代码
  • GammaCorrectionSettings.cs
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. public class GammaCorrectionSettings : MonoBehaviour
    4. {
    5.     [Range(0.1f, 3.0f)]
    6.     public float gammaValue = 2.2f;
    7.     void Start()
    8.     {
    9.         // 确保项目使用线性颜色空间
    10.         if (QualitySettings.activeColorSpace != ColorSpace.Linear)
    11.         {
    12.             Debug.LogWarning("建议在Player Settings中将颜色空间改为Linear");
    13.         }
    14.     }
    15.     void OnRenderImage(RenderTexture src, RenderTexture dest)
    16.     {
    17.         // 后处理方式应用伽马校正
    18.         Material mat = new Material(Shader.Find("Hidden/Universal Render Pipeline/GammaCorrection"));
    19.         mat.SetFloat("_GammaValue", gammaValue);
    20.         Graphics.Blit(src, dest, mat);
    21.     }
    22. }
    复制代码
使用场景


  • PBR材质‌:确保光照计算在线性空间
  • UI混合‌:避免颜色叠加出现亮度异常
  • 后处理效果‌:如Bloom、Tonemapping前需要正确伽马空间
注意事项


  • 移动平台需注意GLES 3.0支持,部分设备可能回退到Gamma空间
  • 透明通道(alpha)不参与伽马转换
  • 法线贴图等非颜色纹理应标记为"Bypass sRGB"避免错误转换
<blockquote>
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册