色彩空间转换:Linear、sRGB 与 Gamma 校正教程
引言
在计算机图形学中,色彩空间转换至关重要。sRGB 是标准显示色彩空间,具有非线性的 gamma 曲线;而物理计算通常在线性空间进行。正确转换可以避免颜色偏差和光照错误。本教程将指导你进行转换。
理解基本概念
- **sRGB**:标准红绿蓝色彩空间,广泛用于显示器、图像文件。其值经过 gamma 编码,约等于 2.2 的幂函数。
- **线性空间**:光强度与数值成正比,用于物理渲染和光照计算。
- **Gamma 校正**:将线性值转换为 sRGB 供显示,或反向转换。
使用 Photoshop 转换
查看和分配色彩空间
- 打开图片,选择“编辑” > “颜色设置”。
- 在“工作空间”中设置 RGB 为 sRGB 或自定义线性。
- 要转换图片,使用“编辑” > “转换为配置文件”,选择目标色彩空间。
- 使用“图像” > “调整” > “色阶”或“曲线”,将中间调调整为 gamma 2.2(提亮)或 1/2.2(压暗)。
- **纹理导入**:在 Unity 或 Unreal 中,标记纹理为 sRGB(颜色贴图)或 Linear(法线、粗糙度等)。
- **渲染管线**:确保光照计算在线性空间,输出前转换为 sRGB。
- **精度**:使用浮点缓冲存储线性数据,避免 8 位精度损失。
- **一致性**:项目中统一色彩空间管理,避免混合。
- **浏览器**:Canvas 和 WebGL 需正确处理色彩空间。
手动 gamma 调整
使用 Python 进行转换
使用 Pillow 和 NumPy
安装库:
```bash
pip install Pillow numpy
```
转换函数示例:
```python
import numpy as np
from PIL import Image
def srgb_to_linear(img):
arr = np.array(img, dtype=np.float32) / 255.0
mask = arr <= 0.04045
arr[mask] = arr[mask] / 12.92
arr[~mask] = ((arr[~mask] + 0.055) / 1.055) ** 2.4
return arr
def linear_to_srgb(arr):
mask = arr <= 0.0031308
arr[mask] = arr[mask] * 12.92
arr[~mask] = 1.055 * (arr[~mask] ** (1/2.4)) - 0.055
return (arr * 255).astype(np.uint8)
```
使用着色器转换
在 GLSL 或 HLSL 中,GPU 可以自动处理 sRGB 纹理采样,但手动转换也常见。
GLSL 函数
```glsl
vec3 srgb_to_linear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));
}
vec3 linear_to_srgb(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
```
使用 OpenCV
OpenCV 通常假设图像为 BGR 顺序,且默认不进行 gamma 校正。可以使用 `cvtColor` 配合查找表。
实际应用
注意事项
总结
掌握色彩空间转换是图形开发的基础。通过正确设置图像处理软件、使用代码转换或在着色器中处理,可以确保颜色准确。
常见问题
sRGB 和线性空间有什么区别?
sRGB 经过 gamma 编码,更符合人眼感知,存储效率高;线性空间数值与光强度成正比,适合物理计算。
如何判断我的图片是 sRGB 还是线性?
通常颜色贴图是 sRGB,数据贴图(如法线、粗糙度)是线性。检查图片元数据或来源软件设置。
在 Unity 中如何设置纹理色彩空间?
在纹理导入设置中,将颜色贴图的 sRGB 选项勾选,数据贴图取消勾选。项目设置中可全局配置色彩空间。
为什么我的颜色在引擎中看起来过暗或过亮?
可能是色彩空间不匹配。检查纹理的 sRGB 标记,以及渲染输出的 gamma 设置。
Web 开发中需要考虑色彩空间转换吗?
是的,Canvas 和 WebGL 默认使用 sRGB,如果进行物理渲染需要手动转换为线性空间。