【Modern OpenGL】颜色 Colors

2023-05-16

说明:跟着learnopengl的内容学习,不是纯翻译,只是自己整理记录。
强烈推荐原文,无论是内容还是排版。 原文链接
本文地址:http://blog.csdn.net/aganlengzi/article/details/50479585

颜色 Colors

我们在前面的教程中简单说过一些过于颜色设置的内容,还记得那个三角形吗?就是那个三个点是三原色,三角形的其它点是三个顶点按照位置插值结果的那个三角形。之后我们就再也没有提过关于颜色设置的事情了(实际上是因为我们有了纹理…)。本次教程中,我们将首先介绍颜色相关的内容,一方面是补充一下,另一方面是为了后面具体讲光照做准备。(还记得吗,我们已经完成了初识阶段,现在我们在光照阶段啦)。

在真实世界中,颜色是无穷的,而在数字世界中,颜色 好像都是需要数字来表示的,那么就不会是无穷的,数字总会有大小,而能表示的颜色种类也就取决于存储这些颜色值的物理空间。在数字世界中,为了表示真实世界中的场景(很大程度上决定于颜色),我们需要做一些折中。我们虽然不能表示尽真实世界中的所有颜色,但是我们可以表示足够多的颜色并且使用相近的颜色来表示真实世界中的颜色值而不被人眼所察觉。在计算机中,我们用颜色的三种分量(红,绿,蓝)RGB的组合来表示某种特定的颜色,实际上它们的组合可以表示足够我们使用的颜色。例如:我们用之前已经熟悉的方式定义一种颜色:

glm::vec3 coral(1.0f, 0.5f, 0.31f);   

根据物理学知识,我们看到的颜色实际上不是物体本身的颜色,而是物体反射的颜色。未被物体吸收的光才能够被我们看到,也就是我们平常说的物体的颜色。我们知道,太阳光被认为是白色的,起始它是所有光的组合,其中包括红橙黄绿蓝靛紫各种分量。当白色的太阳光照射在一个蓝色的玩具上的时候,这个蓝色的玩具吸收除自己本身颜色之外的所有其他分量。而和它本身一致的颜色被反射出去也就是我们人眼能够看到的光。所以我们认为这只玩具是蓝色的。下面这张图显示的是人眼观察太阳光照射在珊瑚色的物体上的情形。

可以看到,太阳光中的其它颜色被这个珊瑚色的物体吸收,而一些组成自身颜色分量的颜色并没有被吸收而是被反射到了人眼中。所以我们认为它是珊瑚色的。

上面说的原理在图形中同样适用。当我们在OpenGL中定义一个光源,我们需要为这个光源指定一个光源颜色。在上面的举例中,我们的光源是太阳,它的光源颜色是白色。而物体的最终颜色是由它反射光源的颜色决定的(与自身相同的颜色被吸收)。让我们利用向量相乘的方式模拟上面的过程:我们先定义一个光源颜色lightColor,这里定义为白色光,然后定义物体颜色toyColor,定义为珊瑚色,然后将这两个向量按照组件相乘的方式看一下得到的反射结果:

glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);

我们可以看到,最终的反射结果是这个物体的颜色本身!而白色光中的很大部分都被吸收掉了。这和现实世界中是一致的。这样,我们就可以定义对象的颜色为它反射RGB不同颜色分量值了,也就是利用如上所示的方式和值来表示。现在,让我们把白色光源换成是绿色光源,看一下效果是怎样的呢:

glm::vec3 lightColor(0.0f, 1.0f, 0.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f);

正如我们猜想的,经过物体的吸收之后,反射的颜色中只有绿色光分量。而且颜色值和物体本社的颜色值相当。那么这个物体的颜色值就只是暗绿色了。让我们再试一个橄榄绿色的光源看看是什么效果:

glm::vec3 lightColor(0.33f, 0.42f, 0.18f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);

原理和上面的举例一致,我们得到了不同的物体颜色,原来,只要改变光源的颜色,我们就可以改变我们看到的物体的颜色。看来创建颜色也不是一件难事。

对于颜色值,以上的只是基本上就已经够了,现在让我们先放一放,我们来创建一个能够进行实验的场景吧!在其中,我们可以来感受颜色值和光照的其妙。

一个光照场景 A lighting scene

在后面的教程中,我们将会模仿真实世界中关于光照和颜色值的情形,创建有趣的颜色值来充分使用颜色。因为我们将要使用到光源,所以我们想把光源弄成一个真实可见的对象,而不是一个抽象的概念。这样可以更加真实模拟出真实世界中的情形。

我们首先要做的是找一个我们把各种光投射到它身上的物体——一个深度光污染受害者,找什么呢?就用前面教程中使用的那个立方体吧!其次,我们还需要一个物体来扮演光源(比如一个立方体灯泡?^_^),我们还是使用前面教程中用到的立方体吧!

那么,开始干吧!我们需要用我们的坐标数据填充顶点缓冲对象(VBO),设置顶点属性指针等等,这些步骤对你来说应该十分熟悉和简单了。如果在创建的时候不知道怎么做,就先看看前面的教程吧。

我们真正需要做的是首先需要一个顶点处理程序来绘制那个立方体,这和前面的教程是一致的,因为前面我们已经绘制过类似的立方体:

#version 330 core
layout (location = 0) in vec3 position;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);
} 

需要注意的是,之前我们绘制立方体箱子的时候使用了纹理,而在这儿是不需要纹理的,所以我们可以选择在坐标中删除那些纹理坐标并且修改相应的指针,否则OpenGL解读数据就会出现问题。或者也可以保留但是不使用它们。

我们还需要绘制一个立方体灯泡,所以我们会它新创建一个VAO来存储相关的属性和设置,这样做的好处是,光源和被光照的物体是分离的:

GLuint lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// We only need to bind to the VBO, the container's VBO's data already contains the correct data.
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// Set the vertex attributes (only position data for our lamp)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0); 

上面的代码是比较简单直接的。现在我们相当于创建了两个立方体,一个是光源,一个是被光源照射的物体。接下来让我们来创建相应的片段处理程序:

#version 330 core
out vec4 color;

uniform vec3 objectColor;
uniform vec3 lightColor;

void main()
{
    color = vec4(lightColor * objectColor, 1.0f);
}

如代码所示,片段处理程序接收箱子对象和立方体光源的颜色值作为参数,这两个参数是uniform类型的变量。在其中,像是上面举例的那样按照向量相乘的方式模拟物体的官员之间的作用效果。这应该是比较好理解的,接下来让我们来设置物体的颜色并且传递参数:

// Don't forget to 'use' the corresponding shader program first (to set the uniform)
GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");
GLint lightColorLoc  = glGetUniformLocation(lightingShader.Program, "lightColor");
glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(lightColorLoc,  1.0f, 1.0f, 1.0f); // Also set light's color (white)

有一件事情需要注意的是:如果只使用上面的方式,当我们进行改变顶点和片段处理程序的时候,立方体“灯泡”也会像箱子一样改变(因为它们使用的是相同的顶点和片段处理程序),这显然并不是我们想要的。我们想要的结果是:灯泡是恒定的颜色值(比如设置为白色),而且不会受其他的颜色值的影响,这样显得更加真实。

为了达到我们期望的效果,我们需要做的是创建一个新的片段处理程序来绘制这个光源立方体。片段处理程序是一致的,因为都是从点来绘制对象结构的,只需要改变片段处理器就可以了,因为片段处理器决定一个对象的表面颜色。在下面的绘制光源的片段处理程序中,我们将光源的颜色设置为白色。

#version 330 core
out vec4 color;

void main()
{
    color = vec4(1.0f); // Set alle 4 vector values to 1.0f
}

有了上面的shaders,当我们绘制物体的时候,比如说绘制箱子或者其它物体,我们使用前面定义的shader或者根据要绘制的物体新创建的shader;而当我们绘制光源的时候,我们就利用我们刚刚创建的这个片段处理程序就可以了。这样的话可以将物体的绘制和光源的绘制分开来,方便各自的修改也相互不影响。

其实,绘制这个立方体光源的主要作用是让我们实际看到光源的位置,知道光是从哪里发出来的。在实际应用中,我们只是将光源的位置定义在场景中的某个位置处,这个位置实际上并不需要可以看见。在这个例子中,我们在只是在定义的光源位置处绘制了一个光源来让我们有直观的认识。

Ok,我们来利用一个vec3类型的全局向量来表示世界坐标系中光源的位置:

glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

为了在这个位置上绘制立方体光源,我们需要在绘制之前将这个小立方体转换到这个位置,用我们之前学到的转换方法就可以了。当然,还有就是光源大小的事情(在前面我还一直担心绘制出来一个巨大无比的立方体光源呢^_^):

model = glm::mat4();
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f)); 

最终,和绘制光源相关的代码应该是如下所示的样子:

lampShader.Use();
// Set the model, view and projection matrix uniforms
...
// Draw the lamp object
glBindVertexArray(lightVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);          
glBindVertexArray(0);

让我们把上面的步骤合起来,运行一下看看效果吧,我的效果是这样的:

这是代码:main.cpp

看上去并没有想象得那么真实啊,因为我们后面还要学到好多关于光源和颜色的知识,这个教程算是颜色和光照学习的环境搭建和知识准备吧。后面才是真正能够体验到光照的其妙之处的地方~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Modern OpenGL】颜色 Colors 的相关文章

  • 悬停时显示表格行大纲

    我试图在悬停时突出显示表格行的边框 不幸的是 这仅适用于第一行单元格 较低的行有一个不改变颜色的边框 我尝试过使用outline但它不太适合 hover在网络套件中 http jsfiddle net S9pkM 2 http jsfidd
  • gluPerspective 与 gluOrtho2D

    我查看了 MSDN 上关于这两个函数的文档 但是 我不太明白这两个功能之间的区别 一个是用于设置 3D 相机视图 另一个是用于设置 2D 相机视图 如果能得到解答就太好了 预先感谢您的评论 正交投影基本上是没有透视的 3D 投影 本质上 这
  • 如何使用 OpenCV 找到红色区域? [复制]

    这个问题在这里已经有答案了 我正在尝试编写一个检测红色的程序 然而有时它比平常更暗 所以我不能只使用一个值 检测不同深浅的红色的最佳范围是多少 我目前使用的范围是 128 0 0 255 60 60 但有时它甚至检测不到我放在它前面的红色物
  • 使用 glDrawElements 时在 OpenGL 核心配置文件中选取三角形

    我正在使用 glDrawElements 绘制三角形网格 并且希望能够使用鼠标单击来拾取 选择三角形 三角形的网格可以很大 在固定功能 OpenGL 中 可以使用 GL SELECT http content gpwiki org inde
  • 即使在顶点着色器中使用,glGetUniformLocation()也会返回-1

    我正在尝试用法线渲染一个简单的立方体 我使用以下代码来初始化着色器 void initShader const char vertexShaderPath const char fragmentShaderPath cout lt lt I
  • 为什么我的 FPS 相机一劳永逸地滚动?

    如果我忽略四元数代数的肮脏细节 我想我理解了旋转和平移变换背后的数学 但仍然不明白我做错了什么 为什么我的相机一劳永逸地滚动 更具体地说 我应该如何从相机的方向 旋转矩阵 计算相机视图矩阵 我正在用 Python 编写一个简约的 3d 引擎
  • 使用 BufferedImages 获取图像每个像素的颜色

    我试图获取图像的每个像素的每种颜色 我的想法如下 int pixels BufferedImage image image ImageIO read this getClass getResources image png int pixe
  • ggplot2 使用 geom_line 手动指定颜色

    我正在尝试绘制下面的图表 并想手动指定颜色 我需要按基因型绘制 因为有多个基因型属于同一个 Bgrnd All 并且我希望它们在绘制的线条中单独出现 但是 我想按 Bgrnd All 对线条进行着色 特别是按照我在 scale fill m
  • 如何将点光源转换为卵形/椭圆形?

    我希望通过具有不同 x 和 y 值的 vec2 半径将当前的圆形光变成椭圆形 有没有办法根据我当前在片段着色器中的代码来做到这一点 uniform struct Light vec4 colour vec3 position vec2 ra
  • 如何在Java中从一组选定的颜色中输出随机颜色? (安卓)

    因此 我希望每当用户输入答案时都为字符串赋予随机颜色 我的问题是 我不确定如何使字符串的随机颜色成为特定范围的颜色 例如 如果我希望字符串随机变成蓝色 红色 绿色 粉色 白色或棕色 只有这些颜色 没有其他颜色 到目前为止 我已经使用以下代码
  • 将像素传递给 glTexImage2D() 后会发生什么?

    例如 如果我创建一个像素数组 如下所示 int getPixels int pixels new int 10 pixels 0 1 pixels 1 0 pixels 1 1 etc glTexImage2D getPixels glTe
  • CSS 文本装饰:反向

    我很惊讶 CSS 中没有 text decoration reverse 因为使用 JavaScript 来实现似乎非常尴尬 IE 将元素的前景色和背景色分别设置为父元素的背景色和前景色 我注意到了 JavaScript 技术here ht
  • 覆盖按钮的文本颜色不起作用

    我使用每个按钮上的buttonBarStyle 和布局上的buttonBarButtonStyle 为按钮栏创建了一个自定义主题 它工作正常 但我想更改按钮的文本颜色 但它仍然采用默认颜色 android color primary tex
  • glBlitFramebuffer 渲染缓冲区和渲染全屏纹理哪个更快?

    哪个更快更高效 使用 OpenGL 纹理作为 CUDA 表面并在四边形上渲染 新样式 使用渲染缓冲区作为 CUDA 表面并使用 glBlitFramebuffer 进行渲染 None
  • 如何计算图像中的 RGB 或 HSV 通道组合?

    我使用 python opencv 加载形状为 30 100 3 的图像 现在想要按颜色计算所有颜色的频率 我不是指单个通道 而是指通道组合 含义 3 个频道列表 例如 255 0 0 表示红色 255 255 0 表示黄色 100 100
  • 每个刻度标签都有不同的颜色

    我正在尝试使用 matplotlib python 3 5 创建一个散点图 其中 x 轴上的每个刻度都有不同的颜色 这怎么可能 例如 假设 x 刻度为 Mo Tu We Th Fr Sa Su 现在我希望 Mo 是绿色的 Tu 是蓝色的 等
  • 0 因为饱和度和亮度不起作用,但 0% 在 hsl/hsla 中起作用?

    我正在尝试一个简单的演示 其中我为元素赋予了颜色hsl 根据我的经验 我知道0CSS 中的 ZERO 是无单位的 如果要指定 0 作为值 可以保留单位 然而 情况似乎并非如此hsl hsla 在 Chrome 和 Firefox 上 结果都
  • 将四元数旋转转换为旋转矩阵?

    基本上 给定一个四元数 qx qy qz qw 我如何将其转换为OpenGL旋转矩阵 我也对哪个矩阵行是 向上 向右 向前 等感兴趣 我有一个四元数的相机旋转 我需要在向量中 以下代码基于四元数 qw qx qy qz 其中顺序基于 Boo
  • OpenGL - 两个纹理的幂

    OpenGL 使用二次幂纹理 这是因为由于 MipMapping 某些 GPU 只接受 2 的幂纹理 当绘制比实际更大的纹理时 使用这些二次方纹理会导致问题 我想到了一种方法来解决这个问题 即仅在我们使纹理小于实际大小时使用 PO2 比率
  • 在两种颜色之间进行插值的最有效方法是什么? (预计有伪代码和按位运算)

    制作一个黑莓应用程序 想要一个渐变类 插入两种颜色的最有效方法 例如速度和电池寿命 是什么 请具体说明 Java of course int c1 0xFFAA0055 color 1 ARGB int c2 0xFF00CCFF colo

随机推荐

  • Maven的pom.xml介绍

    6 1 简介 pom xml文件是 Maven进行工作的主要配置文件 在这个文件中我们可以配置 Maven项目的 groupId artifactId和 version等 Maven项目必须的元素 xff1b 可以配置 Maven项目需要使
  • 使用简单的wcf文件实现上传,下载文件到服务器

    wcf是微软开发出的用户数据通信的app接口 xff0c 在 net framework3 0中与wpf xff0c wf一同集成 xff0c 是 net框架的一部分 具体请参见点击打开链接 本文主要讲述了使用wcf服务契约来进行文件或者数
  • mysql添加用户和权限

    用户管理 mysql gt use mysql 查看 mysql gt select host user password from user 创建 mysql gt create user 用户名 IDENTIFIED by 39 用户密
  • MacBook Pro(M1)安装mysql

    1 下载 网址 xff1a mysql com 2 选择社区版本 3 MySQL Community Server 版本 兼容性说明 xff1a 适配 macOS 10 15 版本 xff0c 但上面有说明 xff0c 可运用于 Big S
  • MySQL进阶-监控、高可用

    MySQL监控 1 常见的监控方式 一般来说 xff0c 常见的监控方式主要有如下三种 xff1a 监控方式特点优点缺点工具 脚本自己编写工具或脚本 xff0c 适合初期机器很少的生产环境在企业初期可以快速满足监控需求后期部署和维护成本大商
  • Druid+Commons DBUtils基本使用

    Druid 1 jar包下载 xff1a https github com alibaba druid releases 2 导入jar包 2 1创建lib文件夹 xff0c 复制粘贴进去 2 2 2 3 3 创建配置文件 文件名称 xff
  • MAC安装maven及每次启动需要刷新bash_profile问题

    1 下载 网址 xff1a https maven apache org download cgi 2 解压安装 选择一个目录 示例 xff1a Users i18 apache maven 3 8 1 3 配置变量 vim span cl
  • win10解压安装mysql方法及遇见的问题(缺少MSVCR120.dll文件、服务无法启动)

    WIN10系统MYSQL的下载与安装详细教程 第一步 xff1a 下载 MySQL 下载地址 xff1a https dev mysql com downloads mysql 5 1 html downloads 具体过程如下 xff1a
  • 基于select函数实现的tcp简单服务器

    select 实现 tcp demo 回忆TCP的连接过程selectselect 的封装tcp类的封装程序流程 程序cli cpp 客户端建立连接SelectSvr hpp 服务器的头文件Tcpsvr hppmain cpp 主函数mak
  • 【odroid-xu3】 ODROID-XU3软件环境搭建记录

    原文链接 xff1a http blog csdn net aganlengzi article details 50036951 1 操作系统环境准备 我用的是Ubuntu12 04 xff0c 但是建议用更高的版本 按照android官
  • D435i相机首次开发与踩坑记录

    D435i相机首次开发与踩坑记录 介绍 配置完D435i相机的开发环境后开始尝试研究官方例程 xff0c 试着运行一些demo初入intel相机 开始的时候也是很头疼 xff0c 不知道如何下手 xff0c 看了众多博客后稍微有了一些眉目
  • 单片机串口收发字符数据的类型

    今天在用51单片机进行串口收发数据的时候遇到了这样一个问题 xff0c 上位机给单片机的字符数据是什么类型的 xff0c 单片机又是怎样存储的 xff1f 串口中断如下 UART中断服务函数 void InterruptUART inter
  • cmake之链接外部动态库

    cmake不再使你在构建项目时郁闷地想自杀了 xff0d xff0d 佚名KDE开发者 xff11 xff0e 写在开头 有两种方式 xff0c 一种是cmake自己内置的find package 另一种是使用pkg config 2 fi
  • 一帧CAN数据需要多长时间发送

    1 CAN通讯速率 默认 500kbit s xff1b 2 xff1a 从下图CAN数据包的完整结构可知 xff0c 一包完整的扩展帧CAN数据总共包含 128bit xff1b 3 xff1a 发送一帧扩展帧CAN数据耗时 128 50
  • 在单片机中什么是堆栈?它的作用是什么?

    在片内RAM中 xff0c 常常要指定一个专门的区域来存放某些特别的数据 它遵循顺序存取和后进先出 LIFO FILO 的原则 这个RAM区叫堆栈 子程序调用和中断服务时CPU自动将当前PC值压栈保存 xff0c 返回时自动将PC值弹栈 保
  • 初识Java内部类

    初识Java内部类原创 xff1a morgan83 提起Java内部类 xff08 Inner Class xff09 可能很多人不太熟悉 xff0c 实际上类似的概念在C 43 43 里也有 xff0c 那就是嵌套类 xff08 Nes
  • 微型四轴设计之通过arduino读取MPU6050原始数据

    概述 打算自己选型配件 画PCB以及焊元件 xff0c 制作一个微型四轴飞行器 主控板打算使用stm32 xff0c 此处使用arduino来读取mpu6050只是为了便于开发和调试 xff08 arduino的串口监视器用起来很方便 xf
  • PS304远距离串口服务器模块应用于电子设备开发芯片测试工业数字接口转换数字接口学习验证

    PS304 Ports Server channel 4 是多种数字接口物理层协议转发器 xff0c 可实现 UART 转换 I2C SPI 1Wire 远距离通讯 xff0c 内嵌磁隔离双电源及辅助增强电源电路 自适应线缆算法 强大灵活的
  • 使用 eBPF 技术跟踪 Netfilter 数据流

    1 网络层数据流向与 Netfilter 体系 图 1 1 为网络层内核收发核心流程图 xff0c 在函数流程图中我们可以看到 Netfliter 在其中的位置 xff08 图中深色底纹圆角矩形 xff09 图中对应的 hook 点有 5
  • 【Modern OpenGL】颜色 Colors

    说明 xff1a 跟着learnopengl的内容学习 xff0c 不是纯翻译 xff0c 只是自己整理记录 强烈推荐原文 xff0c 无论是内容还是排版 原文链接 本文地址 xff1a http blog csdn net aganlen