关于纹理

一般游戏里的物体不一定都是纯色的物体,物体上面会有一些图片贴在上面,比如墙壁,箱子,地板,可以看到砖头、木板和大理石组成的图片,要把图片贴到计算机里的几何图形的话,就要把图片的颜色采样贴到几何图形上,采样是计算机经常干的工作,计算机要处理自然中的数据就需要对数据进行采样,比如说对声音采样就是采集声音的频率和频幅,分别代表声音的音色和声量,当然,采集到的是一个模拟量,然而计算机无法处理模拟量,所以需要将模拟量量化数字量,也就是二进制数进行存储,同理,颜色也是一样,现代计算机中颜色一般以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) 纹理相关推荐

  1. 【OpenGL学习笔记④】——纹理贴图【SOIL2的配置 + 绘制木板 + 纹理环绕】

  2. 【OpenGL学习笔记⑤】——纹理变换【glm配置+两张图片交替渐变变换 + 纹理平移 + 实现雪花飘落】

  3. 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】

    ✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...

  4. OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7919 ...

  5. 【OpenGL学习笔记】地月系

    OpenGL学习笔记2-地月系 文章目录 OpenGL学习笔记2-地月系 前言 运行结果 纹理图片 一.TexturePool 1.**TexturePool.h** 2.**TexturePool. ...

  6. OpenGL学习笔记:矩阵变换

    文章目录 缩放 glm矩阵表示 glm缩放矩阵实现 位移 齐次坐标 glm位移矩阵实现 旋转 沿x轴旋转 沿y轴旋转 沿z轴旋转 沿任意轴旋转 glm旋转矩阵实现 矩阵的组合 glm矩阵组合使用 接上 ...

  7. OpenGL学习笔记(一)绘制点线面及多面体

    OpenGL学习笔记(一)绘制点线面及多面体 绘制点线面 #include <iostream> #include <GL/GLUT.h> #define PI 3.14159 ...

  8. OpenGL学习笔记(一):环境搭建、三维空间坐标系理解以及OpenGL的基本使用

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7866 ...

  9. 【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】

    ✅ 重点参考了 LearnOpenGL CN 的内容,但大部分知识内容,小编已作改写,以方便读者理解. 文章目录 零. 成果预览图 一. 光照原理与投光物的配置 1.1 光照原理 1.2 投光物 二. ...

  10. OPENGL学习笔记之八

    OPENGL学习笔记之八 2017/11/15 阅读材料来自learnopengl.com以及learnopengl-cn.github.io 我们通常会自己设定一个坐标的范围,之后再在顶点着色器中将 ...

最新文章

  1. LTE SIB1时频资源
  2. 公众科学日|科普图书展,图灵参展啦!
  3. pyqt webview 执行js
  4. C#String.PadLeft函数,文本对齐以及填补解决方案
  5. SKlearn参数详解—随机森林
  6. 笔记本电脑销量排名_网友总结京东笔记本销量,联想高居第一,华为表现很出色...
  7. 内推!美团2021届应届生补招与2022届实习生招聘同步开启
  8. Windows Networking 4: CloudMonitor 引发的网络问题排查一则
  9. UNIX环境高级编程——创建与打开IPC通道
  10. linux# 解读wmctrl一览输出的项目
  11. jquery省市县三级导航栏
  12. Arduino学习总结
  13. LitsModer —— 开发日志(上)
  14. Mybatis中的类型转换器
  15. 无人机智能巡检系统及功能介绍
  16. 无套路送9部 kindle,价值5400元
  17. 常见硬件术语手册!绝对权威!(转!)七、显示卡术语解释
  18. 知识图谱学习思维导图
  19. Markdown语法简介,markdown导出PDF
  20. Dynamics CRM 无法导出数据

热门文章

  1. OpenLayers学习入门篇
  2. python管道_python中管道
  3. SQLite数据库的使用——利用命令行或Navicat Premium创建数据库
  4. -source 1.6 中不支持 lambda 表达式 [ERROR] (请使用 -source 8 或更高版本以启用 lambda 表达式)
  5. Freeswitch视频会议远超Telepresence
  6. LINUX下载编译libx264
  7. LINUX SHELL中while循环和遍历参数
  8. UltraEdit打开就报错,文件找不到
  9. VS2010:把项目由WIN32改为x64
  10. 龙芯指令集也可以开源