米嘉怡 发表于 2025-10-18 22:35:07

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

【从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:手动伽马校正

csharp
// 在Shader中手动校正
float3 linearColor = pow(sRGBColor, 2.2);// sRGB转线性
float3 processedColor = DoLightingCalculation(linearColor);
float3 gammaCorrected = pow(processedColor, 1/2.2);// 线性转sRGB示例2:Unity颜色空间设置

csharp
// 检查当前颜色空间
if (QualitySettings.activeColorSpace == ColorSpace.Linear) {
// 在线性空间下自动处理伽马校正
    material.color = Color.red;// Unity会自动处理转换
}示例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
Shader "Custom/GammaCorrection"
{
    Properties
    {
      _MainTex ("Texture", 2D) = "white" {}
      _GammaValue ("Gamma Value", Range(0.1, 3.0)) = 2.2
    }

    SubShader
    {
      Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" }

      Pass
      {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct Varyings
            {
                float2 uv : TEXCOORD0;
                float4 positionHCS : SV_POSITION;
            };

            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);
            float _GammaValue;

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                // 采样纹理(自动处理sRGB到线性转换)
                half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);

                // 手动伽马校正(线性空间计算)
                half3 linearColor = col.rgb;
                half3 processedColor = linearColor * 2.0; // 示例光照计算

                // 应用伽马校正输出
                half3 gammaCorrected = pow(processedColor, 1.0/_GammaValue);
                return half4(gammaCorrected, col.a);
            }
            ENDHLSL
      }
    }
}
[*]GammaCorrectionSettings.cs
using UnityEngine;
using UnityEngine.Rendering;

public class GammaCorrectionSettings : MonoBehaviour
{
   
    public float gammaValue = 2.2f;

    void Start()
    {
      // 确保项目使用线性颜色空间
      if (QualitySettings.activeColorSpace != ColorSpace.Linear)
      {
            Debug.LogWarning("建议在Player Settings中将颜色空间改为Linear");
      }
    }

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
      // 后处理方式应用伽马校正
      Material mat = new Material(Shader.Find("Hidden/Universal Render Pipeline/GammaCorrection"));
      mat.SetFloat("_GammaValue", gammaValue);
      Graphics.Blit(src, dest, mat);
    }
}
使用场景


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


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

睁扼妤 发表于 2025-11-19 11:05:39

不错,里面软件多更新就更好了

簑威龙 发表于 2025-12-5 19:12:22

分享、互助 让互联网精神温暖你我
页: [1]
查看完整版本: 【光照】UnityURP为什么要[Gamma矫正]?