opengl高级光照之gamma校正

article/2025/10/16 0:35:26

官方文章

gamma校正

gamma校正概念

在这里插入图片描述

  • 一个渐变的效果
    在这里插入图片描述
  • 通过以下网站调整Gamma值可以观察到效果
    色彩管理网

gamma校正

Gamma校正(Gamma Correction)的思路是在最终的颜色输出上应用监视器Gamma的倒数。回头看前面的Gamma曲线图,你会有一个短划线,它是监视器Gamma曲线的翻转曲线。我们在颜色显示到监视器的时候把每个颜色输出都加上这个翻转的Gamma曲线,这样应用了监视器Gamma以后最终的颜色将会变为线性的。我们所得到的中间色调就会更亮,所以虽然监视器使它们变暗,但是我们又将其平衡回来了。

我们来看另一个例子。还是那个暗红色(0.5,0.0,0.0)。在将颜色显示到监视器之前,我们先对颜色应用Gamma校正曲线。线性的颜色显示在监视器上相当于降低了2.2次幂的亮度,所以倒数就是1/2.2次幂。Gamma校正后的暗红色就会成为(0.5,0.0,0.0)1/2.2=(0.5,0.0,0.0)0.45=(0.73,0.0,0.0)。校正后的颜色接着被发送给监视器,最终显示出来的颜色是(0.73,0.0,0.0)2.2=(0.5,0.0,0.0)。你会发现使用了Gamma校正,监视器最终会显示出我们在应用中设置的那种线性的颜色。
有两种在你的场景中应用gamma校正的方式:

使用OpenGL内建的sRGB帧缓冲。 自己在像素着色器中进行gamma校正。 第一个选项也许是最简单的方式,但是我们也会丧失一些控制权。开启GL_FRAMEBUFFER_SRGB,可以告诉OpenGL每个后续的绘制命令里,在颜色储存到颜色缓冲之前先校正sRGB颜色。sRGB这个颜色空间大致对应于gamma2.2,它也是家用设备的一个标准。开启GL_FRAMEBUFFER_SRGB以后,每次像素着色器运行后续帧缓冲,OpenGL将自动执行gamma校正,包括默认帧缓冲。

开启GL_FRAMEBUFFER_SRGB简单的调用glEnable就行:

glEnable(GL_FRAMEBUFFER_SRGB);

自此,你渲染的图像就被进行gamma校正处理,你不需要做任何事情硬件就帮你处理了。有时候,你应该记得这个建议:gamma校正将把线性颜色空间转变为非线性空间,所以在最后一步进行gamma校正是极其重要的。如果你在最后输出之前就进行gamma校正,所有的后续操作都是在操作不正确的颜色值。例如,如果你使用多个帧缓冲,你可能打算让两个帧缓冲之间传递的中间结果仍然保持线性空间颜色,只是给发送给监视器的最后的那个帧缓冲应用gamma校正。

sRGB纹理(要看官方文档)

因为监视器总是在sRGB空间中显示应用了gamma的颜色,无论什么时候当你在计算机上绘制、编辑或者画出一个图片的时候,你所选的颜色都是根据你在监视器上看到的那种。这实际意味着所有你创建或编辑的图片并不是在线性空间,而是在sRGB空间中(译注:sRGB空间定义的gamma接近于2.2),假如在你的屏幕上对暗红色翻一倍,便是根据你所感知到的亮度进行的,并不等于将红色元素加倍。

衰减

在使用了gamma校正之后,另一个不同之处是光照衰减(Attenuation)。真实的物理世界中,光照的衰减和光源的距离的平方成反比。

    // simple attenuationfloat max_distance = 1.5;float distance = length(lightPos - fragPos);float attenuation = 1.0 / (gamma ? distance * distance : distance);

gamma校正代码(要看官方文档)

  • 顶点着色器:顶点着色器:VAO获取顶点数据,接口块传递值,缓冲取值,绘制图形

    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aNormal;
    layout (location = 2) in vec2 aTexCoords;out VS_OUT {vec3 FragPos;vec3 Normal;vec2 TexCoords;
    } vs_out;uniform mat4 projection;
    uniform mat4 view;void main()
    {vs_out.FragPos = aPos;vs_out.Normal = aNormal;vs_out.TexCoords = aTexCoords;gl_Position = projection * view * vec4(aPos, 1.0);
    }
    
  • 片段着色器

    #version 330 core
    out vec4 FragColor;in VS_OUT {vec3 FragPos;vec3 Normal;vec2 TexCoords;
    } fs_in;uniform sampler2D floorTexture;uniform vec3 lightPositions[4];
    uniform vec3 lightColors[4];
    uniform vec3 viewPos;
    uniform bool gamma;vec3 BlinnPhong(vec3 normal, vec3 fragPos, vec3 lightPos, vec3 lightColor)
    {// diffusevec3 lightDir = normalize(lightPos - fragPos);float diff = max(dot(lightDir, normal), 0.0);vec3 diffuse = diff * lightColor;// specularvec3 viewDir = normalize(viewPos - fragPos);vec3 reflectDir = reflect(-lightDir, normal);float spec = 0.0;vec3 halfwayDir = normalize(lightDir + viewDir);  spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0);vec3 specular = spec * lightColor;    // 衰减float max_distance = 1.5;float distance = length(lightPos - fragPos);float attenuation = 1.0 / (gamma ? distance * distance : distance);//修改光照的gamma效果diffuse *= attenuation;specular *= attenuation;return diffuse + specular;
    }void main()
    {           vec3 color = texture(floorTexture, fs_in.TexCoords).rgb;vec3 lighting = vec3(0.0);for(int i = 0; i < 4; ++i)lighting += BlinnPhong(normalize(fs_in.Normal), fs_in.FragPos, lightPositions[i], lightColors[i]);color *= lighting;//比较不同的加载纹理方式//选用的纹理是一样的但是透明值是发生改变的if(gamma)color = pow(color, vec3(1.0/2.2));FragColor = vec4(color, 1.0);
    }
    
  • 主函数

  • 修改输入函数按“空格键”改变gamma传值(通过不同的纹理传递效果:SRGB和RGB,又因为SRGB的时候值发生改变所以在片段着色器中才有了if(gamma)中的内容)

    void processInput(GLFWwindow* window)
    {if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)camera.ProcessKeyboard(FORWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.ProcessKeyboard(BACKWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.ProcessKeyboard(LEFT, deltaTime);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.ProcessKeyboard(RIGHT, deltaTime);if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS && !gammaKeyPressed){gammaEnabled = !gammaEnabled;gammaKeyPressed = true;}if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_RELEASE){gammaKeyPressed = false;}
    }
    
  • 纹理加载函数:gamma为true则图片都想下拱了一次从0.5-0.2,是否进行gamma校正,可查看官方文档

    // utility function for loading a 2D texture from file
    // ---------------------------------------------------
    unsigned int loadTexture(char const* path, bool gammaCorrection)
    {unsigned int textureID;glGenTextures(1, &textureID);int width, height, nrComponents;unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);if (data){GLenum internalFormat;GLenum dataFormat;if (nrComponents == 1){internalFormat = dataFormat = GL_RED;}//以下都进行了gamma判断到底是加载SRGB还是RGBelse if (nrComponents == 3){internalFormat = gammaCorrection ? GL_SRGB : GL_RGB;dataFormat = GL_RGB;}//GL_SRGB_ALPHA 还是GL_RGBAelse if (nrComponents == 4){internalFormat = gammaCorrection ? GL_SRGB_ALPHA : GL_RGBA;dataFormat = GL_RGBA;}glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, dataFormat, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);stbi_image_free(data);}else{std::cout << "Texture failed to load at path: " << path << std::endl;stbi_image_free(data);}return textureID;
    }
    
  • while循环:注意光照的相关信息是一个数组,所以传值需要用到uniform

    	// load textures// -------------unsigned int floorTexture = loadTexture("resources/textures/wood.png", false);unsigned int floorTextureGammaCorrected = loadTexture("resources/textures/wood.png", true);............// render loop// -----------while (!glfwWindowShouldClose(window)){// per-frame time logic// --------------------.....// draw objectsshader.useShader();glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();shader.setMat4("projection", projection);shader.setMat4("view", view);// set light uniformsglUniform3fv(glGetUniformLocation(shader.ID, "lightPositions"), 4, &lightPositions[0][0]);glUniform3fv(glGetUniformLocation(shader.ID, "lightColors"), 4, &lightColors[0][0]);shader.setVec3("viewPos", camera.Position);shader.setInt("gamma", gammaEnabled);// floorglBindVertexArray(planeVAO);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, gammaEnabled ? floorTextureGammaCorrected : floorTexture);glDrawArrays(GL_TRIANGLES, 0, 6);std::cout << (gammaEnabled ? "Gamma enabled" : "Gamma disabled") << std::endl;// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)// -------------------------------------------------------------------------------glfwSwapBuffers(window);glfwPollEvents();}
    
  • 效果

关闭在这里插入图片描述

开启在这里插入图片描述


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

相关文章

gama校正算法matlab,Gamma校正及其FPGA实现(一)

最近在做一个千兆以太网传输的项目&#xff0c;里面用到了Gama校正算法&#xff0c;本来想使用Altera 的IP核Gamma Corrector&#xff0c;但Quartus16.0把Gamma Corrector的IP核集成到了QSYS里面了&#xff0c;还得研究qsys怎么用&#xff0c;与自己本来的方案不符合&#xff0…

Gamma Correction/Gamma校正/灰度校正/亮度校正 - 部分 DCC 中的线性工作流配置

文章目录 也可以简单参考 SIGGRAPH 2010 的 PBS 的 Gamma-CorrectUnity 中的 gamma、linear 工作流Unity BRP 管线中的 shaderlab 提供的 gamma2linear, linear2gammaUnity中线性空间配置DCC 中的 gamma 值是怎么计算的&#xff1f;Photoshop 的线性工作流方式1方式2验证 Photo…

光栅投影中Gamma校正

学习 郑东亮 达飞鹏 《提高数字光栅投影测量系统精度的gamma校正技术》一文后&#xff0c;对其中的关键技术进行了解析。 摄像机获得的实际灰度图像&#xff1a; 其中是整个系统的gamma值&#xff0c;是预编码值&#xff08;需要根据实验进行选择&#xff09;&#xff0c;是幅…

java opencv gamma_Gamma校正及其OpenCV实现

一、什么是Gamma校正&#xff1f; Gamma校正是对输入图像灰度值进行的非线性操作&#xff0c;使输出图像灰度值与输入图像灰度值呈指数关系&#xff1a; [2] 这个指数即为Gamma. 经过Gamma校正后的输入和输出图像灰度值关系如图1所看到的&#xff1a;横坐标是输入灰度值&#x…

gamma校正 matlab,Gamma校正 ——图像灰度变化 OpenCV (十)

Gamma校正(C++、OpenCV实现) 1.作用: Gamma校正是对输入图像灰度值进行的非线性操作,使输出图像灰度值与输入图像灰度值呈指数关系: 伽玛校正由以下幂律表达式定义: 2.函数原型 void calcHist( const Mat* images, int nimages, const int* channels, InputArray mask, Out…

图像Gamma(伽玛)校正的原理及OpenCV代码实现

什么是Gamma校正&#xff1f; Gamma校正是对输入图像灰度值进行的非线性操作&#xff0c;使输出图像灰度值与输入图像灰度值呈指数关系。 Gamma校正的原理表达式如下&#xff1a; 上面中的指数γ即为Gamma。这就是Gamma校正的名称来历。 其中的取值范围是0~1&#xff0c;最重…

Gamma校正

下面是自己所整理的一些gamma校正的东西如有侵权请联系删除。 Gamma矫正&#xff1a; 伽马矫正也称幂律变换&#xff0c;一般用于平滑的扩展暗调的细节。 进行伽马矫正的原因是人类的眼睛在感知光线时&#xff0c;眼睛对亮度的感知遵循近似的幂函数&#xff08;如图&#xf…

图像Gamma校正

1. gamma校正的起因&#xff1a;人眼感知光线的特殊性 对于现实世界的光的强度来说&#xff0c;描述光的强弱&#xff0c;是根据光子在单位面积上的光子数量来描述的&#xff0c;这是物理规则&#xff0c;这是没错的&#xff0c;光的亮度&#xff08;强度&#xff09;是和光子数…

Gamma校正原理及实现

gamma校正原理&#xff1a;   假设图像中有一个像素&#xff0c;值是 200 &#xff0c;那么对这个像素进行校正必须执行如下步骤&#xff1a;   1. 归一化 &#xff1a;将像素值转换为 0 &#xff5e; 1 之间的实数。 算法如下 : ( i 0. 5)/256 这里包含 1 个除法和 1…

python手撕分水岭算法

python手撕分水岭算法 1 分水岭算法实现 主要思路就是&#xff1a; 利用一个优先队列与有序队列&#xff08;有序队列其实可以不用&#xff09;。优先队列是按像素的灰度值排列的&#xff0c;灰度值低的先被淹。通过统计像素的附近的点的标记种类个数来确认当前像素点的标记…

分水岭算法java,OpenCV 学习笔记 04 深度估计与分割——GrabCut算法与分水岭算法...

1 使用普通摄像头进行深度估计 1.1 深度估计原理 这里会用到几何学中的极几何(Epipolar Geometry)&#xff0c;它属于立体视觉(stereo vision)几何学&#xff0c;立体视觉是计算机视觉的一个分支&#xff0c;它从同一物体的两张不同图像提取三维信息。 极几何的工作原理&#x…

分水岭算法-python-opencv

分水岭算法简单原理&#xff1a; 对于一个图像的灰度值&#xff0c;将图像放平&#xff0c;可以看成是类似与山谷与山顶的图像&#xff0c;灰度值小的就是山底。先找到若干个山底&#xff0c;同时加水&#xff0c;当加到一定程度时候&#xff0c;某些山顶会被淹没&#xff0c;…

分水岭算法解析[halcon]

分水岭算法 分水岭算法是根据分水岭的构成来考虑图像的分割&#xff0c; 它是—种基于拓扑理论的数学形态学的分割方法。 首先&#xff0c; 把一幅图像看作起伏的地形&#xff0c; 图像的每像素灰度值作为这个地形的高度&#xff0c; 极小值是盆地&#xff0c; 极大值为山脊。…

opencv-分水岭算法图像分割

分水岭算法图像分割 目标   本节我们将要学习   • 使用分水岭算法基于掩模的图像分割   • 函数&#xff1a;cv2.watershed()    原理   任何一副灰度图像都可以被看成拓扑平面&#xff0c;灰度值高的区域可以被看成是山峰&#xff0c;灰度值低的区域可以被看成是山…

分水岭算法的python实现及解析

1 算法简介 分水岭算法的原理很容易查到&#xff0c;但是很多文章都是直接用的opencv或matlab函数&#xff0c;看不到具体实现方法&#xff0c;这篇文章希望能对大家有点帮助。 分水岭算法就是往山谷中注水&#xff0c;把不同湖水接触的位置称作分水岭&#xff0c;这么做的结…

opencv28:分水岭算法的图像分割

目标 在本章中&#xff0c;将学习 使用分水岭算法实现基于标记的图像分割函数&#xff1a;cv2.watershed() 理论 任何灰度图像都可以看作是一个地形表面&#xff0c;其中高强度的像素表示山峰&#xff0c;低强度表示山谷。可以用不同颜色的水(标签)填充每个孤立的山谷(局部…

OpenCV-分水岭算法

文章目录 分水岭算法cv2.watershed示例 分水岭算法 任何灰度图像都可以看作是一个地形表面&#xff0c;其中高强度表示山峰&#xff0c;低强度表示山谷。你开始用不同颜色的水(标签)填充每个孤立的山谷(局部最小值)。随着水位的上升&#xff0c;根据附近的山峰(坡度)&#xff…

【OpenCV】- 分水岭算法

文章目录 什么是图像分割分水岭算法1、实现分水岭算法&#xff1a;watershed()函数2、处理流程&#xff08;视频&#xff09;3、示例程序&#xff08;书中&#xff09; 什么是图像分割 将图像中像素根据一定的规则分为若干个cluster集合&#xff0c;每个集合包含一类对象 如下…

OpenCV分水岭算法详解

原理分析 分水岭算法主要用于图像分段&#xff0c;通常是把一副彩色图像灰度化&#xff0c;然后再求梯度图&#xff0c;最后在梯度图的基础上进行分水岭算法&#xff0c;求得分段图像的边缘线。 下面左边的灰度图&#xff0c;可以描述为右边的地形图&#xff0c;地形的高度是由…

分水岭算法 matlab实现

背景 做图像分割的时候用到了&#xff0c;就学习了一下 大概思想 把图像中的像素大小理解成山地的海拔&#xff0c;向山地灌水&#xff0c;海拔低的地方会积水&#xff0c;这些地方称之为谷底。随着水位上升&#xff0c;不同谷底的水会相遇&#xff0c;相遇的地方就是分水岭。…