OpenGL学习笔记(模型材质、光照贴图)
目录
- 光照与材质
- 光照贴图
- 漫反射贴图
- 采样镜面光贴图
GitHub主页:https://github.com/sdpyy
OpenGL学习仓库:https://github.com/sdpyy1/CppLearn/tree/main/OpenGLtree/main/OpenGL):https://github.com/sdpyy1/CppLearn/tree/main/OpenGL
光照与材质
在现实世界里,每个物体会对光产生不同的反应。比如,钢制物体看起来通常会比陶土花瓶更闪闪发光,一个木头箱子也不会与一个钢制箱子反射同样程度的光。有些物体反射光的时候不会有太多的散射(Scatter),因而产生较小的高光点,而有些物体则会散射很多,产生一个有着更大半径的高光点。如果我们想要在OpenGL中模拟多种类型的物体,我们必须针对每种表面定义不同的材质(Material)属性。

当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting),再添加一个反光度(Shininess)分量。将这个结构体写入片段着色器,简单理解就是把物体颜色分成了三部分,然后在三种光照计算中分别使用,最后直接将光照相加即可
#version 330 core
out vec4 FragColor;// 材质结构体
struct Material {vec3 ambient;vec3 diffuse;vec3 specular;float shininess;
};// 法线
in vec3 Normal;
// 像素对应空间位置
in vec3 FragPos;
// 光照位置
uniform vec3 lightPos;
// 光照颜色
uniform vec3 lightColor;
// 摄像机位置
uniform vec3 viewPos;
// 材质
uniform Material material;
void main()
{// 环境光vec3 ambient = lightColor * material.ambient;// 漫反射vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm,lightDir),0.0);vec3 diffuse = lightColor * (diff * material.diffuse);// 高光反射vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm);float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);vec3 specular = (spec * material.specular) * lightColor;// 综合vec3 result = ambient + diffuse + specular;FragColor = vec4(result, 1.0);
}
对于结构体的uniform,赋值方法比较简单
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);

这个物体太亮了。物体过亮的原因是环境光、漫反射和镜面光这三个颜色对任何一个光源都全力反射。同样,我们可以对光源的环境光、漫反射和镜面光属性进行分别设置。这样设置可以对被光照物体的比如环境光强度统一设置
struct Light {vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;
};uniform Light light;
这样设置光照
cubeShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);cubeShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); // 将光照调暗了一些以搭配场景cubeShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);cubeShader.setVec3("light.position", lightPos);

下面可以做一些有趣的处理,让光源的ambientColor和diffuseColor随时间变化
glm::vec3 lightColor;lightColor.x = sin(glfwGetTime() * 2.0f);lightColor.y = sin(glfwGetTime() * 0.7f);lightColor.z = sin(glfwGetTime() * 1.3f);glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f); // 降低影响glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); // 很低的影响cubeShader.setVec3("light.ambient", ambientColor);cubeShader.setVec3("light.diffuse", diffuseColor);
光照贴图
在上一节中,我们将整个物体的材质定义为一个整体,但现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成。想想一辆汽车:它的外壳非常有光泽,车窗会部分反射周围的环境,轮胎不会那么有光泽,所以它没有镜面高光,轮毂非常闪亮(如果你洗车了的话)。汽车同样会有漫反射和环境光颜色,它们在整个物体上也不会是一样的,汽车有着许多种不同的环境光/漫反射颜色。总之,这样的物体在不同的部件上都有不同的材质属性。
漫反射贴图
我们希望通过某种方式对物体的每个片段单独设置漫反射颜色。用纹理就能实现。其实都是使用一张覆盖物体的图像,让我们能够逐片段索引其独立的颜色值。在光照场景中,它通常叫做一个漫反射贴图(Diffuse Map)。这也就解释了为什么把纹理作为漫反射系数。
将Material的漫反射项改为采样器,通过UV坐标采样漫反射贴图,这里还移除了环境光,因为因为环境光颜色在几乎所有情况下都等于漫反射颜色
struct Material {sampler2D diffuse;vec3 specular;float shininess;
};
之后就是常规的把采样器设置好,就可以了。提供一个加载纹理的方法
// 加载材质返回纹理ID
GLuint loadTexture(char const * path)
{GLuint 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;
}

采样镜面光贴图
虽然纹理颜色上了,但是木头和铁边的高光应该是不一样的,所以高光的系数也需要一张贴图来定义,来保证不同的位置高光系数是不一样的,下图就表示边上才有颜色,中间对高光的贡献小

同样,把高光颜色也设置为采样器
struct Material {sampler2D diffuse;sampler2D specular;float shininess;
};
最终就只有铁边反射

相关文章:
OpenGL学习笔记(模型材质、光照贴图)
目录 光照与材质光照贴图漫反射贴图采样镜面光贴图 GitHub主页:https://github.com/sdpyy OpenGL学习仓库:https://github.com/sdpyy1/CppLearn/tree/main/OpenGLtree/main/OpenGL):https://github.com/sdpyy1/CppLearn/tree/main/OpenGL 光照与材质 在现实世界里&…...
【MySQL】常用SQL--持续更新ing
一、配置信息类 1.查看版本 select version; 或 select version(); 2.查看配置 show global variables where variable_name in (basedir,binlog_format,datadir,expire_logs_days,innodb_buffer_pool_size,innodb_log_buffer_size,innodb_log_file_size,innodb_log_files_i…...
视频分析设备平台EasyCVR打造汽车门店经营场景安全:AI智慧安防技术全解析
一、方案背景 某电动车企业不停爆出维权新闻,支持和反对的声音此起彼伏,事情不断发酵、反转,每天都有新消息,令人目不暇接。车展、车店作为维权事件的高发场所,事后复盘和责任认定时,安防监控和视频监控平…...
【AI提示词】因果溯源大师
提示说明 因果遡源大师,能够帮助你找出从起点到终点的因果链。 提示词 ## Role: 因果溯源大师## Profile: - author: xxx - version: 1.0 - language: 中文 - description: 我是因果遡源大师,能够帮助你找出从起点到终点的因果链## Goals: - 帮助用户…...
Hibernate里的对象不同状态和Session的核心方法
临时状态的测试 Student student new Student("张三", "男", 22, new Date()); 以上student就是一个Transient(临时状态),此时student并没有被session进行托管,即在session的缓存中还不存在student这个对象,当执行完save方法后&a…...
模型嵌入式部署
背景 自从深度学习大规模应用以来,其中一个应用方向就是将深度学习视觉算法部署到嵌入式平台上,使用NPU推理。虽然已经做了很久的模型部署,但一直都是在公司默默耕耘,为了发展一下自己“边缘部署专家”这个个人品牌,打…...
Redlinux(2025.3.29)
1、将你的虚拟机的网卡模式设置为nat模式,给虚拟机网卡配置三个主机位分别为100、200、168的ip地址。(以nmtui命令为例) 2、测试你的虚拟机是否能够ping通网关和dns,如果不能请修改网关和dns的地址。 首先打开虚拟网络编辑器查看NAT设置里的网关IP&…...
uni-app项目运行在浏览器、微信开发者工具、mumu模拟器
一、安装HBuilder X 1、下载HBuilder X 官网网址:https://dcloud.io/hbuilderx.html 根据电脑系统下载对应的版本(我的电脑是Windows 10) 2.安装HBuilder X 直接将HBuilderX.4.61.2025040322-alpha.zip解压到自己想要存放的文件夹中 双击…...
2025-04-07 NO.3 Quest3 MR 配置
文章目录 1 MR 介绍1.1 透视1.2 场景理解1.3 空间设置 2 配置 MR 环境2.1 场景配置2.2 MR 配置 3 运行测试 配置环境: Windows 11Unity 6000.0.42f1Meta SDK v74.0.2Quest3 1 MR 介绍 1.1 透视 透视(Passthrough)是将应用的背景从虚拟的…...
抓wifi无线空口包之Macbook Pro抓包(一)
参考: 在MAC OS上进行Wi-Fi抓包和空中包分析_空口抓包和无线网卡抓包的区别-CSDN博客 WireShark中802.11帧的类型、子类型对照表_wireshark 怎么看disassociate帧和deauthenticate-CSDN博客 一、在macbook pro上,点击option 同时点击右上角wifi 功能&a…...
单元测试原则之——不要模拟值对象 (1)
1. 什么是值对象(Value Objects)? 值对象是指那些不可变且仅通过其属性(数据)来定义的对象。它们通常没有复杂的逻辑或行为,主要用于存储和传递数据。例如: ● 字符串(String) ● 数字(Integer, Double) ● 日期(LocalDate, Instant) ● 自定义的简单数据类(如…...
版本控制工具——SVN
目录 【版本控制系统】 【SVN概述】 【SVN基本使用】 【解决SVN拉取文件到本地后不显示绿色图标问题】 【版本控制系统】 版本控制系统(version control system)是一种用于管理文件变更的软件工具,主要用于记录文件的修改历史,…...
2022第十三届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(题解解析)
记录刷题的过程、感悟、题解。 希望能帮到,那些与我一同前行的,来自远方的朋友😉 大纲: 1、九进制转十进制-(解析)-简单的进制转化问题😄 2、顺子日期-(解析)-考察日期 3…...
Three.js 系列专题 5:加载外部模型
内容概述 Three.js 支持加载多种 3D 文件格式(如 GLTF、OBJ、FBX),这让开发者可以直接使用专业建模软件(如 Blender、Maya)创建的复杂模型。本专题将重点介绍 GLTF 格式的加载,并调整模型的位置和材质。 学习目标 理解常见 3D 文件格式及其特点。掌握使用 GLTFLoader 加…...
【EC200N-CN——Linux驱动移植】问题回顾
【EC200N-CN——Linux驱动移植】问题回顾 1)、开发回顾一、问题回顾与解决过程二、核心原理分析1. **USB设备识别的关键:VID/PID**2. **为什么之前不生成ttyUSB节点?**3. **为什么添加PID后就能生成节点?** 三、日志关键信息解读1…...
linux安装ollama
俩种方式都可 一、linux通过docker安装ollama镜像 1.下载安装ollama镜像 # 安装 Docker sudo yum install docker sudo systemctl start docker#docker查看所有容器 docker ps -a # 查看所有容器# docker查看指定容器 docker ps -a |grep ollama# 创建模型存储目录ÿ…...
构建k8s下Helm私有仓库与自定义Chart开发指南
#作者:程宏斌 文章目录 自定义helm模板1、开发自己的chare包2、调试chart3、安装chart 自定义helm模板 https://hub.helm.sh/ 1、开发自己的chare包 [rootmaster ~]# helm create mychare //创建一个名为mychare的chare包 [rootmaster ~]# tree -C mychare/ //以…...
【7】C#上位机---Modbus RTU 界面设计与封装
C#上位机---Modbus通讯 1 Modbus RTU 通讯1.1 RS485串口与串行通信(Serial Communications)1.2 Modbus RTU协议1.3 Modbus RTU主从模式1.4 Modbus 主从站模拟调试2 Modbus RTU 界面设计与封装2.1 温度控件的类属性2.2 C#封装Modbus实现通讯2.3 C#封装Modbus TRU通用类2.4 上位…...
【JVM】question
问题 JVM线程是用户态还是内核态 java线程在jdk1.2之前,是基于名为“绿色线程”的用户线程实现的,这导致绿色线程只能同主线程共享CPU分片,从而无法利用多核CPU的优势。 由于绿色线程和原生线程比起来在使用时有一些限制, jdk1.2…...
Node.js 中处理 Excel 文件的最佳实践
在现代应用开发中,Excel 文件仍然是数据交换和存储的重要格式之一。在 Node.js 环境中,处理 Excel 文件的需求日益增加。本文将介绍如何在 Node.js 中高效地处理 Excel 文件,涵盖工具选择、基本操作和最佳实践。 1. 选择合适的库 在 Node.js…...
【嵌入式学习6】多任务版TCP服务器
目录 如何实现: 客户端1.0版本: 服务端: 客户端2.0版本: thread.join() 是一个线程同步方法,用于主线程等待子线程完成。当你调用 thread.join() 时,主线程会阻塞,直到调用 join() 的子线程…...
每天认识一个设计模式-外观模式:化繁为简的接口魔法
一、前言 在设计模式中,结构型设计模式处理类或对象组合,可助力构建灵活、可维护软件结构。此前探讨过组合模式(将对象组合成树形结构,统一处理单个与组合对象,如文件系统管理)和装饰器模式(动…...
VLAN(虚拟局域网)
一、vlan概述 VLAN(virtual local area network)是一种通过逻辑方式划分网络的技术,允许将一个物理网络划分为多个独立的虚拟网络。每一个vlan是一个广播域,不同vlan之间的通信需要通过路由器或三层交换机 [!注意] vlan是交换机独有的技术,P…...
Transformers without Normalization论文翻译
论文信息: 作者:Jiachen Zhu, Xinlei Chen, Kaiming He, Yann LeCun, Zhuang Liu 论文地址:arxiv.org/pdf/2503.10622 代码仓库:jiachenzhu/DyT: Code release for DynamicTanh (DyT) 摘要 归一化层在现代神经网络中无处不在…...
题目练习之set的奇妙使用
♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…...
负载均衡是什么,Kubernetes如何自动实现负载均衡
负载均衡是什么? 负载均衡(Load Balancing) 是一种网络技术,用于将网络流量(如 HTTP 请求、TCP 连接等)分发到多个服务器或服务实例上,以避免单个服务器过载,提高系统的可用性、可扩…...
网站提示“不安全“怎么办?原因分析与解决方法
引言:为什么浏览器会提示网站"不安全"? 当您访问某些网站时,浏览器可能会显示"不安全"警告。这通常意味着该网站存在安全风险,可能影响您的隐私或数据安全。本文将介绍常见原因及解决方法,帮助您…...
如何利用AI智能生成PPT,提升工作效率与创意表现
如何利用AI智能生成PPT,提升工作效率与创意表现!在这个信息爆炸的时代,制作一份既专业又富有创意的PPT,已经不再是一个简单的任务。尤其是对于每天都需要做报告、做展示的职场人士来说,PPT的质量直接影响着工作效率和个…...
【11】Redis快速安装与Golang实战指南
文章目录 1 Redis 基础与安装部署1.1 Redis 核心特性解析1.2 Docker Compose 快速部署1.3 Redis 本地快速部署 2 Golang 与 Redis 集成实战2.1 环境准备与依赖安装2.2 核心操作与数据结构实践2.2.1 基础键值操作2.2.2 哈希结构存储用户信息 3 生产级应用场景实战3.1 分布式锁实…...
【数据结构】图论存储革新:十字链表双链设计高效解决有向图入度查询难题
十字链表 导读一、邻接表的优缺点二、十字链表2.1 结点结构2.2 原理解释2.2.1 顶点表2.2.2 边结点2.2.3 十字链表 三、存储结构四、算法评价4.1 时间复杂度4.2 空间复杂度 五、优势与劣势5.1 优势5.2 劣势5.3 特点 结语 导读 大家好,很高兴又和大家见面啦ÿ…...
