当前位置: 首页 > news >正文

LearnOpenGL-光照-4.光照贴图

本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正

我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject

文章目录

  • 光照贴图
  • 漫反射贴图
    • 例子1
  • 镜面光贴图
    • 例子2 采样镜面光贴图
  • 小结
    • 什么是光照贴图
    • 光照贴图如何影响颜色

光照贴图

  • 上一节手动设置材质的缺点
    • 不能对一个物体的视觉输出提供足够多的灵活性。
    • 将整个物体的材质定义为一个整体,但现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成

所以我们需要拓展之前的系统,引入漫反射镜面光贴图(Map)。这允许我们对物体的漫反射分量(以及间接地对环境光分量,它们几乎总是一样的)和镜面光分量有着更精确的控制。

漫反射贴图

  • 纹理作用

    能够让我们根据片段在物体上的位置来获取颜色值,让我们能够逐片段索引其独立的颜色值

  • 纹理变为漫反射贴图

    在光照场景中,纹理通常叫做一个漫反射贴图(Diffuse Map)(3D艺术家通常都这么叫它),它是一个表现了物体所有的漫反射颜色的纹理图像。

例子1

glsl

#version 330 core
out vec4 FragColor;in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;// 纹理坐标uniform vec3 viewPos;struct Material {sampler2D diffuse;	// 纹理单元vec3 specular;		// 镜面光照颜色分量依旧是手动设置float shininess;
}; 
uniform Material material;// 光照强度
struct Light {vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;
};uniform Light light;
void main()
{// 环境光照分量float ambientStrength = 0.1;// 从漫反射纹理读取颜色分量vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));// 漫反射光照分量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));// 从漫反射纹理读取颜色分量// 镜面光照分量float specularStrength = 0.5;vec3 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, 1.0);
}
  • 可见

    依旧是冯氏光照模型

    • 镜面光照的颜色分量是手动设置的

    • 而漫反射光照的颜色分量是读取纹理

      vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

      漫反射光照分量 = 光源漫反射颜色分量 * 对当前片段采样漫反射纹理颜色 * 光源对片段的漫反射影响

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

cpp

要更新顶点数据(坐标、法线、纹理),要重新设置顶点属性指针,要加载纹理

 // position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);// load and create a texture 
// -------------------------
unsigned int texture1 = loadTexture("assest/textures/container2.png");
// 纹理加载出过错,由于图片是rgba格式,没有自动读取格式,而是硬编码设置了rgb,所以导致错误// 设置使用的纹理单元
lightingShader.use();
lightingShader.setInt("material.diffuse", 0);// 绘制前绑定漫反射贴图-出过错,放在绘制光源cube那里
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);// render the cube
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);// 根据图片的通道不同,设置加载图像的参数
unsigned int loadTexture(char const* path)
{unsigned int textureID;glGenTextures(1, &textureID);int width, height, nrComponents;unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);if (data){GLenum format;if (nrComponents == 1)format = GL_RED;else if (nrComponents == 3)format = GL_RGB;else if (nrComponents == 4)format = GL_RGBA;glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, 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;
}

materials_diffuse_map

镜面光贴图

  • 例子1效果的不足

    如例子1的效果,木头不应该这么强的镜面光。

  • 不足原因

    因为我们手动设置的镜面光强度,适用于整个物体,整个物体的镜面光颜色分量都一样

  • 需要镜面光贴图解决

    引入镜面光贴图,将木头部分的颜色为黑色,glsl读取时会变成0,即:没有镜面光

    而四周金属部分保持原样,glsl读取时会大于0,即:具有镜面光

例子2 采样镜面光贴图

相比例子1

  • cpp

    增加了一个纹理,并设置它的纹理单元

  • glsl

    镜面光不再是手动指定整个物体都是同一个颜色分量,而是读取材质的颜色作为颜色分量

    #version 330 core
    out vec4 FragColor;in vec3 Normal;
    in vec3 FragPos;
    in vec2 TexCoords;// 纹理坐标uniform vec3 viewPos;struct Material {sampler2D diffuse;// 纹理单元//vec3 specular;    // 镜面光照颜色分量依旧是手动设置sampler2D specular;//  镜面光照颜色分量从纹理采样float shininess;
    }; 
    uniform Material material;// 光照强度
    struct Light {vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;
    };uniform Light light;
    void main()
    {// 环境光照分量float ambientStrength = 0.1;// 从漫反射纹理读取颜色分量vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); // 漫反射光照分量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));// 从漫反射纹理读取颜色分量// 镜面光照分量float specularStrength = 0.5;vec3 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 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); vec3 result = (ambient + diffuse + specular) ;FragColor = vec4(result, 1.0);
    }
    
  • 效果

    请添加图片描述

小结

什么是光照贴图

  • 上节

    lightingShader.setVec3("material.ambient", 1.0f, 0.5f, 0.31f);
    lightingShader.setVec3("material.diffuse", 1.0f, 0.5f, 0.31f);
    lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
    lightingShader.setFloat("material.shininess", 32.0f);
    

    手动定义了物体的材质,即设置了环境光、漫反射光、镜面光照颜色分量

  • 而这节用光照贴图

    代替手动定义材质,从光照贴图中读取三种光照的颜色分量。

    光照贴图等同纹理,只不过在光照场景下,纹理被称为光照贴图。

  • 光照贴图包含

    • 漫反射贴图
    • 镜面光贴图

光照贴图如何影响颜色

  • 漫反射贴图

    根据各个片段的uv读取漫反射贴图上的颜色值,然后作为漫反射、环境光照颜色分量,乘以2.2节光源对片段的漫反射影响再乘以光源颜色分量。

  • 镜面光贴图

    根据各个片段的uv读取镜面光贴图上的颜色值,然后作为镜面光颜色分量,乘以2.2节光源对片段的镜面光影响再乘以光源颜色分量。

相关文章:

LearnOpenGL-光照-4.光照贴图

本人刚学OpenGL不久且自学&#xff0c;文中定有代码、术语等错误&#xff0c;欢迎指正 我写的项目地址&#xff1a;https://github.com/liujianjie/LearnOpenGLProject 文章目录光照贴图漫反射贴图例子1镜面光贴图例子2 采样镜面光贴图小结什么是光照贴图光照贴图如何影响颜色光…...

ThreadLocal解析

ThreadLocal是一个存储线程本地变量的对象&#xff0c;在ThreadLocal中存储的对象在其他线程中是不可见的&#xff0c;本文介绍ThreadLocal的原理。 1、threadLocal使用 有如下代码&#xff1a; Slf4j public class TestThreadLocal {public static void main(String[] args…...

时间格式表

时间格式化对照表 仅供参考标识符含义aAM/PM(上午/下午)A0~86399999 (一天的第A微秒)c/cc1~7 (一周的第一天, 周天为1)cccSun/Mon/Tue/Wed/Thu/Fri/Sat (星期几简写)ccccSunday/Monday/Tuesday/Wednesday/Thursday/Friday/Saturday (星期几全拼)d1~31 (月份的第几天, 带0)D1~36…...

enscape和twinmotion哪个好用?

Twinmotion 和 Enscape这2款渲染软件最近受到了一些初学者的关注。这 2 个软件适用于那些需要 3D 渲染但质量不是他们项目的首要任务的人。在本文中&#xff0c;我们将对Twinmotion 和 Enscape 进行面对面的比较&#xff0c;并帮助您确定哪一个更适合您。什么是 Twinmotion&…...

Canvas

canvas介绍 什么是 Canvas&#xff1f;Canvas 是为了解决 Web 页面中只能显示静态图片这个问题而提出的&#xff0c;一个可以使用 JavaScript 等脚本语言向其中绘制图像的 HTML 标签。 Canvas 解决了什么问题 我在 MSDN&#xff08;《Microsoft Developer Network》是微软一…...

旅游预约APP开发具有什么优势和功能

旅游活动目前正在作为广大用户休闲娱乐的一个首选内容&#xff0c;不仅是公司团建活动可以选择旅游&#xff0c;而且一些节假日也可以集结自己的亲朋好友来一次快乐有趣的旅游活动&#xff0c;随着当代人对于旅游的需求呈现上升的趋势&#xff0c;也让旅游预约APP开发开始流行并…...

Python之函数参数细讲

文章目录前言一、了解形式参数和实际参数1. 通过作用理解2. 通过一个比喻来理解形式参数和实际参数二、位置参数1. 数量必须与定义时一致2. 位置必须与定义时一致三、关键字参数四、为参数设置默认值五、可变参数1. *parameter2. **parameter总结前言 在调用函数时&#xff0c;…...

跑步耳机入耳好还是不入耳好、十大跑步运动耳机品牌排行榜推荐

健身房经常会播放一些节奏较快的歌曲&#xff0c;这样能够激发大家在运动过程中的动力&#xff0c;所以运动时聆听音乐确实比较有效果&#xff0c;居家运动、室外跑步时选择运动耳机就变成了刚需&#xff0c;首先不能影响其他人、佩戴时要稳定&#xff0c;音质和续航要有保证&a…...

Go语言容器之数组和切片

Go语言的容器分为值类型和引用数据类型 一、数组 1.数组的声明和初始化 (1) 数组声明的语法 var 数组变量名 [数组大小]数组类型 举例&#xff1a; package main import "fmt"func main(){//数组的声明var arr[10]int//打印数组长度fmt.Println("arr的长度为…...

【ROS2知识】humble下使用插件编程

Creating and using plugins (C++) — ROS 2 Documentation: Humble documentation 一、说明 接口编程的好处不言自明,有兴趣的朋友可以看看相关文章。此处在ROS2上进行接口编程,这是个技术难点,如果不能突破,那么许多方面将不能进行,比如:navigation中的costmap_2d包中…...

MySQL 主备一致

MySQL 主备一致主备切换binlog 格式statementrowmixed生产格式循环复制问题主备切换 MySQL 主备切换流程 : 状态 1 : 客户端的读写都直接访问节点 A&#xff0c;而节点 B 是 A 的备库&#xff0c;只将 A 的更新都同步过来 , 并本地执行。来保持节点 B 和 A 的数据是相同当切换…...

玩转CodeQLpy之用友GRP-U8漏洞挖掘

0x01 前言CodeQLpy是作者使用python3实现的基于CodeQL的java代码审计工具&#xff0c;github地址https://github.com/webraybtl/CodeQLpy。通过CodeQLpy可以辅助代码审计人员快速定位代码中的问题&#xff0c;目前支持对SprintBoot的jar包&#xff0c;SpringMVC的war包&#xf…...

GMP调度模型总结

优秀文章 什么是GMP调度模型 Golang的一大特色就是Goroutine。Goroutine是Golang支持高并发的重要保障。Golang可以创建成千上万个Goroutine来处理任务&#xff0c;将这些Goroutine分配、负载、调度到处理器上采用的是G-M-P模型。 什么是Goroutine Goroutine Golang Coro…...

蓝桥回文日期题

题目 题目描述 2020 年春节期间&#xff0c;有一个特殊的日期引起了大家的注意&#xff1a;2020 年 2 月 2 日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202&#xff0c;恰好是一个回文数。我们称这样的日期是回文日期。 有人表示 20200202 是 “千年…...

【2023】某python语言程序设计跟学第三周内容

目录1.数字类型与操作&#xff1a;整数&#xff1a;浮点数&#xff1a;复数数值运算操作符数字之间关系数值运算函数2.案例&#xff1a;天天向上的力量第一问&#xff1a;1‰的力量第二问&#xff1a;5‰和1%的力量第三问&#xff1a;工作日的力量第四问&#xff1a;工作日的努…...

c++11右值引发的概念

右值引用右值&&左值c11增加了一个新的类型&#xff0c;右值引用&#xff0c;记作&#xff1a;&&左值是指在内存中有明确的地址&#xff0c;我们可以找到这块地址的数据&#xff08;可取地址&#xff09;右值是只提供数据&#xff0c;无法找到地址&#xff08;不…...

MySQL 02 :三层结构、备份删除数据库

MySQL 02 &#xff1a;数据库三层结构-破除MySQL神秘 请添加图片描述 通过golang操作MySQL 创建删除数据库 备份恢复数据库 第一次需要配置环境&#xff0c;否则会报错 报错&#xff1a;mysqldump: Got error: 1045: Access denied for user ‘root’‘localhost’ (using …...

质量员错题合集

项目部质量员根据规范要求认为&#xff0c;接地用的绝缘铜电线规定最小截面为( )mm。4 项目部质量员根据规范要求认为&#xff0c;接地用的绝缘铜电线规定最小截面为4mm&#xff0c;是从( )性能考虑的。机械、 案例中所使用的ZST型闭式喷头的工作压力是( )MPa。1.2 案例中所…...

请教大神们,pmp考试和复习有什么攻略诀窍吗?

PMP考试通过率挺高的&#xff0c;很多考生也是朝九晚五甚至天天加班的打工人&#xff0c;还是有很多人通过了的&#xff0c;我也是下班后和周末才有时间学习的&#xff0c;3A通过&#xff0c;但不是什么考试大神&#xff0c;每天抽出3-4个小时跟着培训机构制定的学习计划学习&a…...

Go语言基础之接口

Go语言基础之接口1.Go语言接口类型2.类型与接口的关系一个类型实现多个接口多种类型实现同一接口3.空接口4.类型断言1.Go语言接口类型 每个接口类型由任意个方法签名组成&#xff0c;接口的定义格式如下&#xff1a; type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...