OpenGL学习脚印: 环境纹理映射(environment mapping)

2023-12-29 06:10

本文主要是介绍OpenGL学习脚印: 环境纹理映射(environment mapping),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

写在前面
上一节初步学习了使用cubeMap创建天空包围盒,本节继续深入Cubemap这个主题,学习环境纹理贴图。本节示例程序均可以从我的github下载。

本节内容整理自:
1.www.learnopengl.com cubemaps

环境纹理贴图

同上一节的Cubemap创建天空包围盒有些类似,创建环境纹理贴图也是对当前待渲染物体,从包围的环境纹理上采样作为这个物体的纹理而渲染出的逼真效果。本节介绍环境纹理贴图主要的方式包括:reflection(反射贴图)和refraction(折射贴图)。

Reflection 反射贴图

在上一节cubemaps中,我们提到对立方体纹理进行采样,需要使用3维向量(s,t,r),而当立方体中心处于原点时,立方体的顶点位置就可以作为这个采样的坐标。对于反射贴图,我们也同样需要一个纹理坐标,不过这个向量的计算过程如下图所示(来自 www.learnopengl.com):

reflection

图中向量 I 表示观察向量,注意它从观察者位置指出,N表示顶点对应的法向量,而计算出来的反射向量 R 则作为从Cubemap采样的向量。在光照基础,一节我们已经见过使用reflect函数计算反射向量了,这里再次说明下。我们计算向量的过程都可以在世界坐标系或者相机坐标系,只要统一一个坐标系即可。这里我们使用世界坐标系,在顶点着色器中,实现为:

   #version 330 corelayout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;out vec3 FragNormal;
out vec3 FragPos;void main()
{gl_Position = projection * view * model * vec4(position, 1.0);FragPos = vec3(model * vec4(position, 1.0));    // 在世界坐标系中指定mat3 normalMatrix = mat3(transpose(inverse(model)));FragNormal = normalMatrix * normal; // 计算法向量经过模型变换后值
}

上面在世界坐标系中计算转换后的法向量时使用了公式:

FragPos=vec3(modelvec4(position,1.0));()
FragNormal=mat3(transpose(inverse(model)))normal()
对于公式不熟悉的,可以回过头去参考光照基础一节。

在片元着色器中实现为:

   #version 330 corein vec3 FragNormal;
in vec3 FragPos;uniform samplerCube envText; // 环境纹理
uniform vec3 cameraPos;out vec4 color;void main()
{vec3 viewDir = normalize(FragPos - cameraPos); // 注意这里向量从观察者位置指出vec3 reflectDir = reflect(viewDir, normalize(FragNormal));color = texture(envText, reflectDir);   // 使用反射向量采样环境纹理
}

注意上面计算过程中向量的方向和单位化。这里输入的环境纹理依然是我们的天空包围盒cubemap纹理。绘制上一节的立方体,使用反射贴图得到的效果如下图所示:

这里写图片描述

这个贴图得到的效果是,立方体的表面反射包围盒的纹理。
如果使用球体模型的话,则能得到更逼真的效果:

球体反射贴图

这个球体模型,可以从我的github下载。

Refraction 折射贴图

与反射相对应,当光从一种材质进入另一种材质时将会发生折射,满足折射定律。这里我们采用折射后得到的向量,作为采样纹理的向量,计算如下图所示(来自 www.learnopengl.com):

refraction

这里的 I 为入射向量,从观察者位置指出,N仍然是法向量,而得到的折射向量 R 作为采样纹理的向量。可以看出当光从空气进入水中时,发生了折射现象,折射向量R与原始的入射光线 I <script id="MathJax-Element-535" type="math/tex">I</script>发生了偏离。
在计算折射向量时,需要使用到折射率,这个参数,给出几种材料的折射率参考数据,如下表:

材料折射率
空气1.00
1.33
1.309
玻璃1.52
钻石2.42

从一种材质进入另一种材质,实际计算时使用两种材质的折射率的比例。实现refraction效果是,顶点着色器部分与上面相同,片元着色器需要修改,计算折射向量,如下:

   #version 330 corein vec3 FragNormal;
in vec3 FragPos;uniform samplerCube envText; // 环境纹理
uniform vec3 cameraPos;out vec4 color;void main()
{float indexRation = 1.00 / 1.52;vec3 viewDir = normalize(FragPos - cameraPos); // 注意这里向量从观察者位置指出vec3 refractDir = refract(viewDir, normalize(FragNormal), indexRation);color = texture(envText, refractDir);   // 使用 折射向量 采样环境纹理
}

上面着色器中,refractive的第三个参数是折射率的比例,这里我们模拟的是从空气进入玻璃。绘制上面的立方体,得到的效果像是透明玻璃:

refraction

环境纹理贴图和模型加载

在前面AssImp纹理加载一节,我们使用AssImp库加载了一个纳米战斗服模型,这里对模型使用反射环境贴图加以改进。之前使用的是diffuse map 和specular map,这里将使用的方法称之为reflection map。通过加载模型中的reflection map,决定渲染的物体中哪部分需要做反射环境贴图以及环境贴图的强度系数,而不是像上面球体那样,整个执行反射环境贴图。

利用AssImp加载Reflection map,遇到的麻烦在于,AssImp对Reflection map默认支持不好,因此这里使用的技巧是修改obj资源和代码,做出调整。你可以从我的github下载,修改后的模型。

在资源中使用aiTextureType_AMBIENT作为reflection map,在model.h代码中添加处理:

// 获取Reflection 注意: AssImp对Reflection支持不好 所以这里采用ambient_map
// 除了这里的代码 还需要修改对应的obj文件
std::vector<Texture> reflectionTexture;
this->processMaterial(materialPtr, sceneObjPtr, aiTextureType_AMBIENT, reflectionTexture);
textures.insert(textures.end(), reflectionTexture.begin(), reflectionTexture.end());

同时在mesh.h中也要添加相应的处理,在片元着色器中修改为:

#version 330 corein vec3 FragNormal;
in vec3 FragPos;
in vec2 TextCoord;uniform samplerCube envText;    // 环境纹理
uniform sampler2D texture_diffuse0;
uniform sampler2D specular_diffuse0;
uniform sampler2D texture_reflection0; // 反射map
uniform vec3 cameraPos;out vec4 color;void main()
{vec4    diffuseColor = texture(texture_diffuse0, TextCoord);float   relefctIntensity = texture(texture_reflection0, TextCoord).r; vec4    reflectColor = vec4(0.0, 0.0, 0.0, 0.0);if(relefctIntensity > 0.1) // 决定是否开启{vec3 viewDir = normalize(FragPos - cameraPos); vec3 reflectDir = reflect(viewDir, normalize(FragNormal));reflectColor = texture(envText, reflectDir) * relefctIntensity; // 使用反射向量采样环境纹理 使用强度系数控制}color = diffuseColor + reflectColor;
}

这里通过读取reflection map,采样纹理后,获取一个强度系数,根据强度系数来决定是否开启refleciton map,如果开启则输出颜色为diffuse map和reflection map的混合结果。

渲染时只输出diffuse map或者reflection map,以及最终得到的效果,对比如下图所示:

reflection map

对头部进行放大,我们看到了反射贴图的效果:

reflection part

反射贴图为模型的表面增加了环境成分,显得更加逼真,而且模型在移动过程中,贴图能够动态变换,反映位置的改变。

最后的说明

在实现本节内容过程中,需要注意,使用反射或者折射时需要提供物体表面法向量,不仅需要修改顶点属性数据,还要修改对应的顶点着色器,如果修改错误,可能产生错误效果如下图:

这里写图片描述

从图中结果,可以看到渲染基本的图形出错,可以立马联想到顶点属性数据配置出错。

同时在加载模型,使用reflection map时,天空包围盒使用的纹理单元,要更新为3, 因为前面加载了其他的specular map、diffuse map、reflection map:

glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_CUBE_MAP, skybox.getTextId());
glUniform1i(glGetUniformLocation(shader.programId, "envText"), 3); 

如果没有更新这个配置,导致天空包围盒的纹理单元被其他纹理占用,将发生错误,例如可能的错误结果如下:

refletion map error

关于环境纹理贴图,还有一项技术称为dynamic environment mapping。通过借助framebuffer,为每个物体渲染6个包含场景中其他物体的纹理,从而构建一个环境纹理,然后实行贴图。这种动态贴图方式,由于在framebuffer中要为每个物体执行6次场景渲染,在保持较好性能开销下使用它需要很多技巧,没有这里介绍的天空包围盒这么容易使用。在后面时间充足时,可以学习下这个技术。

这篇关于OpenGL学习脚印: 环境纹理映射(environment mapping)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/548558

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

安装nodejs环境

本文介绍了如何通过nvm(NodeVersionManager)安装和管理Node.js及npm的不同版本,包括下载安装脚本、检查版本并安装特定版本的方法。 1、安装nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 2、查看nvm版本 nvm --version 3、安装

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联