Learn OpenGL 08 颜色+基础光照+材质+光照贴图

我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的(Reflected)颜色。物体的颜色为物体从一个光源反射各个颜色分量的大小

创建光照场景

首先需要创建一个光源,因为我们以及有一个立方体数据,我们只需要进行模型变换就可以将它变成我们的光源

unsigned int lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// 只需要绑定VBO不用再次设置VBO的数据,因为箱子的VBO数据中已经包含了正确的立方体顶点数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 设置灯立方体的顶点属性(对我们的灯来说仅仅只有位置数据)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

要注意的是,当我们修改顶点或者片段着色器后,灯的位置或颜色也会随之改变,这并不是我们想要的效果。我们不希望灯的颜色在接下来的教程中因光照计算的结果而受到影响,而是希望它能够与其它的计算分离。我们希望灯一直保持明亮,不受其它颜色变化的影响(这样它才更像是一个真实的光源)。

为了实现这个目标,我们需要为灯的绘制创建另外的一套着色器,从而能保证它能够在其它光照着色器发生改变的时候不受影响。顶点着色器与我们当前的顶点着色器是一样的,所以你可以直接把现在的顶点着色器用在灯上。灯的片段着色器给灯定义了一个不变的常量白色,保证了灯的颜色一直是亮的:

然后再渲染完物体以后把光源渲染出来

       lightShader.use();lightShader.setMat4("projection", projection);lightShader.setMat4("view", view);model = glm::mat4(1.0f);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.2f)); // a smaller cubelightShader.setMat4("model", model);glBindVertexArray(lightVAO);glDrawArrays(GL_TRIANGLES, 0, 36);

基础光照模型

  • 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
  • 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
  • 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色

法线变换矩阵

法向量只是一个方向向量,不能表达空间中的特定位置。同时,法向量没有齐次坐标(顶点位置中的w分量)。这意味着,位移不应该影响到法向量。因此,如果我们打算把法向量乘以一个模型矩阵,我们就要从矩阵中移除位移部分,只选用模型矩阵左上角3×3的矩阵(注意,我们也可以把法向量的w分量设置为0,再乘以4×4矩阵;这同样可以移除位移)。对于法向量,我们只希望对它实施缩放和旋转变换。

其次,如果模型矩阵执行了不等比缩放,顶点的改变会导致法向量不再垂直于表面了。因此,我们不能用这样的模型矩阵来变换法向量。下面的图展示了应用了不等比缩放的模型矩阵对法向量的影响:

每当我们应用一个不等比缩放时(注意:等比缩放不会破坏法线,因为法线的方向没被改变,仅仅改变了法线的长度,而这很容易通过标准化来修复),法向量就不会再垂直于对应的表面了,这样光照就会被破坏。

修复这个行为的诀窍是使用一个为法向量专门定制的模型矩阵。这个矩阵称之为法线矩阵(Normal Matrix)。

这里对法线矩阵的解释参照了入门精要里的

变换后的法线与切线的点乘的结果也应该是0,这里将变换后的矩阵与切线转置的原因是M*T得到的结果是一个3行1列的矩阵,GN的结果是3行1列的矩阵,而我们是为了获得法线变换的矩阵,将前面得到的矩阵进行转置矩阵乘法才可以继续进行,而且书里前面说过一个向量是可以当作列矩阵或者行矩阵的。最后的结果就是原变换矩阵的逆转置矩阵。

光照合并

顶点着色器

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;out vec2 TexCoord;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;out vec3 FragPos;  
out vec3 Normal;void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0);FragPos = vec3(model * vec4(aPos, 1.0));Normal = mat3(transpose(inverse(model))) * aNormal;
}

片段着色器

#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPos;void main()
{float ambientStrength = 0.1;float specularStrength = 0.5;vec3 ambient = ambientStrength * lightColor;vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = diff * lightColor;vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm);float spec = pow(max(dot(viewDir, reflectDir), 0.0), 16);vec3 specular = specularStrength * spec * lightColor;vec3 result = (ambient + diffuse+specular) * objectColor;FragColor = vec4(result, 1.0);
}

 材质

将三个材质的构成分量创建一个结构体

将光的属性也设置成结构体,进而可以更好的控制着色器的各种属性

顶点着色器

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;out vec3 FragPos;
out vec3 Normal;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{FragPos = vec3(model * vec4(aPos, 1.0));Normal = mat3(transpose(inverse(model))) * aNormal;  gl_Position = projection * view * vec4(FragPos, 1.0);
}

片段着色器

#version 330 core
out vec4 FragColor;struct Material {vec3 ambient;vec3 diffuse;vec3 specular;    float shininess;
}; struct Light {vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;
};in vec3 FragPos;  
in vec3 Normal;  uniform vec3 viewPos;
uniform Material material;
uniform Light light;void main()
{// ambientvec3 ambient = light.ambient * material.ambient;// diffuse vec3 norm = normalize(Normal);vec3 lightDir = normalize(light.position - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = light.diffuse * (diff * material.diffuse);// specularvec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm);  float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);vec3 specular = light.specular * (spec * material.specular);  vec3 result = ambient + diffuse + specular;FragColor = vec4(result*10, 1.0);
} 

光照贴图

效果

因为很多物体并不只是由一种材质构成的,我们控制不同部分的属性,所以引入了光照贴图

修改原来的结构体,把漫反射和高光都修改为贴图,另外练习中增加的自发光贴图也加进去

贴图采样的原理和之前的教程差不多

顶点着色器

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{FragPos = vec3(model * vec4(aPos, 1.0));Normal = mat3(transpose(inverse(model))) * aNormal;  TexCoords = aTexCoords;gl_Position = projection * view * vec4(FragPos, 1.0);
}

片段着色器

最后的自发光贴图的加了个随时间移动和淡入淡出的效果,并且箱子的钢铁部分是不应该有自发光的,对自发光进行了限制

#version 330 core
out vec4 FragColor;struct Material {sampler2D diffuse;sampler2D specular;    sampler2D emission;float shininess;
}; struct Light {vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;
};in vec3 FragPos;  
in vec3 Normal;  
in vec2 TexCoords;uniform vec3 viewPos;
uniform Material material;
uniform Light light;
uniform float time;void main()
{// ambientvec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));  	// diffuse vec3 norm = normalize(Normal);vec3 lightDir = normalize(light.position - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));    // specularvec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm);  float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); vec3 emission = texture(material.emission,TexCoords+vec2(0.0,time/2)).rgb;vec3 result = ambient + diffuse + specular+emission;FragColor = vec4(result, 1.0);} 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python之自动化(django)

1、安装 我用的是pip install Django 在命令行中安装 然后django-admin startproject autotext(在命令行中) 这句话是创建一个django 项目 然后切换到你所创建项目的目录下 输入: python manage.py runserver 当你出现以下错误时 You…

CSS3技巧38:3D 翻转数字效果

博主其它CSS3 3D的文章: CSS3干货4:CSS中3D运用_css 3d-CSDN博客 CSS3干货5:CSS中3D运用-2_中3d-2-CSDN博客 CSS3干货6:CSS中3D运用-3_css3d 使用-CSDN博客 最近工作上烦心的事情太多,只有周末才能让我冷静一下 cod…

C语言分析基础排序算法——归并排序

目录 归并排序 递归版本 非递归版本 非递归版本的问题 归并排序小优化 归并排序 归并排序,分为分治以及合并,分治部分可以使用递归或者非递归完成,归并排序的基本思路是:将已有序的子序列合并,得到完全有序的序列…

spring boot集成redis实现共享存储session

spring boot集成redis实现共享存储session redis实现共享存储session 首先下载redis,我下载的版本是5.0.14,目前官网貌似找不到5.x版本&#xff0c;可以自行去网上寻找。我这里的springboot版本是2.6.4引入redis依赖 <!-- https://mvnrepository.com/artifact/org.spring…

使用endnote插入引用文献导致word英文和数字变成符号的解决方案

使用endnote插入引用文献导致word英文和数字变成符号的解决方案 如图使用endnote插入引用文献导致word英文和数字变成符号字体Wingdings Wingdings 是一个符号字体系列&#xff0c;它将许多字母渲染成各式各样的符号&#xff0c;用途十分广泛。 解决方法&#xff1a; 直接通过更…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:UIExtensionComponent (系统接口))

UIExtensionComponent用于支持在本页面内嵌入其他应用提供的UI。展示的内容在另外一个进程中运行&#xff0c;本应用并不参与其中的布局和渲染。 通常用于有进程隔离诉求的模块化开发场景。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0…

苹果Vision Pro即将在中日韩等九国开卖 | 百能云芯

苹果公司近期透露&#xff0c;首款混合实境&#xff08;MR&#xff09;头盔「Vision Pro」即将在今年晚些时候推向更多国家销售。虽然苹果尚未公布具体的销售细节&#xff0c;但根据最新的外媒报道&#xff0c;这款高科技产品可能即将在中国、日本、韩国等九个国家开卖&#xf…

RIPGeo参文33、37(最大化原始图与摄动图之间的一致性):保证不相关信息的消除

RIPGeo中有: 训练目标为: 由于需要准确的地理定位预测,损失鼓励图表示学习来保持IP地理定位的基本信号。同时,和通过最大化原始图与摄动图[33]、[37]之间的一致性,保证了不相关信息的消除。考虑到模型在训练早期的不稳定性,我们不直接同时优化多个训练目标。相反,我们采…

1.6w字数据库基础知识超详细解析~‍(进阶/复习版)

文章目录 前言一、数据库的操作1.登入数据库2.创建数据库3.显示当前数据库4.使用数据库5.删除数据库 二、常用数据类型三、数据库的约束1约束类型2NULL约束3UNIQUE:唯一约束4DEFAULT&#xff1a;默认值约束5 PRIMARY KEY&#xff1a;主键约束6 FOREIGN KEY&#xff1a;外键约束…

【类脑智能】脑网络通信模型分类及量化指标(附思维导图)

脑网络通信模型分类及量化指标(附思维导图) 参考论文&#xff1a;Brain network communication_ concepts, models and applications 概念 脑网络通信模型是一种使用图论和网络科学概念来描述和量化大脑结构中信息传递的模型。这种模型可以帮助研究人员理解神经信号在大脑内…

插入排序:一种简单而有效的排序算法

插入排序&#xff1a;一种简单而有效的排序算法 一、什么是插入排序&#xff1f;二、插入排序的步骤三、插入排序的C语言实现四、插入排序的性能分析五、插入排序的优化六、总结 在我们日常生活和工作中&#xff0c;排序是一种非常常见的操作。比如&#xff0c;我们可能需要对一…

【机器学习】分类模型的评价方法

&#x1f33b;个人主页&#xff1a;相洋同学 &#x1f947;学习在于行动、总结和坚持&#xff0c;共勉&#xff01; #学习笔记# 目录 一、混淆矩阵&#xff08;Confusion Matrix&#xff09; 二、评估指标&#xff08;Evaluation metrics&#xff09; 1.正确率(accuracy) …

C#,动态规划问题中基于单词搜索树(Trie Tree)的单词断句分词( Word Breaker)算法与源代码

1 分词 分词是自然语言处理的基础,分词准确度直接决定了后面的词性标注、句法分析、词向量以及文本分析的质量。英文语句使用空格将单词进行分隔,除了某些特定词,如how many,New York等外,大部分情况下不需要考虑分词问题。但有些情况下,没有空格,则需要好的分词算法。…

操作系统知识-操作系统作用+进程管理-嵌入式系统设计师备考笔记

0、前言 本专栏为个人备考软考嵌入式系统设计师的复习笔记&#xff0c;未经本人许可&#xff0c;请勿转载&#xff0c;如发现本笔记内容的错误还望各位不吝赐教&#xff08;笔记内容可能有误怕产生错误引导&#xff09;。 本章的主要内容见下图&#xff1a; 1、操作系统的作用…

开源绘图工具 PlantUML 入门教程(常用于画类图、用例图、时序图等)

文章目录 一、类图二、用例图三、时序图 一、类图 类的UML图示 startuml skinparam classAttributeIconSize 0 class Dummy {-field1 : String#field2 : int~method1() : Stringmethod2() : void } enduml定义能见度&#xff08;可访问性&#xff09; startumlclass Dummy {-f…

【强化学习笔记一】初识强化学习(定义、应用、分类、性能指标、小车上山案例及代码)

文章目录 第1章 初识强化学习1.1 强化学习及其关键元素1.2 强化学习的应用1.3 强化学习的分类1.3.1 按任务分类1.3.2 按算法分类 1.4 强化学习算法的性能指标1.5 案例&#xff1a;基于Gym库的智能体/环境接口1.5.1 安装Gym库1.5.2 使用Gym库1.5.3 小车上山1.5.3.1 有限动作空间…

Rocky Linux 基本工具的安装

1.系统安装后先查看ip地址 ip addr 2.安装net工具 &#xff1a;ifconfig yum install net-tools 3.安装gcc &#xff1b;选择都选 y yum install gcc yum install gcc-c 4.安装tcl yum install -y tcl 5.安装lsof &#xff08;端口查看工具&#xff09; yum install l…

Spring Web MVC入门(2)

学习Spring MVC Postman介绍 在软件工程中, 我们需要具有前后端分离的思想, 以降低耦合性. 但是在测试后端代码时,我们还得写前端代码测试,这是个令人头疼的问题. 那么我们如何测试自己的后端程序呢, 这就用到了一个工具: Postman. 界面介绍: 传参的介绍 1.普通传参, 也就…

这次玩个猛的,复现 2000 年前碳化卷轴

公元79年10月24日&#xff0c;意大利的维苏威火山爆发&#xff0c;一天之内就毁灭了两万多人的庞贝古城。 火山灰掩盖了整座城市&#xff0c;其中有一栋房子存放了各种书籍。直到18世纪&#xff0c;这栋房子才重新被发现&#xff0c;下面是考古学家的建筑复原图。 房子里面的1…

电脑那个部件坏了或者是哪个软件需要修复来看价钱

电脑维修价格表是多少&#xff1f; 价格取决于计算机的哪个部分损坏或哪个软件需要修复。 由于电脑中的部件非常多&#xff0c;而且会以各种奇怪的方式出现问题&#xff0c;下面我们就来看看具体的充电方法。 电脑维修价格表&#xff1a; 1. 重新安装系统。 安装XP系统通常需…