OpenGL学习笔记(3) 纹理
关于纹理
一般游戏里的物体不一定都是纯色的物体,物体上面会有一些图片贴在上面,比如墙壁,箱子,地板,可以看到砖头、木板和大理石组成的图片,要把图片贴到计算机里的几何图形的话,就要把图片的颜色采样贴到几何图形上,采样是计算机经常干的工作,计算机要处理自然中的数据就需要对数据进行采样,比如说对声音采样就是采集声音的频率和频幅,分别代表声音的音色和声量,当然,采集到的是一个模拟量,然而计算机无法处理模拟量,所以需要将模拟量量化为数字量,也就是二进制数进行存储,同理,颜色也是一样,现代计算机中颜色一般以RGB的形式,分别代表Red,Green,Blud三种颜色在这个颜色中的占比,一般使用3个字节存储,也就是说颜色的数量可以达到2^24种,已经足够用了,想要把采集到的颜色贴到物体上的话,我们需要指定顶点的纹理坐标,告诉着色器要从纹理的哪个点开始采样, 纹理坐标的范围是0到1
项目的代码
learnOpenGL里的图解
纹理环绕
纹理坐标的范围是0到1,假如超出这个范围的话,在OpenGL里会有几种方式来贴图,这些方式叫做纹理环绕方式
- GL_REPEAT 对纹理的默认行为。重复纹理图像。
- GL_MIRRORED_REPEAT 和GL_REPEAT一样,但每次重复图片是镜像放置的。
- GL_CLAMP_TO_EDGE 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
- GL_CLAMP_TO_BORDER 超出的坐标为用户指定的边缘颜色。
在我的工程里有用来测试的函数SurroundTest,把环绕方式和过滤模式封装成枚举方便调试
//环绕模式
enum class SurroundMode
{Repeat = GL_REPEAT, //重复纹理图像MirroredRepeat = GL_MIRRORED_REPEAT, //镜像重复纹理图像ClampToEdge = GL_CLAMP_TO_EDGE, //将边缘拉伸ClampToBoreder = GL_CLAMP_TO_BORDER //超出的部分为指定边缘颜色
};
//过滤方式
enum class FilteringMode
{Nearest = GL_NEAREST,Linear = GL_LINEAR,
};
将纹理坐标的分量值设置到[0,1]之外,进行测试
float vertices[] = {// ---- 位置 ---- - 纹理坐标 -0.5f, 0.5f, 0.0f, 2.0f, 2.0f, // 右上0.5f, -0.5f, 0.0f, 2.0f, -1.0f, // 右下-0.5f, -0.5f, 0.0f, -1.0f, -1.0f, // 左下-0.5f, 0.5f, 0.0f, -1.0f, 2.0f // 左上};
测试结果
过滤模式
可以看到环绕方式测试的图片是有一丢丢模糊的(其实是用错过滤模式了),这是因为用了线性过滤模式(Linear),使用线性过滤模式采样的时候,每个像素的颜色会和周围的颜色进行混合算出一个插值,这个插值近似于这些像素
而采用 邻近过滤(Nearest) 的话,那么像素的颜色就是采样器采到的颜色,如果分辨率小的图片贴到大的物体上的话,就会出现颗粒状的图案,但使用线性过滤模式的话这些颗粒就会变得比较平滑
测试一下
是不是可以很明显地看出Linear方式采样的珂朵莉的眼神比较温柔啊,这就是经过平滑处理过的图片。在这里顺便说下unity的三种过滤模式
- Point 点像素过滤,纹理像素会在附近变为块状。
- Bilinear 双线性过滤,平均纹理样本
- Trilinear 三线性过滤,平均纹理样本,同时也在多级渐远纹理级别之间混合
测试代码
首先是读取图片用的代码,对 learnOpenGL 里的代码进行了一次封装,针对不同位深度的图片进行处理,位深度就是图片记录每个像素点的位数,24位表示3个float,也就是RGB值,32位则是4个float,即RGBA,读取图片用了官方介绍的stb_image库
//加载纹理
template <typename S1 = std::string>
void LoadTexture(unsigned int &texture, S1&& pic)
{glGenTextures(1, &texture);glBindTexture(GL_TEXTURE_2D, texture);// 为当前绑定的纹理对象设置环绕、过滤方式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);// 加载并生成纹理int width, height, nrChannels;stbi_set_flip_vertically_on_load(true);unsigned char *data = stbi_load((TEXTURE_PATH + std::forward<std::string>(pic)).c_str(), &width, &height, &nrChannels, 0);std::cout << "nrChannels = " << nrChannels << endl;if (data){//位深度为24,3个通道(jpgif (nrChannels == 3)glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);//位深度为32,4个通道(pngelse if (nrChannels == 4)glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);}else{std::cout << "Failed to load texture" << std::endl;}stbi_image_free(data);data = nullptr;
}
着色器代码
顶点着色器
vertex_3.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;out vec2 TexCoord;void main()
{gl_Position = vec4(aPos, 1.0);TexCoord = aTexCoord;
}
位置0是顶点的坐标,位置1是输入的纹理坐标,要通过顶点着色器将纹理坐标输出到片段着色器
片段着色器
fragment_3.fs
#version 330 core
out vec4 FragColor;in vec2 TexCoord;uniform sampler2D ourTexture;void main()
{FragColor = texture(ourTexture, TexCoord);
}
TexCoord是从顶点着色器接收的纹理坐标,使用texture函数来采样,第一个参数是采样器(Sampler),第二个参数是纹理坐标,着色器输出过滤后的颜色值FragColor
接下来是就是正常的测试代码了
void NormalTest()
{float vertices[] = {// ---- 位置 ---- - 纹理坐标 -0.9f, 0.9f, 0.0f, 1.0f, 1.0f, // 右上0.9f, -0.9f, 0.0f, 1.0f, 0.0f, // 右下-0.9f, -0.9f, 0.0f, 0.0f, 0.0f, // 左下-0.9f, 0.9f, 0.0f, 0.0f, 1.0f // 左上};//索引unsigned int indices[] = {0,1,3,1,2,3};//编译着色器Shader ourShader("vertex_3.vs", "fragment_3.fs");ourShader.use();//glUseProgram(shaderProgram);unsigned int VAO, VBO, EBO;//顶点数组glGenVertexArrays(1, &VAO);glBindVertexArray(VAO);//绑定顶点数组缓存glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//索引缓存glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//生成纹理unsigned int texture1;LoadTexture(texture1, std::move("picture4.png")); //加载纹理glBindTexture(GL_TEXTURE_2D, texture1);//绑定纹理// 位置属性glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 纹理属性glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); //最后一个参数是数据的起点glEnableVertexAttribArray(1);while (!glfwWindowShouldClose(glWindow)){glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);//drawglBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glBindVertexArray(0);glfwPollEvents();glfwSwapBuffers(glWindow);}glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteBuffers(1, &EBO);glfwTerminate();
}
测试结果
珂朵莉好可爱~
转载于:https://www.cnblogs.com/NightFrost/p/10792604.html
OpenGL学习笔记(3) 纹理相关推荐
- 【OpenGL学习笔记④】——纹理贴图【SOIL2的配置 + 绘制木板 + 纹理环绕】
- 【OpenGL学习笔记⑤】——纹理变换【glm配置+两张图片交替渐变变换 + 纹理平移 + 实现雪花飘落】
- 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】
✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...
- OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结
原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7919 ...
- 【OpenGL学习笔记】地月系
OpenGL学习笔记2-地月系 文章目录 OpenGL学习笔记2-地月系 前言 运行结果 纹理图片 一.TexturePool 1.**TexturePool.h** 2.**TexturePool. ...
- OpenGL学习笔记:矩阵变换
文章目录 缩放 glm矩阵表示 glm缩放矩阵实现 位移 齐次坐标 glm位移矩阵实现 旋转 沿x轴旋转 沿y轴旋转 沿z轴旋转 沿任意轴旋转 glm旋转矩阵实现 矩阵的组合 glm矩阵组合使用 接上 ...
- OpenGL学习笔记(一)绘制点线面及多面体
OpenGL学习笔记(一)绘制点线面及多面体 绘制点线面 #include <iostream> #include <GL/GLUT.h> #define PI 3.14159 ...
- OpenGL学习笔记(一):环境搭建、三维空间坐标系理解以及OpenGL的基本使用
原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7866 ...
- 【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】
✅ 重点参考了 LearnOpenGL CN 的内容,但大部分知识内容,小编已作改写,以方便读者理解. 文章目录 零. 成果预览图 一. 光照原理与投光物的配置 1.1 光照原理 1.2 投光物 二. ...
- OPENGL学习笔记之八
OPENGL学习笔记之八 2017/11/15 阅读材料来自learnopengl.com以及learnopengl-cn.github.io 我们通常会自己设定一个坐标的范围,之后再在顶点着色器中将 ...
最新文章
- LTE SIB1时频资源
- 公众科学日|科普图书展,图灵参展啦!
- pyqt webview 执行js
- C#String.PadLeft函数,文本对齐以及填补解决方案
- SKlearn参数详解—随机森林
- 笔记本电脑销量排名_网友总结京东笔记本销量,联想高居第一,华为表现很出色...
- 内推!美团2021届应届生补招与2022届实习生招聘同步开启
- Windows Networking 4: CloudMonitor 引发的网络问题排查一则
- UNIX环境高级编程——创建与打开IPC通道
- linux# 解读wmctrl一览输出的项目
- jquery省市县三级导航栏
- Arduino学习总结
- LitsModer —— 开发日志(上)
- Mybatis中的类型转换器
- 无人机智能巡检系统及功能介绍
- 无套路送9部 kindle,价值5400元
- 常见硬件术语手册!绝对权威!(转!)七、显示卡术语解释
- 知识图谱学习思维导图
- Markdown语法简介,markdown导出PDF
- Dynamics CRM 无法导出数据
热门文章
- OpenLayers学习入门篇
- python管道_python中管道
- SQLite数据库的使用——利用命令行或Navicat Premium创建数据库
- -source 1.6 中不支持 lambda 表达式 [ERROR] (请使用 -source 8 或更高版本以启用 lambda 表达式)
- Freeswitch视频会议远超Telepresence
- LINUX下载编译libx264
- LINUX SHELL中while循环和遍历参数
- UltraEdit打开就报错,文件找不到
- VS2010:把项目由WIN32改为x64
- 龙芯指令集也可以开源