图形学基础——伽马校正

article/2025/7/27 19:07:27

百人计划学习视频连接:【技术美术百人计划】图形 2.6 伽马校正


颜色空间

具体内容在之前的文章有讲:色彩空间介绍

  • sRGB:微软在1996年发布的通用颜色标准
  • DCI-P3:数字电影播放的颜色标准
  • Rec-709、PAL:电视行业的颜色标准
    在这里插入图片描述
    图中可以看出sRGB和Rec-709表示的颜色范围很接近,三原色的位置也是相同的

    那他们的区别在哪里呢?

    答案是 他们的传递函数的不同

什么是传递函数

当我们已经知道了颜色空间下三原色的值,但是我们需要将它显示到电子设备上,那就需要把它转换成一个视频信号。
这里就需要用到一个转换函数,也叫传递函数
在这里插入图片描述
一个传递函数包含2个部分:

  • OEFT 把光转换为电子信号(例如:拍摄视频的时候,将场景中的光保存成信号)
  • EOFT 把电子信号转换成光信号(例如:播放视频的时候,将视频中的信号转换成光强度)

而传递函数就是Gamma校正所使用的一个函数

Gamma校正

什么是Gamma校正?
就是指对线性三色值和非线性视频信号之间进行编码和解码的操作

也可以简单的定义为:在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
那为什么不直接用线性方式存储颜色信号呢?

首先我们知道了图像的来龙去脉:自然界捕获的图像 -> 图像存储和处理->显示器输出

而两头的颜色数量都是非常丰富的,但是中间的存储和处理出于存储容量和渲染时带宽的限制,24位色图片每个通道只有28个色阶,总共只能显示224种颜色。

如过是32位通道的图片,确实可以直接存储物理光强,因为存储空间足够;但是目前主流使用还是8位通道的图片

因此,我们得出结论

  • 主要是为了优化存储空间和带宽,传递函数能更好的帮我们利用编码空间
  • 人眼对暗部的变化更加敏感,为了充分的利用带宽,那么就需要使用更多位置存储暗部值。也就是说暗部使用更高精度保存,而亮部使用更低精度保存

韦伯定律

在这里插入图片描述
正常人会觉得是上面更均匀,但是实际下面才是均匀变化的。

将自然界线性增长的亮度和心理上感受到的亮度进行一个映射得到下面的曲线,就是Gamma编码的曲线
在这里插入图片描述
这种情况也符合一个定律,就是韦伯定律
在这里插入图片描述

小结

  1. 人眼对暗部的变化比亮部更加敏感
  2. 我们目前所使用的RGBA32,每个颜色通道只有8位用于记录信息,为了合理使用带宽和存储空间,需要进行非线性转换
  3. 目前我们所普遍使用的sRGB颜色空间标准,他的传递函数gamma值为2.2

CRT

早期的人们是如何修正人眼的视觉感受和物理亮度的差异
在这里插入图片描述
CRT与转换函数
在这里插入图片描述

中灰值

中灰值是什么?
在这里插入图片描述
在这里插入图片描述
中灰值并非是一个固定的具体数值,而是取决于视觉感受

线性工作流

在这里插入图片描述
为了保证我们在着色器中拿到的颜色值是线性空间下的颜色值

如果不在线性空间下进行渲染工作,会产生什么问题
在这里插入图片描述
在这里插入图片描述

Unity中的颜色空间

在这里插入图片描述

  • 选择Gamma Space时,Unity不会做任何处理
  • 当选择Linear Space时,引擎的渲染流程在线性空间计算,理想情况下项目使用线性空间的贴图颜色,不需要勾选sRGB,如果勾选了sRGB的贴图,会通过硬件特性采样时进行线性转换。

在这里插入图片描述
Unity目前主要通过以下两个硬件特性来支持
在这里插入图片描述

资源导出问题

在这里插入图片描述

当Substance的贴图导出时,线性的颜色值经过伽马变换,颜色被提亮了,所以需要在Unity中勾选sRGB选项,让它在采样时能还原回线性值。
在这里插入图片描述
如果使用线性空间,一般来说Photoshop可以什么都不改,导出的贴图只要勾上sRGB就可以了。如果调整PhotoShop的伽玛值为1,导出的贴图在Unity中也不需要勾选sRGB了。

关于Color profile

在这里插入图片描述

半透明效果不一致在这里插入图片描述

Unity中的混合是线性混合,Photoshop的图层和图层之间做混合的时候,每个上层图层都经过了伽马变换,然后才做了混合。在设置中更改,选择“用灰度系数混合RGB颜色”,参数设置为1,这样图层才是直接混合的结果。

作业

手动尝试几种伽马校正的方法

1. 直接修改Unity3d的设置为Linear空间进行gamma校正

写了一个简单的带纹理的blin-phong模型

fixed4 frag (v2f i) : SV_Target
{fixed3 MainTex = tex2D(_MainTex, i.uv) * _DiffuseColor.rgb;float3 normalDir = normalize(i.worldNormal);float3 lightDir = normalize(UnityWorldSpaceLightDir(_WorldSpaceLightPos0.xyz)) ;float3 Diffuse =  _LightColor0.rgb * MainTex.rgb *  saturate(dot(lightDir, normalDir));float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);float3 halfDir = normalize(lightDir + viewDir);float3 Specular = _LightColor0.rgb * pow(saturate(dot(halfDir, normalDir)), _Gloss);float3 Ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * MainTex.rgb;float4 col = fixed4(Diffuse + Specular, 1);return col;
}

Gamma空间:
在这里插入图片描述

Linear空间:
在这里插入图片描述

2. 在shader中手动进行gamma校正

fixed4 frag (v2f i) : SV_Target
{fixed3 MainTex = tex2D(_MainTex, i.uv) * _DiffuseColor.rgb;//先将Gamma空间的颜色值进行一次转换,转换到Linear空间。MainTex.rgb = GammaToLinearSpace(MainTex.rgb);float3 normalDir = normalize(i.worldNormal);float3 lightDir = normalize(UnityWorldSpaceLightDir(_WorldSpaceLightPos0.xyz)) ;float3 Diffuse =  _LightColor0.rgb * MainTex.rgb *  saturate(dot(lightDir, normalDir));float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);float3 halfDir = normalize(lightDir + viewDir);float3 Specular = _LightColor0.rgb * pow(saturate(dot(halfDir, normalDir)), _Gloss);float3 Ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * MainTex.rgb;float4 col = fixed4(Diffuse + Specular, 1);//输出的时候,再进行一次逆转换,从Linear空间转换回Gamma空间。col.rgb = LinearToGammaSpace(col.rgb * unity_ColorSpaceDouble);return col;
}

注:最后转换回Gamma Space的时候,我在Color上乘了一个Unity_ColorSpaceDouble。Unity_ColorSpaceDouble是一个Unity提供的与色彩空间相关的值,这个值在Gamma颜色空间时为2,在Linear Color Space时为4.594(2的2.2次方)。
对于这个值可以这样来理解。一般在Gamma颜色空间中将两个Color值相乘后,为了避免颜色变得很暗,会在后面乘以2。
也就是说,为了避免颜色变暗,应该扩大两倍,但是这个值在不同空间下不一样,所以要使用Unity_ColorSpaceDouble。

原效果:
在这里插入图片描述
加上gamma校正后的效果
在这里插入图片描述


参考学习:
《Unity+Shader入门精要》——冯乐乐著
https://blog.csdn.net/candycat1992/article/details/46228771


http://chatgpt.dhexx.cn/article/QuwovPHD.shtml

相关文章

伽马校正

注:本文为博主参考书籍和他人文章并加上自己的理解所编,作为学习笔记使用并将其分享出去供大家学习。若涉及到引用您的文章内容请评论区告知!如有错误欢迎指正! 一、伽马校正简介 射伽马校正是图像预处理阶段经常使用的一个非…

可执行文件的装载,进程和线程,运行时库的入口函数(第六章)

目录 第1步.创建一个独立的虚拟内存空间 第2步.读取可执行文件头,建立虚拟空间和可执行文件之间的映射关系 第3步.将cpu的指令寄存器设置成可执行文件的入口地址,启动运行。 程序开始执行,发生页错误。 随着程序的执行,不停…

疯狂python讲义学习笔记——前十章完结

#第一章&#xff1a;绪论 #单行注释多行注释""" 多行注释 """#dir列出指定类或模块的属性与方法&#xff0c;help查看某个函数或方法的帮助文档 print(dir(str)) print(help(str.title))#第二章&#xff1a;数据类型 a5 print(type(a))#<clas…

嵌入式代码学习心得记录

一、C语言学习心得记录 函数递归 编写顺序 终结条件输入下一级递归参数,调用下一级递归函数.当前递归函数的操作代码,在下一级递归函数执行完成后执行的操作代码. #include <stdio.h> #include <string.h>void revert(char *s, int len) {// 终结条件if(len <…

python 字节流分段_一文掌握CTF中Python全部考点

声明&#xff1a;Tide安全团队原创文章&#xff0c;转载请声明出处&#xff01;文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用&#xff0c;任何人不得将其用于非法用途以及盈利等目的&#xff0c;否则后果自行承担&#xff01; 前 言 一次偶然的机会&#xff0c…

Python课程学习笔记 下

笔记目录 Python 学习笔记 上面向对象和异常面向对象基本理论定义类根据类实例化对象属性相关方法相关类的补充属性补充方法相关补充描述器python对象的生命周期内存管理机制面向对象的三大特性面向对象应当遵循的原则 错误和异常错误和异常的概念常见的系统异常和系统异常类继…

从点击APP图标到首页展示的加载过程

转载链接在这里 iOS 系统架构 Mac系统是基于Unix内核的图形化操作系统&#xff0c;Mac OS 和 iOS 系统架构的对比分析发现&#xff0c;Mac OS和iOS的系统架构层次只有最上面一层不同&#xff0c;Mac是Cocoa框架&#xff0c;而iOS是Cocoa Touch框架&#xff0c;其余的架构层次都…

python os模块

10.3 os模块 os就是“operating system”的缩写&#xff0c;顾名思义&#xff0c;os模块提供的就是各种 Python 程序与操作系统进行交互的接口。通过使用os模块&#xff0c;一方面可以方便地与操作系统进行交互&#xff0c;另一方面页可以极大增强代码的可移植性。如果该模块中…

Python:异常处理,模块和包,正则表达式

1&#xff0c;异常处理机制 Python的异常处理机制可以让程序具有极好的容错性&#xff0c;让程序更加健壮。当程序运行出现意外情况时&#xff0c;系统会自动生成一个Error对象来通知程序&#xff0c;从而实现将“业务实现代码”和“错误处理代码”分离&#xff0c;提供更好的可…

操作系统面试题总结

进程的常见状态&#xff1f;以及各种状态之间的转换条件? 创建状态&#xff1a;进程在创建时需要申请一个空白PCB&#xff0c;向其中填写控制和管理进程的信息&#xff0c;完成资源分配。如果创建工作无法完成&#xff0c;比如资源无法满足&#xff0c;就无法被调度运行&#…

Python--多线程与并行

一、线程和进程介绍 1.1、进程基本概念 进程&#xff08;Process&#xff09;&#xff0c;是计算机中已运行程序的实体&#xff0c;曾经是分时系统的基本运作单位。在面向进程设计的系统&#xff08;如早期的Unix、Linux2.4及更早的版本&#xff09;中&#xff0c;进程是程序…

【Python学习】--pythonf笔记

系列文章目录 文章目录 系列文章目录前言一、Python代码规范&#xff08;编码、代码格式&#xff09;1. 编码2. 代码格式3. import 语句4. 空格 二、Python代码注释&#xff08;块注释、行注释、文档注释&#xff09;1. 块注释2. 行注释3. 建议4. 文档注释 三、Python命名规范&…

进程和信号

进程和信号 一、进程初识 1、程序的开始和结束 开始&#xff1a;编译链接时的引导代码。操作系统下的应用程序其实在main执行前也需要先执行一段引导代码才能去执行main。在程序链接时由链接器将编译器中事先准备好的引导代码给链接进去与我们的代码一起组成最终的可执行程序…

什么!学Python多进程的你还不知道multiprocessing模块?该充电了>_(Python编程 | 系统编程 | 并行系统工具 | multiprocessing模块)

文章目录 multiprocessing模块基本操作&#xff1a;进程和锁关于实现和用法的规则 *IPC*工具&#xff1a;管道、共享内存和队列管道共享内存和全局对象队列和子类 启动独立程序其他更多 multiprocessing模块 Python标准库中的multiprocessing模块允许脚本通过与threading模块非…

一文入门Python基础

Python基础 python中的输出函数 print()函数 可以输出的内容 数字字符串含有运算符的表达式(会返回表达式计算的结果) 内容输出的目的地 显示器文件 # 将数据输入文件中&#xff0c;注意点——所指的盘必须存在——使用filefp fp open(路径,模式) print(hello,file fp) fp.…

深入理解Linux进程管理(1.0)

学习方法论 写作原则 标题括号中的数字代表完成度与完善度 0.0-1.0 代表完成度&#xff0c;1.1-1.5 代表完善度 0.0 &#xff1a;还没开始写 0.1 &#xff1a;写了一个简介 0.3 &#xff1a;写了一小部分内容 0.5 &#xff1a;写了一半内容 0.9 &#xff1a;还有个别内容没写 1…

一、Linux系统编程:进程基础

1 进程基础 1.1 概念 定义 程序在计算机上的一次执行过程&#xff0c;执行中的程序。本质 1、程序在地址空间中按照代码逻辑控制流执行 2、资源分配最小单位 进程是一个抽象概念 1.2 进程与程序 区别 进程程序动态静态有生命周期指令集合只能对应一个程序可以对应多个进程…

YARN源码解析之NodeManager中的ContainerExecutor

在NodeManager中&#xff0c;有三种运行Container的方式&#xff0c;它们分别是: DefaultContainerExecutorLinuxContainerExecutorDockerContainerExecutor 从它们的名字中&#xff0c;我们就能看得出来&#xff0c;默认情况下&#xff0c;一定使用的是DefaultContainerExec…

linux进程状态怎么手动切换,二十六、Linux 进程与信号---system 函数 和进程状态切换...

26.1 system 函数 26.1.1 函数说明 system(执行shell 命令) 相关函数 fork&#xff0c;execve&#xff0c;waitpid&#xff0c;popen #include int system(const char * string); 函数功能&#xff1a;简化 exec 函数 函数说明 system()会调用 fork() 产生子进程&#xff0c;由…

进程控制(详解)

进程控制 上篇文章介绍了进程的相关概念&#xff0c;形如进程的内核数据结构task_struct 、进程是如何被操作系统管理的、进程的查看、进程标识符、进程状态、进程优先级、已经环境变量和进程地址空间等知识点&#xff1b; 本篇文章接着上篇文章继续对进程的控制进行展开&#…