【从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
- {
- [Range(0.1f, 3.0f)]
- 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开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |