Unity - 伽马空间和线性空间是什么与他们之间的不同?

本文深入探讨了线性空间和伽马空间的概念,解释了两者之间的区别及其在基于物理的渲染(PBR)中的应用。文章详细介绍了线性空间如何确保颜色的正确相加和相乘,而伽马空间则考虑到屏幕和人眼对亮度的非线性响应。此外,文中还讨论了Unity引擎如何支持线性空间渲染,并提供了在不支持线性空间的平台上实现伽马校正的方法。

译自:http://www.kinematicsoup.com/news/2016/6/15/gamma-and-linear-space-what-they-are-how-they-differ

线性空间照明的概念越来越多的被开发人员使用,为了达到下一代真实感使用基于物理的渲染。虽然线性空间和其对应的伽马空间是很简单和重要的概念,下文说明了两种空间是什么,它们的不同,和它们是如何使用的。

线性空间

首先我们需要了解线性颜色空间的概念。简单的说,它意味数值强度与它的感知强度成正比。这意味着可以正确的添加和相乘颜色。一个颜色空间没有属性叫"non-linear"。下面是一个双倍强度值在线性空间和非线性空间的例子。虽然在线性空间中相应数值是正确的,但是在非线性空间中,我们不能简单的通过加法得到结果,相机对于光强的感知是基于线性的,业界的图片都是存储在Gamma空间中,那么相机的到最终的图片就会经历一个编码过程,也就是Gamma编码, Vout = Vin(1/2.2) ;
在这里插入图片描述

伽马空间

需要使用伽马的两种主要情况:首先屏幕对强度是非线性的相应。另一点人眼在暗色的识别比在亮色的识别更好,这意味着图片压缩到存储空间,我们想得到更好的精度对于暗强度,且牺牲了较亮强度。这两个问题都是通过伽马矫正来解决的问题,伽马校正是指将图片中的每个像素强度他通过幂函数进行处理。具体来说,gamma是应用于图像的幂的名称。

11173460-ce83e7f0a67db748.png [点击并拖拽以移动]

下图显示了gamma值的不同

在这里插入图片描述

颜色空间和渲染管线

当时使用伽马管线渲染,纹理将传给shader,进行gamma修正,下一步进行光照计算。之后图片将输出到屏幕上进行显示并调整显示的伽马值。这种操作是很简单的,没有物理修正。在真正的生活中,光照现象是线性的,这意味着复杂光源对光照表现的贡献是同时添加到正确的强度。这样着色的过程是线性的,这意味着着色的结果不是真正的准确,但是在显示修正后表现的很好,然而随着对真实沉浸感渲染的需求不断增加,这种方法已经不再适用。
PBR的典型实践是使用线性管道。在将输入颜色、纹理传给线性空间之前将伽马修正移除。当着色完成,结果在物理上是正确的因为着色和输入都在同样的空间。之后,后处理也在线性空间中,后处理通常是线性的。最后对图像进行伽马修正以将正确的结果显示出来。

Unity中的颜色空间

Unity可以很方便的切换颜色空间,对于许多项目渲染管线可以无缝工作。通常Unity只在PC、Xbox、PS平台支持线性渲染。对于这些平台:Edit -> Project Settings -> Player -> Other Settings中来切换空间。
这样Shaders将接受无伽马修正的纹理。
当渲染适用HDR的线性颜色空间,Unity全部的后处理将在线性空间中。当只启用线性色彩空间的时候,Unity将是哟个gamma帧缓存,但是当都写得时候,Unity将自动将颜色空间进行转换,这样图片效果还在线性空间中被处理。
虽然Unity在一些平台上不支持线性空间,但是可以通过在shader中实现pow()方法来实现伽马修正,这个方法将有较大的性能消耗。

<think> 我们讨论的是色彩空间转换,特别是线性伽马色彩空间之间的转换。根据引用内容,伽马空间转换对视觉有显著影响(引用[1]),并且转换涉及线性代数中的基底变换(引用[2])。同时,引用[3]解释了为什么线性空间更接近真实情况,而引用[4]提到了Unity中色彩空间的切换。 转换公式: 1. 线性空间伽马空间(编码):$C_{\gamma} = C_{\text{linear}}^{1/\gamma}$ 其中 $C_{\gamma}$ 是伽马空间的值,$C_{\text{linear}}$ 是线性空间的值,$\gamma$ 是伽马值(通常为2.2)。 2. 伽马空间线性空间(解码):$C_{\text{linear}} = C_{\gamma}^{\gamma}$ 注意:在实际应用中,我们通常使用近似伽马值2.2(sRGB标准)或2.4(某些视频标准)。但有时为了计算效率,会使用近似函数。 步骤分解: 步骤1:将伽马空间的颜色值转换到线性空间(解码) - 对于每个颜色通道(R, G, B),应用转换公式 - 例如,如果输入颜色为$(r_{\gamma}, g_{\gamma}, b_{\gamma})$,则线性空间颜色为: $$r_{\text{linear}} = r_{\gamma}^{2.2}$$ $$g_{\text{linear}} = g_{\gamma}^{2.2}$$ $$b_{\text{linear}} = b_{\gamma}^{2.2}$$ 步骤2:在线性空间中处理颜色(例如渲染、混合等) - 线性空间中的操作是符合物理规律的,例如光照计算、颜色混合等。 步骤3:将线性空间的颜色转换回伽马空间(编码)以用于显示 - 使用逆转换: $$r_{\gamma} = r_{\text{linear}}^{1/2.2}$$ $$g_{\gamma} = g_{\text{linear}}^{1/2.2}$$ $$b_{\gamma} = b_{\text{linear}}^{1/2.2}$$ 注意:在转换过程中,我们通常假设颜色值已经在0到1的范围内。如果使用8位颜色(0-255),需要先归一化。 为什么需要转换? - 人眼对暗部的变化更敏感,而伽马空间通过非线性分配(更多的位用于暗部)来适应人眼。但是,计算机图形学中的计算(如光照、阴影)需要在线性空间中进行,因为物理世界的光照是线性的。在伽马空间中直接计算会导致错误(如混合颜色变暗)。 实际应用(如Unity): -Unity中,如果选择线性空间Linear Space),则Unity会在着色器计算前自动将纹理从伽马空间转换到线性空间,并在输出到屏幕时转换回伽马空间。而如果选择伽马空间Gamma Space),则不会进行转换(引用[4])。 代码示例(Unity Shader): - 在着色器中,我们可以使用内置函数进行转换: // 伽马空间线性空间 fixed3 linearRGB = pow(gammaRGB, 2.2); // 线性空间伽马空间 fixed3 gammaRGB = pow(linearRGB, 1/2.2); - 但注意,在支持线性空间的平台上,Unity会自动处理大部分纹理(标记为sRGB的纹理)最终输出。我们只需在需要时手动转换(例如处理非纹理数据)。 特殊情况:sRGB纹理 - 在图形API(如OpenGL、DirectX)中,我们可以将纹理指定为sRGB格式,这样在采样时硬件会自动将其转换为线性空间。同样,在渲染到屏幕时,帧缓冲区设置为sRGB,则硬件会自动将线性空间的计算结果转换回伽马空间。 注意事项: - 并非所有纹理都需要转换:法线贴图、金属度贴图等非颜色纹理通常应标记为线性(不进行转换)。 - 性能:自动转换由硬件完成,效率很高。 引用说明: - 引用[1]指出伽马校正对色彩过渡的影响。 - 引用[2]从线性空间角度解释色彩空间转换是线性变换(但注意伽马转换是非线性的,这里可能是指不同线性RGB空间之间的转换)。 - 引用[3]解释了线性空间渲染流程的优势。 - 引用[4]提到Unity中色彩空间的切换设置。 因此,在图形渲染管线中,正确的色彩空间转换流程是: [图像存储] (伽马空间) -> 解码 -> [渲染处理] (线性空间) -> 编码 -> [显示] (伽马空间) 如果跳过转换步骤,会导致渲染结果不符合物理真实(如光照计算错误)。 相关问题: </think> ### 线性伽马色彩空间转换方法 线性伽马色彩空间的转换本质上是非线性变换过程,其数学关系可表示为: #### 1. **伽马校正(线性 → 伽马)** $$C_{\gamma} = C_{\text{linear}}^{1/\gamma}$$ 其中: - $C_{\gamma}$:伽马空间颜色值(0-1) - $C_{\text{linear}}$:线性空间颜色值(0-1) - $\gamma$:伽马值(通常取2.2) ```python import numpy as np def linear_to_gamma(linear, gamma=2.2): return np.power(linear, 1.0/gamma) # 示例:将线性值0.5转换为伽马空间 linear_val = 0.5 gamma_val = linear_to_gamma(linear_val) # ≈0.73 ``` #### 2. **伽马解码(伽马 → 线性)** $$C_{\text{linear}} = C_{\gamma}^{\gamma}$$ ```python def gamma_to_linear(gamma_val, gamma=2.2): return np.power(gamma_val, gamma) # 示例:将伽马值0.73转换回线性空间 recovered_linear = gamma_to_linear(gamma_val) # ≈0.5 ``` #### 3. **实际应用场景** | **操作阶段** | **转换方向** | **目的** | |-------------------|------------------|----------| | 纹理加载时 | 伽马 → 线性 | 确保光照计算在物理正确空间进行[^3] | | 着色器计算 | 保持线性 | 使光照/阴影符合物理规律 | | 最终帧缓冲输出前 | 线性 → 伽马 | 适配显示设备的伽马特性[^1] | #### 4. **Unity引擎中的实现** ```csharp // 手动转换示例 Color gammaColor = new Color(0.5f, 0.5f, 0.5f); Color linearColor = gammaColor.linear; // 伽马→线性 // 渲染管线自动转换流程 void OnRenderImage(RenderTexture src, RenderTexture dest) { Graphics.Blit(src, dest, gammaCorrectionMaterial); // 线性→伽马转换 } ``` 通过**Edit > Project Settings > Player > Other Settings**切换色彩空间[^4]: - Gamma Space:直接输出(移动平台) - Linear Space:自动管理转换(PC/主机) #### 5. **视觉差异对比** | **色彩过渡** | 无转换(错误) | 正确转换 | |-------------------|-------------------|-----------------| | 暗部细节 | 严重丢失 | 清晰可见[^1] | | 颜色混合 | 出现非物理性变暗 | 符合光学叠加原理 | | 高光区域 | 过度饱 | 自然过渡 | #### 注意事项 1. **8位纹理处理**:需先归一化 ```csharp float linearValue = Mathf.Pow(texel.rgb/255.0, 2.2); ``` 2. **HDR内容**:超过1.0的值需特殊处理 3. **法线/金属贴图**:应保持在线性空间[^3] 4. **性能优化**:使用查找表(LUT)加速转换 > 关键原则:**始终在线性空间进行计算,仅在输入/输出时转换**[^3]。错误的空间转换会导致渲染结果偏离物理真实,尤其影响PBR材质表现。 --- ### 相关问题 1. 如何在Unity中为移动设备优化伽马空间渲染? 2. 线性空间下HDRLDR纹理的处理差异? 3. 如何手动实现sRGB纹理的硬件加速转换? 4. 伽马校正对PBR材质球反射率计算的影响
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值