[openGL] 高级光照-Gamma矫正
目录
一 Gamma是什么?
二 感知光度和物理光度
2.1 与Gamma的关系
2.3 存在问题和弊端?
三 Gamma矫正(逆Gamma)
3.1 Gamma矫正的两种方法
3.2 sRGB空间
3.3 重复校正
3.3.1 在着色器中处理重复校正
3.3.2 在加载纹理时就重复校正
3.3.3 校正前后效果
本章节Qt源码点击此处
一 Gamma是什么?
在了解Gamma之前我们应该先知道一种物理特性: 输出亮度 = 输入电压 的 2.2次方幂
- 这是因为以前大多数的监视器是阴极射线显示器(CRT),这里后面我们就把显示器统称为监视器
这个经过2.2次方幂处理过后就相当于是一种压缩处理(可以理解为将图片变暗了至于为什么要这样处理将图片变暗,这将在后面进行说明,当然理解为压缩处理是不对的,但后面会修正。)\,每种显示设备的这个值都不一样,但是大多数都是2.2,这个2.2就叫做监视器的Gamma。
- 经过Gamma处理过的颜色都会变暗,但只是中间的颜色,一定要理解的是,他并不会影响全暗(0),以及全亮(1)的光度。
输出亮度 = 输入电压 ^ 2.2
二 感知光度和物理光度

要知道的是无论是感知光度还是物理光度:两边的光度(最暗和最亮的)总是相同的
感知光度: 其实就是人对光的感觉
- 因为我们人眼对比较暗的光是比较敏感的,而对亮光是不太敏感的,比如再黑暗中,稍微有点亮光我们就会感知出来,但是对于比较亮的环境中,即使增加光度
- 这样理解:由于人眼感知对较暗的光比较敏感,所以就会给暗光分配更多的精度(暗光的变化随着值的增加不会变化的很快)。
物理光度:
- 就是真实世界的光度,亮度的效果在0-1之间是线性增加的
在上面的图中,红色框代表的是人眼的感知光的变化,而下面的绿色框代表的是物理的真是光照。
2.1 与Gamma的关系
感知光度的形成:
对与我们输入给显示器也就是我们在程序或者应用中设置的颜色来讲,在传递给监视器后,监视器会根据自己的Gamma来进行处理,也就是对于我们设置的值0.5,他会进行Camma计算,变成0.5^2.2 = 0.218,这就会将光源变暗,从而从物理光照变成了人眼所感知的光照效果。
- 也就是说我们本身设置的想要他显示物理真实光照,但实际上经过监视器Gamma处理后,他显示出来的光照被放暗了,
- 颜色传递给监视器后经过监视器Gamma计算,将本该显示的物理光度(中间那条线性变化的值)压缩成凹下去的那条线的值,0.5^2.2 = 0.218。
- 最上面凸出去的那条线暂时不用管,其实就是一个逆的Gamma操作。
2.3 存在问题和弊端?

这会导致什么问题呢? 如果我们传给监视器前的光照是基于线性空间的,也就是上图的白色虚线
那监视器的Gmama就会给他进行Gmama计算,让他变到下面红色实线(凹下去的那条线),这就导致了,中间的亮度会被变暗,
和上面的例子一样,我们观察白色虚线(线性空间 也就是我们想要设置的真实物理光度)上的0.5,他就会被压缩至0.218,从而变暗.
这种压缩只是一种幂次方比例上的压缩,只是以某种幂次方比例压缩了中间值(Gamma校验无论怎样都不会影响最小的和最大的亮度也就是0和1)
弊端:
由于所有中间的亮度都是在线性空间计算的(传递给监视器处理前) ,所以这几乎导致了我们现在所看到的光照都不在准确的(因为他会进行Gamma计算),不是真实的物理上的光度,这就会导致我们会看到物体的效果并不会很有层次感,也就是说我们会看到更多的较暗的光度,而丢失很多比较亮的光度。
三 Gamma矫正(逆Gamma)
我们这里要先熟悉一下这两个概念
逆Gamma: 将线性的空间上供为上面凸出去的非线性空间
Gamma:将线性的空间下供为下面凹下去的非线性空间
简而言之就是给传入的值做一个1/2.2的幂次方计算,把这个值先变大,然后经过Gamma就会变回正常的线性空间的值。也就是真实的物理空间的值
- 其实Gamma矫正很简单,如果我们本身不存在监视器Gamma这个处理过程,那我们根本无需处理任何东西和计算,得到的就是真实的物理上的光度
- 但由于有了这个监视器Gamma导致我们看到的光度比实际物理上的光度暗了,那么很简单我们只需要给这个设定的值进行 逆运算,也就是说,你监视器会把我给定的值做做幂次方压缩导致我的线性空间变成了Camma空间(也就是红色实线凹下去的那条线),那我在给监视器之前我就给让我给定的这个值去做一个逆Gamma,也就是将这个线性空间变成上面的凸出去的那条线,这样再传递给监视器的时候,经过Gamma处理,他就还是正常的线性空间的值,显示出来的也就是真实的物理世界的值。
3.1 Gamma矫正的两种方法
- 让着色器每次运行后自动进行Gamma,这样就不需要人为处理了
glEnable(GL_FRAMEBUFFER_SRGB);
- 我们直接再发送到帧缓冲前,在每个相关像素着色器运行的最后应用gamma校正
void main()
{float gamma = 2.2;fragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma));
}
值得注意的是:我们对于Gamma的校正一定要是在最后输出的时候再进行校正,而不要在中间计算的时候就进行校正,因为中间值的一些属性:比如两个光照和的相加,混合等处理是要在线性空间计算的,这要值得注意
3.2 sRGB空间
监视器总是按照sRGB颜色空间的规则显示颜色,我们可以这样简单的理解:
- sRGB空间 是一个包含了预设Gamma特性的标准化颜色空间,旨在确保跨设备颜色的一致性。
- sRGB空间中的值: 我们可以这样理解他是将线性空间中的值进行逆Gamma处理后得到凸起的那条上供的非线性空间中的值。
- 这种值我们就不能手动在对他进行Gamma校正了。
3.3 重复校正
- 有的设计师在设计sRGB纹理图案时,会自动的将亮度提高,然后当他经过监视器的Gamma处理之后他就会变成正常的线性空间的颜色值。但此时使用着可能并不知道,手动又给他在最后输出给监视器之前进行了一次逆Gamma,这就导致最终输出在监视器上的亮度被放亮了。所以这就需要我们在进行Gamme校正之前判断。
3.3.1 在着色器中处理重复校正
float gamma = 2.2;
vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));
// 首先调整环境光照和漫反射光照的亮度
shaderProgramObject.setUniformValue("light.ambient", QVector3D(0.3f, 0.3f, 0.3f));
shaderProgramObject.setUniformValue("light.diffuse", QVector3D(0.7f, 0.7f, 0.7f));// 将漫反射光照进行Gamma处理vec3 diffuseTexColor=vec3(texture(material.texture_diffuse1,TexCoords));float gamma = 2.2;diffuseTexColor = pow(diffuseTexColor,gamma);
- 但是为每个sRGB空间的纹理这样设置是很烦人的,
3.3.2 在加载纹理时就重复校正
OpenGL给我们提供了另一个方案来解决我们的麻烦,这就是GL_SRGB和GL_SRGB_ALPHA内部纹理格式。
如果我们在OpenGL中创建了一个纹理,把它指定为以上两种sRGB纹理格式其中之一,OpenGL将自动把颜色校正到线性空间中,这样我们所使用的所有颜色值都是在线性空间中的了。我们可以这样把一个纹理指定为一个sRGB纹理:
QImage wall = QImage(":/wood.png").convertToFormat(QImage::Format_RGB888);m_planeTex = new QOpenGLTexture(QOpenGLTexture::Target2D);glBindTexture(GL_TEXTURE_2D,m_planeTex->textureId());glTexImage2D(GL_TEXTURE,0,GL_SRGB,wall.width(),wall.height(),0,GL_RGB,GL_UNSIGNED_BYTE,wall.bits());glGenerateMipmap(GL_TEXTURE_2D);m_planeMesh = processMesh(planeVertices,6,m_planeTex->textureId());
3.3.3 校正前后效果
- 未处理重复校正前的效果:我们会发现这个亮度很高,这是因为漫反射纹理的颜色空间已经在sRGB中空间(说明他已经进行了逆Gamma处理)了,但是我们在着色器的最后又进行了一次逆Gamma处理,两次的逆Gamma处理就会导致亮度异常大。

- 处理后的效果: 我们要注意 漫反射纹理已经在sRGB空间中了,我们在着色器最后又给他逆Gamma那肯定会异常亮
- 并且我们要保证中间的光照计算要在线性空间中,所以我们需要在获取到漫反射纹理后,对他进行Gamma处理,把他变到线性空间中,这样保证了他后续的计算不会出问题,然后在再着色器最后输出时进行一次逆Gamma处理这样就能得到正确的颜色了。

值得注意的:
- 不是所有的纹理都是在sRGB空间中的所有我们如果要把纹理指定为sRGB纹理时一定要注意
- 一般像漫反射光照这种为物体上色的纹理几乎都是在sRGB空间中的
- 但是像镜面光贴图和法线贴图几乎都在线性空间中,所以如果你把它们也配置为sRGB纹理的话,光照就会连续被衰弱两次,这样光照几乎就坏掉了。
相关文章:
[openGL] 高级光照-Gamma矫正
目录 一 Gamma是什么? 二 感知光度和物理光度 2.1 与Gamma的关系 2.3 存在问题和弊端? 三 Gamma矫正(逆Gamma) 3.1 Gamma矫正的两种方法 3.2 sRGB空间 3.3 重复校正 3.3.1 在着色器中处理重复校正 3.3.2 在加载纹理时就重复校正 3.3.3 校正前后效果 本章节Qt源码点…...
Prometheus+Grafana监控K8S集群(基于K8S环境部署)
目录 一.环境信息二.部署提前工作三.部署Prometheus监控系统四.部署Node_exporter组件五.部署Kube_state_metrics组件六.部署Grafana可视化平台七.Grafana接入Prometheus数据八.Grafana添加监控模板九.拓展 一.环境信息 1.服务器及k8s版本信息 IP地址主机名称角色版本192.168…...
[opencv]VideoWriter写出fourcc格式
fourcc支持的格式 fourcc全名Four-Character Codes,四字符代码,该编码由四个字符组成 cv2.VideoWriter_fourcc(O,O,O,O) cv2.VideoWriter_fourcc(*OOOO) 通常写法有上述两种形式,O代表一个字符,通常有 支持avi格式的有&#…...
软考中级网络工程师-网络技术
下列命令片段含义是( )。 system-view [HUAWEI] observe-port 1 interface gigabitethernet 0/0/1 [HUAWEI] interface gigabitethernet 0/0/2 [HUAWEI-GigabitEthernet0/0/2] port-mirroring to observe-port 1 inbound A 配置端口镜像 B 配置链路聚合 C 配置逻辑接口 D 配置访…...
cmake基础教程(12)函数和宏用法
参考: https://cmake.org/cmake/help/latest/command/function.html https://cmake.org/cmake/help/latest/command/macro.html#command:macro 文章目录 函数宏在CMake中,宏(macro)和函数(function)命令用于封装重复的任务,这些任务可能分散在你的CMakeLists文件中。一…...
SQLite的PRAGMA 声明(二十三)
返回:SQLite—系列文章目录 上一篇:SQLite从出生到现在(发布历史记录)(二十二) 下一篇:用于 SQLite 的异步 I/O 模块(二十四) PRAGMA 语句是特定于 SQLite 的 SQL 扩…...
Qt 实战(1)Qt 概述
一、Qt概述 1、什么是Qt? Qt(官方发音 [kju:t],音同 cute)是一个跨平台的 C 开发库,主要用来开发图形用户界面(Graphical User Interface,GUI)程序,也可以开发不带界面的…...
【练习】二分查找
1、704 (1)题目描述 (2)代码实现 package com.hh.practice.leetcode.array.demo_02;public class BinarySearch_704 {public int search(int[] nums, int target) {int i 0,j nums.length -1;while (i < j){int mid (ij) &…...
FactoryTalk View 上位机画面版本升级,还原和备份
FactoryTalk View 上位机画面版本升级,还原和备份 1 归档文件(尾缀.apa)升级2 画面文件(尾缀.sed)升级3 提示“目标工程中包含旧的HMI标签报警,FT View 10.0是最后一个......” 解决方法1 归档文件(尾缀.apa)升级 案例是FTVIEW5.0升级到FT VIEW12,需要用FT VIEW 6过渡升…...
【微信小程序】分包
整个小程序所有分包大小不超过 20M(开通虚拟支付后的小游戏不超过30M) 单个分包/主包大小不能超过 2M在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,客户端会把对应分包下载下来…...
Golang教程六(单元测试,反射,网络编程,部署)
目录 一、单元测试 单元测试 子测试 TestMain 二、反射 类型判断 通过反射获取值 通过反射修改值 结构体反射 利用tag修改结构体的某些值 调用结构体方法 orm的一个小案例 对反射的一些建议 三、网络编程 socket编程 websocket编程 四、部署 打包命令 交叉编译…...
mybatis进阶篇-执行CRUD操作-typeAliases别名-接口绑定
目录结构 1.创建数据表(book) # 创建book表 create table book(id int auto_increment primary key,name varchar(255) ,price double ,num int );2.mybatis.xml配置文件 <?xml version"1.0" encoding"UTF-8" ?> <!DOC…...
C#面:泛型的主要约束和次要约束是什么
在 C# 中,泛型的约束是用来限制泛型类型参数的行为和能力的。 主要约束和次要约束是两种不同的约束方式。 主要约束(Primary Constraint): 主要约束指定了泛型类型参数必须满足的最基本的条件,它可以是一个类、一个接…...
Java使用documents4j将word和excel转pdf
pom.xml添加documents4j依赖 <!-- documents4j --> <dependency><groupId>com.documents4j</groupId><artifactId>documents4j-local</artifactId><version>1.0.3</version> </dependency> <!-- documents4j 转 wor…...
使用策略模式实现 Spring 分布式和单机限流
我们可以使用策略模式来统一单机限流和分布式限流的实现,提高代码的可扩展性和可维护性。 思路是定义一个 RateLimitStrategy 接口,并分别实现单机限流策略 LocalRateLimitStrategy 和分布式限流策略 DistributedRateLimitStrategy。在 AOP 切面中,根据配置决定使用哪种限流策…...
@CrossOrigin注解解决跨域问题
文章目录 一、什么是跨域二、CrossOrigin注解是干什么用的三、用法 一、什么是跨域 跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。 所谓同源是指,域名,协议&…...
【力扣】45. 跳跃游戏 II
Problem: 45. 跳跃游戏 II 文章目录 问题思路复杂度Code 问题 思路 核心思路,例如nums[i]5,那么最远能跳五步; //那么在这接下来1-5范围内,哪个能让我跳的最远,这个最远指的是 -------------------------------------…...
【Python基础】19.eval函数的使用
eval函数 eval()将字符串转变为有效的表达式来求值并返回对应的结果 基础数据计算 In [1]: eval("1 1") Out[1]: 2字符串重复 In [2]: eval (" * * 10") Out[2]: **********字符串转为列表 In [3]: type(eval("[1,2,3,4,5]")) Out[3]: lis…...
对装饰器模式的理解
目录 一、场景二、面对场景中的新需求,我们怎么办?1、暴力法:直接修改原有的代码。2、子类继承法:既然要增强行为,那我搞一个子类,覆写不就完事了?3、装饰器模式 三、对装饰器模式的思考1、从代…...
在替换微软AD的CA证书服务AD CS前,要先做哪些准备工作?
AD CS是什么 关于这个问题,有几个概念需要先弄明白:PKI、CA、数字证书。 PKI(Public Key Infrastructure,公钥基础设施)是提供公钥加密和数字签名服务的系统或平台,实现基于公钥密码体制的密钥和证书的产生…...
一文读懂水面无人艇:每个硬件模块到底负责什么
目录 一、水面无人艇完整系统 二、硬件搭配负责哪些功能 2.1 艇体模块:决定“能不能稳、能不能装、能不能扛风浪” 2.2 动力与航行执行模块:决定“怎么动” 2.3 导航传感器模块:决定“我现在在哪、朝哪、跑多快” 1)GPS / 北…...
用Segment Anything Model (SAM) 做3D目标检测?手把手教你复现SAM3D论文核心流程
从BEV到3D检测:基于Segment Anything的零样本实践指南 当Meta的Segment Anything Model(SAM)横空出世时,计算机视觉领域掀起了一阵"分割一切"的浪潮。但大多数应用仍停留在2D图像领域,直到SAM3D论文提出将这…...
大脑极简原理:比冯·诺依曼架构还简单的电磁路由网络 ——为什么意识和智能会从“对称判断”里自然涌现
前言:被复杂化的真相——大脑其实简单到爆我们从小被灌输一个观念:大脑是宇宙中最复杂的系统,860亿神经元、百万亿突触、无数神经递质,像一台精密到无法拆解的超级计算机。神经科学论文越写越长,模型越来越复杂&#x…...
WikiJS全文搜索实战:用ElasticSearch+IK分词器提升内容检索效率(Docker版)
WikiJS全文搜索实战:ElasticSearch与IK分词器的深度优化指南 引言:为什么需要专业级全文搜索解决方案? 想象一下,当你面对一个包含数千篇技术文档的Wiki系统时,传统的关键词匹配就像在黑暗房间里寻找一根针。WikiJS自带…...
如何快速掌握React Email Editor:深入理解拖拽邮件编辑器的实现原理
如何快速掌握React Email Editor:深入理解拖拽邮件编辑器的实现原理 【免费下载链接】react-email-editor Drag-n-Drop Email Editor Component for React.js 项目地址: https://gitcode.com/gh_mirrors/re/react-email-editor React Email Editor是一个功能…...
用快马ai五分钟生成java学习路线可视化原型,清晰规划你的编程进阶之路
今天想和大家分享一个特别实用的Java学习路线可视化工具的开发过程。作为一个Java初学者,我经常被各种知识点搞得晕头转向,直到发现用InsCode(快马)平台可以快速搭建一个学习路线图,整个开发过程只用了不到半小时,效果却出奇地好。…...
ai结对编程实践:如何利用kimi在快马平台智能辅助完成用户认证系统开发
AI结对编程实践:如何利用Kimi在快马平台智能辅助完成用户认证系统开发 最近在开发一个需要用户认证功能的项目,后端用Node.js Express,前端用Vue。作为一个独立开发者,面对这种前后端都要兼顾的情况,我决定尝试用Kimi…...
CANdb++ Editor高效使用技巧:5个隐藏功能大幅提升dbc编辑效率
CANdb Editor高效使用技巧:5个隐藏功能大幅提升dbc编辑效率 在汽车电子开发领域,Vector的CANdb Editor堪称dbc文件编辑的行业标准工具。大多数工程师都能熟练使用其基础功能,但真正的高手往往掌握着那些鲜为人知的"秘密武器"。本文…...
ImageMagick安装后报错‘vcomp140.dll缺失’?手把手教你彻底解决Visual C++依赖问题
ImageMagick安装后报错‘vcomp140.dll缺失’?手把手教你彻底解决Visual C依赖问题 当你兴冲冲下载完ImageMagick准备大展身手时,命令行却突然弹出一串红色错误提示——"无法启动程序,因为计算机中丢失vcomp140.dll"。这种场景对于…...
深度学习驱动的图像去雾:2023年最新算法与应用实践
1. 图像去雾技术的现状与挑战 清晨打开窗户,如果外面雾气弥漫,我们往往会等雾散了再拍照。但计算机视觉系统可没这个耐心——自动驾驶汽车必须实时看清路况,无人机巡检得在雾天正常工作。这就是图像去雾技术存在的意义。2023年,随…...
