【OpenGL学习】颜色和光照

news2024/11/16 11:33:59

颜色和光照

一、颜色的物理解释

颜色(英语:colour,color)又称色彩色泽,是眼、脑和我们的生活经验对光的颜色类别描述的视觉感知特。这种对颜色的感知来自可见光谱中的电磁辐射对人眼视锥细胞的刺激。颜色是由光反射所产生的,这种反射是由物体的物理性质决定的,如光的吸收、发射光谱等。

二、计算机中的颜色

在计算机领域中,我们需要使用(有限的)数值来模拟真实世界中(无限)的颜色,所以并不是所有现实世界中的颜色都可以用数值来表示的。颜色可以数字化的由红色(Red)、绿色(Green)和蓝色(Blue)三个分量组成,它们通常被缩写为RGB。仅仅用这三个值就可以组合出任意一种颜色。红绿蓝三种颜色被称之为三原色。下图是表示RGB色彩的立方体:

请添加图片描述

三、人眼观察颜色的原理

3.1 人眼的生理构造

人眼中的视锥细胞和视杆细胞都能感受颜色,一般人眼中有三种不同的视锥细胞:第一种主要感受黄绿色,它的最敏感点在565纳米左右;第二种主要感受绿色,它的最敏感点在535纳米左右;第三种主要感受堇紫色,其最敏感点在420纳米左右。视杆细胞只有一种,它的最敏感的颜色波长在蓝色和绿色之间。

每种视锥细胞的敏感曲线大致是钟形的,视锥细胞依照感应波长不同由长到短分为L、M、S三种。因此进入眼睛的光一般相应这三种视锥细胞和视杆细胞被分为4个不同强度的信号。
请添加图片描述

同一种颜色在不同的亮度中会产生不同的颜色感。这个现象的原因是我们的眼睛中除了有锥状细胞外还有可以感光的杆状细胞。杆状细胞虽然一般被认为只能分辨黑白,但它们对不同的颜色的灵敏度是略微不同的,因此当光暗下来的时候,杆状细胞的感光特性就越来越重要了,它可以改变我们对颜色的感觉。

3.2 物体的反射

我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的(Reflected)颜色。换句话说,那些不能被物体所吸收(Absorb)的颜色(被拒绝的颜色)就是我们能够感知到的物体的颜色。例如,太阳光能被看见的白光其实是由许多不同的颜色组合而成的(如下图所示)。如果我们将白光照在一个蓝色的玩具上,这个蓝色的玩具会吸收白光中除了蓝色以外的所有子颜色,不被吸收的蓝色光被反射到我们的眼中,让这个玩具看起来是蓝色的。下图显示的是一个珊瑚红的玩具,它以不同强度反射了多个颜色。

img

四、图形学中颜色的计算

在图形学中,只需要把光源的颜色与物体的颜色值相乘,所得到的就是这个物体所反射的颜色(也就是我们所感知到的颜色)。可以定义物体的颜色为物体从一个光源反射各个颜色分量的大小,比如用一个白色的光源来照一个珊瑚红的物体:

glm::vec3 light_color(1.0f, 1.0f, 1.0f);
glm::vec3 coral_color(1.0f, 0.5f, 0.31f);
glm::vec3 result = light_color * coral_color; // = (1.0f, 0.5f, 0.31f);

物体的颜色表示了该物体对红色光的反射率为1,也就是全部反射掉,对绿色光的反射率为0.5,也就是有一半的绿光被反射,蓝色光的反射率为0.31,有0.31的蓝色光被反射,最终反射的光线就有 1的红光,0.5的绿光和0.31的蓝光,正好就是反射光线对应的珊瑚红色。

五、创建光照场景

我们使用之前创建的立方体箱子作为光源的投射对象,并在3D场景中添加一个光源,简单起见仍然用一个立方体来表示。

首先创建一个顶点数组和顶点缓冲区对象,并用之前的箱子数据填充(这里我使用了抽象的vertex array 和 buffer 类,详见Kaoru-misono/OpenGL):

std::shared_ptr<Vertex_Array> box_vertex_array = std::make_shared<Vertex_Array>();
	std::shared_ptr<Vertex_Buffer> box_vertex_buffer = std::make_shared<Vertex_Buffer>(box_vertices, sizeof(box_vertices));
	box_vertex_buffer->set_layout(layout2);

	box_vertex_array->add_vertex_buffer(box_vertex_buffer);

创建一个简单的顶点着色器用于给箱子的顶点变换:

#version 330 core

layout(location = 0) in vec3 a_position;
layout(location = 1) in vec2 a_texcoord;

uniform mat4 u_mvp;

void main()
{
	gl_Position = u_mvp * vec4(a_position, 1.0);
}

然后我们给光源对应的箱子创建一个新的顶点数组,但是顶点缓冲区仍然用刚才创建好的箱子的顶点数据,因为两个箱子的顶点数据是相同的,没有必要再创建一个顶点缓冲区。

std::shared_ptr<Vertex_Array> light_VAO = std::make_shared<Vertex_Array>();
	light_VAO->add_vertex_buffer(box_VBO);

最后我们需要计算物体的颜色,因此创建一个片元着色器:

#version 330 core

out vec4 frag_color;

uniform vec3 light_color;
uniform vec3 object_color;

void mian()
{
	frag_color = light_color * object_color;

}

创建好shader之后,在循环中对两个颜色对应的全局变量进行设置:

		box_shader.bind();
		box_shader.set_float3("light_color", glm::vec3(1.0f, 1.0f, 1.0f));
		box_shader.set_float3("object_color", glm::vec3(1.0f, 0.5f, 0.31f));

为了能够让光源的颜色不受干扰,我们单独创建一个顶点和片元着色器来绘制光源对应的箱子:

#version 330 core

layout(location = 0) in vec3 a_position;
layout(location = 1) in vec2 a_texcoord;

uniform mat4 u_mvp;

void main()
{
	gl_Position = u_mvp * vec4(a_position, 1.0);
}

#version 330 core

out vec4 frag_color;

void mian()
{
	frag_color = vec4(1.0);

}

使用这个灯立方体的主要目的是为了让我们知道光源在场景中的具体位置。通常在场景中定义的一个光源的位置只是一个空间坐标,它并没有视觉意义。为了让我们能够直观观察到光源,将表示光源的立方体绘制在与光源相同的位置。本节中我们使用一个单独的片元着色器绘制光源,让它一直处于白色的状态,不受场景中的光照影响。

首先声明光源在场景中的位置:

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

把我们用作光源的箱子平移到该位置,并且将其的大小缩小一点:

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

在 Render Loop 中,我们绘制两个立方体,注意要使用不同的顶点数组来绘制:

		box_VAO->bind();
		model = glm::mat4(1.0f);
		mvp = camera.get_view_projection_matrix() * model;
		box_shader.bind();
		box_shader.set_float4("light_color", glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
		box_shader.set_float4("object_color", glm::vec4(1.0f, 0.5f, 0.31f, 1.0f));
		box_shader.set_mat4("u_mvp", mvp);
		
		glDrawArrays(GL_TRIANGLES, 0, 36);

		light_VAO->bind();
		model = glm::mat4(1.0f);
		model = glm::translate(model, light_pos);
		model = glm::scale(model, glm::vec3(0.2f));
		mvp = camera.get_view_projection_matrix() * model;
		light_shader.bind();
		light_shader.set_mat4("u_mvp", mvp);

		glDrawArrays(GL_TRIANGLES, 0, 36);

为了能够观察到整个场景,我们对相机的初始位置和朝向进行了重新设置:

Perspective_Camera camera(glm::vec3(0.0f, 2.0f, 5.0f), glm::vec3(-20.0f, -80.0, 0.0f));

这里使用了位置和欧拉角来初始化相机,运行代码得到的结果应该是这样的:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184739.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

boost搜索引擎

文章目录1.项目介绍2.搜索引擎技术栈和项目环境3.正排索引和倒排索引 - 搜索引擎具体原理4.编写数据去标签和数据清洗模块Parser5.编写建立索引模块Index6.编写搜索引擎模块Searcher7.编写http_server8.效果展示1.项目介绍 Boost官网没有对应的搜索引擎&#xff0c;不方便我们…

04 frameset-iframe【尚硅谷JavaWeb教程】

04 frameset-iframe【尚硅谷JavaWeb教程】 frameset、iframe这个标签基本上已经不用了。 frameset标签 一个大的网页由很多个小的网页组成&#xff0c;会用到frameset。 frameset 表示页面框架&#xff0c;这个标签已经淘汰&#xff0c;了解&#xff0c;不需要掌握。 frame表…

Element ui Avatar头像管理组件 实现当用户没有头像时 以名称最后一个字为头像

el-avatar是一个比较方便的头像管理组件 src控制他的图片展示 <el-avatarclass "avatar":src"item.images" ></el-avatar>样式的话 可以用avatar控制 <style> .avatar{width: 18px;height: 18px;line-height: 18px; } .avatar img{b…

【可解释性机器学习】TextExplainer: 调试黑盒文本分类器

TextExplainer: 调试黑盒文本分类器示例问题&#xff1a;20个新闻组数据集的LSA SVM模型TextExplainer文本解释器的工作原理我们应该相信这个解释吗&#xff1f;让它们犯错吧让它们再次犯错吧自定义TextExplainer: 采样过程自定义TextExplainer&#xff1a;分类器参考资料尽管…

记录每日LeetCode 237.删除链表中的节点 Java实现

题目描述&#xff1a; 有一个单链表的 head&#xff0c;我们想删除它其中的一个节点 node。 给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。 链表的所有值都是 唯一的&#xff0c;并且保证给定的节点 node 不是链表中的最后一个节点。 删除给定的节点。注…

Kotlin之使用DSL构建专有的语法结构

DSL的全称是领域特定语言(Domain Specific Language)&#xff0c;它是编程语言赋予开发者的一种特殊能力&#xff0c;通过它我们可以编写出一些看似脱离其原始语法结构的代码&#xff0c;从而构建出一种专有的特殊结构。 Kotlin也是支持DSL的&#xff0c;并且在Kotlin中实现DSL…

CF——1766C - Hamiltonian Wall

题目链接 1766C - Hamiltonian Wall Rating&#xff1a;1300 题目描述 Sir Monocarp Hamilton is planning to paint his wall. The wall can be represented as a grid, consisting of 2 rows and m columns. Initially, the wall is completely white. Monocarp wants to p…

Leetcode力扣秋招刷题路-0101

从0开始的秋招刷题路&#xff0c;记录下所刷每道题的题解&#xff0c;帮助自己回顾总结 101. 对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1…

[LeetCode周赛复盘] 第 330 场周赛20230129

[LeetCode周赛复盘] 第 330 场周赛20230129 一、本周周赛总结二、 [Easy] 6337. 统计桌面上的不同数字1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6338. 猴子碰撞的方法数1. 题目描述2. 思路分析3. 代码实现四、[Hard] 6339. 将珠子放入背包中1. 题目描述2. 思路分析3. 代…

过年了,给网站加个灯笼+飘雪效果!

过年了&#xff0c;下面分享一个网站的特效&#xff0c;给网站添加一个新春灯笼和飘雪的效果&#xff0c;过年期间多一点年味。灯笼特效下面是css样式&#xff0c;可以放在公共样式中&#xff1a;.deng-box{position:fixed;top:-40px;right:150px;z-index:9999;pointer-events:…

【音视频工具】前端屏幕录制工具 + 录制<video>标签内容

一、录制的实现思路 1.开始录制、停止录制、下载视频 2.Blob介绍 3.概念 var mediaRecord //用于录制视频 var mediaStream //视频流 var videoBuffer [] //保存的视频数据二、屏幕录制工具 下载地址&#xff1a; https://chrome.google.com/webstore/detail/tampermonkey…

k8s之POD资源限制和健康监测

写在前面 本文一起看下POD的资源限制配置和健康监测的相关内容。 1&#xff1a;资源限制 如果是不对POD设置资源限制的话&#xff0c;若任由其占用系统资源&#xff0c;可能会造成非常严重的后果&#xff0c;所以我们需要根据具体情况来设置资源限制&#xff0c;如使用多少内…

怎样把截图转换成文字?三分钟教会你如何截图转文字

在日常的学习中&#xff0c;当你在网上看到一篇文章&#xff0c;而当中的某一段话很适合拿来用在自己的写作上&#xff0c;但是你却无法直接将其复制粘贴下来&#xff0c;只能先截图下来再手动输入&#xff0c;这种方法虽然可行&#xff0c;但比较消耗时间和精力&#xff0c;那…

详细图解LeetCode经典链表算法题

文章目录链表类型算法题一、链表介绍本文使用的Java中链表类&#xff1a;二、链表基础题1、数组转链表代码&#xff1a;测试&#xff1a;2、单链表翻转题目&#xff1a;代码&#xff1a;解析&#xff1a;测试&#xff1a;补充&#xff1a;3、合并两个有序链表题目&#xff1a;解…

顺应信创发展,君子签电子签章方案全面适配信创环境

信创产业作为战略性新兴产业&#xff0c;近年来&#xff0c;国家不断出台相关政策对行业的发展进行支持。 2018年我国首次将信创纳入国家战略&#xff0c;并提出了 “28N”应用体系&#xff0c;信创发展步入“快车道”&#xff1b;2020年起&#xff0c;信创产业由党政逐渐向其…

垃圾收集器必问系列—ZGC

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 人的一切痛苦&#xff0c;本质上都是对自己的无能的愤怒。——王小波 文章目录Region布局读屏障染色指针染色指针的优势运作过程ZGC的优缺点ZGC有人称它为Zero GC&#xff0c;其实“Z”并非什么专业名词的缩写…

vue前端框架课程笔记(二)

目录计算属性问题引入插值语法实现methods配置属性实现computed配置属性一些问题getter与setter注意监视属性问题引入computed和methods实现watch属性watch属性简介computed与watch的比较注意本博客参考尚硅谷官方课程&#xff0c;详细请参考 【尚硅谷bilibili官方】 本博客以…

千峰网络安全笔记(前三讲)

典中典 《c语言从研发到脱发》 《C从入门到放弃》 《Java从跨平台到跨行业》 《Ios开发从入门到下架》 《Android开发大全——从开始到转行》 《PHP由初学至搬砖》 《黑客攻防:从入门到入狱》 《Mysql从删库到跑路》 《服务器运维管理从网络异常到硬盘全红》 《服务器运维管理…

CentOS 8 中dnf管理器如何仅下载不安装软件

在某些情况下&#xff0c;我们希望从命令行下载特定或一组 RPM 包而不安装它。虽然我们可以使用 wget 命令下载&#xff0c;但 wget 不会下载安装包的依赖项。在 CentOS 8 中DNF&#xff08;或 yum&#xff09;是一个命令行包管理工具。使用 DNF我们可以安装、更新和删除 rpm 包…

HTTP协议学习

一、http请求协议1、常见请求头accept:浏览器通过这个头告诉服务器&#xff0c;它所支持的数据类型Accept-Charset: 浏览器通过这个头告诉服务器&#xff0c;它支持哪种字符集Accept-Encoding&#xff1a;浏览器通过这个头告诉服务器&#xff0c;支持的压缩格式Accept-Language…