【光照】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選項
- 在Project Settings > Player > Other Settings中:
-
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"避免錯誤轉換
【從UnityURP開始探索游戲渲染】專欄-直達
(歡迎點贊留言探討,更多人加入進來能更加完善這個探索的過程,??)

《從UnityURP探索伽馬校正:原理與實現》摘要:伽馬校正是通過冪函數對顏色值進行非線性變換的過程(γ=0.45編碼/2.2解碼),解決人眼非線性感知與顯示設備特性的匹配問題。其核心價值體現在:優化8位色深存儲分配、保持跨設備顯示一致性、確保PBR等光照計算的物理準確性。現代渲染管線(如URP)默認采用線性空間工作流,通過內置函數自動完成sRGB與線性空間的轉換。專欄詳細解析了伽馬校正的歷史淵源、數學原理,并提供了URP中的Shader實現代碼與工程設置要點,包括紋理空間標記、移動平臺兼容性等實踐注意事項
浙公網安備 33010602011771號