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

【OpenGL】LearnOpenGL学习笔记09 - 材质、光照贴图

上接:https://blog.csdn.net/weixin_44506615/article/details/150411373?spm=1001.2014.3001.5501
完整代码:https://gitee.com/Duo1J/learn-open-gl

一、材质

材质就是用来描述一个物体"应该是什么样的",它具备什么视觉特征、物理特性的一系列参数和纹理的集合

在这之前,我们使用了objectColor变量来定义我们的立方体的颜色,接下来我们将其抽离出来,封装成一个材质结构体,为光照模型的每一个分量提供一个颜色,以便之后我们应用贴图来进行更细颗粒度的控制
直接上代码
FragmentShader.fs

#version 330 core// 物体材质结构体
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;out vec4 FragColor;uniform vec3 lightColor;
uniform vec3 viewPos;
uniform Material material;
uniform Light light;void main()
{// 环境光vec3 ambient = lightColor * material.ambient * light.ambient;// 漫反射vec3 normal = normalize(Normal);vec3 lightDir = normalize(light.position - FragPos);vec3 diffuse = max(dot(normal, lightDir), 0.0) * lightColor * material.diffuse * light.diffuse;// 镜面反射vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, normal);vec3 specular = pow(max(dot(viewDir, reflectDir), 0), material.shininess) * lightColor * material.specular * light.specular;FragColor = vec4(ambient + diffuse + specular, 1.0f);
}

Main.cpp

// 传入材质参数
shader.SetVec3("material.ambient", glm::vec3(1.0f, 0.5f, 0.31f));
shader.SetVec3("material.diffuse", glm::vec3(1.0f, 0.5f, 0.31f));
shader.SetVec3("material.specular", glm::vec3(0.5f, 0.5f, 0.5f));
shader.SetFloat("material.shininess", 32.0f);
shader.SetVec3("light.ambient", glm::vec3(0.2f, 0.2f, 0.2f));
shader.SetVec3("light.diffuse", glm::vec3(0.5f, 0.5f, 0.5f));
shader.SetVec3("light.specular", glm::vec3(1.0f, 1.0f, 1.0f));
shader.SetVec3("light.position", lightPos);

编译运行,顺利的话可以看见以下图像
材质

二、光照贴图

现在我们的立方体看起来还是一个纯色的塑料小方块,如果我们想要为我们的立方体赋予更多的细节,比如让我们的立方体变成一个木条箱,那么我们就需要用到光照贴图

1. 漫反射贴图 (Diffuse Map)

首先我们需要一张贴图资产,可以右键保存以下图片或是在顶部的git仓库中的Resource目录找到所有需要用到的资产
我们将图片命名为T_Box_Diffuse.png
漫反射贴图
接下来修改我们的Material结构体以及片段着色器
FragmentShader.fs

struct Material {// 将ambient和diffuse颜色改为sampler2D采样器sampler2D diffuse;vec3 specular;float shininess;
};in vec2 TexCoords;// ...
// 修改环境光和漫反射计算
void main()
{// 环境光vec3 ambient = lightColor * vec3(texture(material.diffuse, TexCoords)) * light.ambient;// 漫反射vec3 normal = normalize(Normal);vec3 lightDir = normalize(light.position - FragPos);vec3 diffuse = max(dot(normal, lightDir), 0.0) * lightColor * vec3(texture(material.diffuse, TexCoords)) * light.diffuse;// ...
}

在片段着色器中,我们又将用到TexCoords,也就是UV坐标,所以我们需要修改下顶点数组、顶点属性以及顶点着色器
VertexShader.vs

#version 330 corelayout (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()
{gl_Position = projection * view * model * vec4(aPos, 1.0);FragPos = vec3(model * vec4(aPos, 1.0));Normal = mat3(transpose(inverse(model))) * aNormal;TexCoords = aTexCoords;
}

Main.cpp

float vertices[] = {// positions          // normals           // texture coords-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 0.0f,0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 1.0f,-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,-0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 0.0f,0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,-0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 1.0f,-0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,-0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 1.0f,-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,-0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 0.0f,-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f,0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f,0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 1.0f,0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,-0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 0.0f,-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f,0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 1.0f,0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f,-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f
};int step = 8, curStep = 0;
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, step * sizeof(float), (void*)curStep);
glEnableVertexAttribArray(0);
curStep += 3;
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, step * sizeof(float), (void*)(curStep * sizeof(float)));
glEnableVertexAttribArray(1);
curStep += 3;
// UV坐标
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, step * sizeof(float), (void*)(curStep * sizeof(float)));
glEnableVertexAttribArray(2);
curStep += 2;

接下来我们需要在c++中加载这张漫反射贴图并传递到着色器中
之后我们会经常处理纹理贴图,方便起见,我们现在来封装一个简单的纹理类
Texture.h 新建

#pragma once#include <iostream>#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "stb_image.h"/**
* 纹理类
*/
class Texture
{
public:/*** 纹理宽度*/int width = 0;/*** 纹理高度*/int height = 0;/*** 纹理通道数*/int channel = 0;private:/*** 纹理ID*/unsigned int textureID = 0;public:explicit Texture(const char* path);/*** 获取纹理ID*/unsigned int GetTextureID();
};

Texture.cpp 新建

#include "Texture.h"Texture::Texture(const char* path)
{unsigned char* textureData = stbi_load(path, &width, &height, &channel, 0);glGenTextures(1, &textureID);if (textureData){glBindTexture(GL_TEXTURE_2D, textureID);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);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);if (channel == 4){glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);}else{glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData);}glGenerateMipmap(GL_TEXTURE_2D);stbi_image_free(textureData);std::cout << "Load texture: " << path << " success, with: " << width << " height: " << height << " channel: " << channel << std::endl;}else{std::cout << "[Error] Failed to load texture: " << path << std::endl;}
}unsigned int Texture::GetTextureID()
{return textureID;
}

接下来加载贴图并传入到着色器
Main.cpp

Texture boxTex("F:/Scripts/Cpp/LearnOpenGL/learn-open-gl/Resource/T_Box_Diffuse.png");while (!glfwWindowShouldClose(window))
{// ...shader.SetInt("material.diffuse", 0);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, boxTex.GetTextureID());glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 36);// ...
}

编译运行,顺利的话可以看见以下图像
如果遇到什么问题,可以参考顶部的git仓库
漫反射贴图

2. 镜面反射贴图 (Specular Map)

我们注意到,箱子上的高光部分,无论是金属边框还是中间的木条上都有高光,但是木条是比较粗糙的,我们不希望木条上有这么强烈的高光表现,接下来就要用到镜面反射贴图
同样,你可以右键保存或是在顶部的git仓库的Resource目录找到
镜面反射贴图
这张贴图通过颜色值来表示某一像素是否需要受到高光 (黑或白),以及受到高光的强度 (灰)
首先修改我们的片段着色器
FragmentShader.fs

#version 330 core// 物体材质结构体
struct Material {sampler2D diffuse;sampler2D specular;float shininess;
};// ...void main()
{// ...// 镜面反射vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, normal);vec3 specular = pow(max(dot(viewDir, reflectDir), 0), material.shininess) * lightColor * vec3(texture(material.specular, TexCoords)) * light.specular;// ..
}

接着,加载镜面反射贴图并传递到着色器中

Texture boxSpecularTex("F:/Scripts/Cpp/LearnOpenGL/learn-open-gl/Resource/T_Box_Specular.png");// ...while (!glfwWindowShouldClose(window))
{// ...shader.SetInt("material.specular", 1);// ...glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, boxSpecularTex.GetTextureID());glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 36);// ...
}

编译运行,顺利的话可以看见以下图像
只有木箱的边缘金属部分会受到高光表现
镜面反射贴图
完整代码可在顶部git仓库中找到
下接:https://blog.csdn.net/weixin_44506615/article/details/150446490?spm=1001.2014.3001.5502

http://www.xdnf.cn/news/1309771.html

相关文章:

  • 登录与登录校验:Web安全核心解析
  • 【昇腾】单张48G Atlas 300I Duo推理卡MindIE+WebUI方式跑7B大语言模型_20250816
  • 如何在FastAPI中玩转APScheduler,实现动态定时任务的魔法?
  • 【wmi异常】关于taskkill命令提示“错误:找不到” 以及无法正常获取设备机器码的处理办法
  • pytorch例子计算两张图相似度
  • PHP反序列化的CTF题目环境和做题复现第2集_POP链构造
  • 利用Qwen大模型进行c++11并发库的学习,与时俱进!!!!
  • AI安全增强核心技术:提示词防火墙、置信度过滤与知识蒸馏防御
  • 第6问 数据分析领域主要的岗位有哪些?
  • Rust 入门 KV存储HashMap (十七)
  • pdf合并代码
  • 【C++】异常详解(万字解读)
  • FPGA串口通信实现方案
  • Qt QDateTime时间部分显示为全0,QTime赋值后显示无效问题【已解决】
  • 【C++】C++11
  • Maven私服配置模版
  • 深入详解PCB布局布线技巧-去耦电容的摆放位置
  • IOMMU的2级地址翻译机制及多级(2~5)页表查找
  • Python 项目高频设计模式实战指南:从理念到落地的全景剖析
  • 电路方案分析(二十一)笔记本电脑散热风扇参考设计
  • 【运维心得】三步更换HP笔记本电脑外壳
  • 玄机靶场 | 日志分析-Tomcat日志分析
  • Tomcat架构深度解析:从Server到Servlet的全流程揭秘
  • Jenkins常见问题及解决方法
  • js原生实现手写签名与使用signature_pad库实现手写签名
  • 【科研绘图系列】R语言在DOM再矿化数据分析与可视化中的应用
  • 【CF】Day128——杂题 (图论 + 贪心 | 集合 + 贪心 + 图论 | 二分答案 + 贪心)
  • bev 感知算法 近一年来的新进展
  • echarts 画一个饼图,并且外围有一个旋转动画
  • pytest tmpdir fixture介绍(tmpdir_factory)(自动在测试开始前创建一个临时目录,并在测试结束后删除该目录)