本系列文章为Learn OpenGL个人学习总结!
OpenGL入门(一)之认识OpenGL和创建Window
OpenGL入门(二)之渲染管线pipeline,VAO、VBO和EBO
OpenGL入门(三)之着色器Shader
OpenGL入门(四)之纹理Texture
OpenGL入门(五)之Matrix矩阵操作和坐标系统
OpenGL进阶(一)之帧缓冲FrameBuffer
OpenGL进阶(二)之像素缓冲PixelBuffer

纹理

纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节!我们可以在一张图片上插入非常多的细节,这样就可以让物体非常精细而不用指定额外的顶点,来减小开销!

为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。

纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。

一个纹理坐标:

float texCoords[] = {0.0f, 0.0f, // 左下角1.0f, 0.0f, // 右下角0.5f, 1.0f // 上中
};

纹理环绕方式

当纹理坐标超出默认范围时,可以设置环绕方式来展示不同的视觉效果输出!默认为GL_REPEAT,还有GL_MIRRORED_REPEATGL_CLAMP_TO_EDGEGL_CLAMP_TO_BORDER

前边提到过纹理坐标的分量可以通过STPQ访问:

// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

纹理过滤

纹理像素(Texture Pixel):组成一张图片的无数个像素点
纹理坐标:设置的顶点数组

OpenGL以这个顶点的纹理坐标数据去查找纹理图像上的像素,然后进行采样提取纹理像素的颜色。那么就会有一个问题,在对应查找像素的时候,用什么方式去拿像素颜色,OpenGL提供了纹理过滤来帮助我们选择,这里有两个重要的选项!

  • GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)是OpenGL默认的纹理过滤方式。当设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素。
  • GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。

当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤的选项:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //缩小
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //放大

多级渐变纹理

多级渐变纹理主要是用来解决物体远近,纹理大小,分辨率不同的问题!距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个,且节省内存!

注意: 多级渐远纹理主要是使用在纹理被缩小的情况下的:纹理放大不会使用多级渐远纹理。

另外:OpenGL提供glGenerateMipmap()函数,在创建完一个纹理后调用它OpenGL就会为纹理图像创建一系列多级渐远纹理!

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);...//创建纹理
glGenerateMipmap(GL_TEXTURE_2D);

加载和创建纹理

这里使用stb_image.h库来加载图片!
https://github.com/nothings/stb

使用非常简单,在我们的cpp文件中添加:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

创建纹理:

int width, height, nrChannels;
//这里会拿到图像的宽高和颜色通道个数
unsigned char *data = stbi_load("../dependency/stb/container.jpg", &width, &height, &nrChannels, 0);//生成纹理
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);//绑定... //设置环绕和过滤/*** @brief 生成纹理* 第1个参数:指定纹理target* 第2个参数:指定多级渐远纹理的级别  0:基本级别* 第3个参数:纹理存储格式。 这里图像只有RGB* 第4,5个参数:纹理的宽高* 第6个参数:总设置为0(历史问题)* 第7,8个参数:源图像的格式和数据类型* 第9个参数:图像数据
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);...
stbi_image_free(data);//释放图像内存

应用纹理

这里我们绘制一个矩形,并把图片纹理贴在矩形上

    //绘制矩形+纹理float vertices[] = {//     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下-0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下-0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上};const char *vertexShaderSource = "#version 330 core\n""layout (location = 0) in vec3 aPos;\n""layout (location = 1) in vec3 aColor;\n""layout (location = 2) in vec2 aTexCoord;\n""out vec3 vertexColor;\n""out vec2 texCoord;\n""void main()\n""{\n""   gl_Position = vec4(aPos, 1.0);\n""   vertexColor = aColor;\n""   texCoord = aTexCoord;\n""}\0";const char *fragmentShaderSource = "#version 330 core\n""in vec3 vertexColor;\n" //接收顶点着色器中的输入变量(名称、类型相同) "in vec2 texCoord;\n" //接收纹理(名称、类型相同)"out vec4 color;\n""uniform sampler2D texture1;\n" //声明一个纹理采样器"void main()\n""{\n""   color = texture(texture1, texCoord) * vec4(vertexColor, 1.0);\n" //将输出颜色设置为纹理  第一个参数纹理采样器,第二个参数纹理坐标"}\0";...

GLSL内建的texture()函数,可以得到一个纹理颜色,第一个参数纹理采样器,第二个参数纹理坐标。

这样我们就能绘制出一个正确的纹理图片了!

需要注意的点:
在前边我们了解了uniform这个变量,这里在片段着色器中声明了一个采样器,但是在代码中却没有给它赋值,目前看工作正常,能够绘制出图片!这是因为OpenGL中有默认的纹理单元0,且这个纹理单元默认激活!

纹理单元

当我们需要绘制多个纹理时,就要使用纹理单元,把纹理单元赋值给采样器,就能够和纹理一一对应起来!

glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);

激活一个纹理单元之后,调用glBindTexture,就会把纹理绑定到当前已激活的纹理单元!

OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。

修改一下我们的片段着色器:

const char *fragmentShaderSource = "#version 330 core\n""in vec3 vertexColor;\n" //接收顶点着色器中的输入变量(名称、类型相同) "in vec2 texCoord;\n" //接收纹理(名称、类型相同)"out vec4 color;\n""uniform sampler2D texture1;\n" //声明一个纹理采样器"uniform sampler2D texture2;\n" "void main()\n""{\n""   color = mix(texture(texture1, texCoord), texture(texture2, texCoord), 0.2) * vec4(vertexColor, 1.0);\n" //将输出颜色设置为纹理  第一个参数纹理采样器,第二个参数纹理坐标"}\0";...
//有两个纹理时,就需要设置纹理单元
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);//指定采样器属于哪个纹理单元
glUniform1i(glGetUniformLocation(shaderProgram, "texture2"), 1);...
//渲染
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
...

GLSL内建的mix函数需要接受两个值作为参数,并对它们根据第三个参数进行线性插值。如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。

另外一个需要注意的问题:纹理上下颠倒了!这是因为OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部。
stb_image.h能够在图像加载时帮助我们翻转y轴,在加载前调用:

stbi_set_flip_vertically_on_load(true);

这时我们将得到两个纹理叠加的效果!

OpenGL入门(四)之纹理Texture相关推荐

  1. 【OpenGL】OpenGL入门之纹理(Texture)

    目录 纹理 纹理环绕方式 纹理过滤 多级渐远纹理(Mipmap) 加载与创建纹理 stb_image.h 生成纹理 应用纹理 纹理单元 参考 纹理   在此之前,我们已经可以为每个顶点添加颜色来增加图 ...

  2. OpenGL从入门到精通--纹理

    纹理 github源码仓库 opengl环境准备 opengl编程从入门到精通-hello,window OpenGL从入门到精通–你好三角形 OpenGL从入门到精通–着色器的使用 我们可以为每个顶 ...

  3. Learn OpenGL (四):纹理

    为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分.这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采 ...

  4. OpenGL入门之纹理的使用

    参考: https://learnopenglcn.github.io/01%20Getting%20started/06%20Textures/#_7 OpenGL超级宝典 纹理 纹理是一种能够应用 ...

  5. OpenGL入门学习

    OpenGL作为当前主流的图形API之一,它在一些场合具有比DirectX更优越的特性. 1.与C语言紧密结合.  2.强大的可移植性.  3.高性能的图形渲染. 总之,OpenGL是一个很NB的图形 ...

  6. 最全面的openGL 入门学习

    自己在找openGL学习资料的时候,找到此篇openGL入门学习(虽然不是移动开发,但给我提供了非常好的思路),所以转一下让更多人知道,本文来自http://www.cppblog.com/doing ...

  7. OpenGL入门学习 (转)

    OpenGL入门学习 (转) 说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜 ...

  8. OpenGL入门学习[三]

    OpenGL入门学习[三] http://xiaxveliang.blog.163.com/blog/static/2970803420126246501930/ OpenGL入门学习[十一] 我们在 ...

  9. 【待完善】OpenGL入门学习

    严正声明:本文转载自网络,但具体出处未知.如果有读者了解,请联系我更正. 为了阅读方便,我对文字格式进行了修改,并填补了缺少的图片. 我尊重每位作者的权益,如果本文存在侵权行为,请联系我删除并道歉. ...

最新文章

  1. 端到端对话模型新突破!Facebook发布大规模个性化对话数据库
  2. 手机用appnium,web自动化用eclips+webdriver2
  3. 开启Linux下Telnet服务
  4. Ubuntu下使用中文语言
  5. java后台开发实习--第一次面试
  6. Linux 基础学习大考核
  7. python去重复元素_python 去除单个list中的重复元素
  8. Longest Palindrome CodeForces - 1304B(思维)
  9. wordpress多站点主站调用分站最新文章_企业网站SEO最新的7个优化步骤!
  10. [Unity] Animation Blend Tree 中混合值变化时部分骨骼错误旋转 360 度的解决办法:将 Humanoid 改成 Generic
  11. php中背景图怎么设置不重复,css 图像不重复怎么设置
  12. VS2013密钥 VS2013专业版密钥 VS2013旗舰版密钥
  13. 判断单链表是否中心对称
  14. 怎么对文件夹名称进行编号排序
  15. 如何将Word转PDF?来看这几个方法
  16. ug中模型不见了怎么办_UG双击prt文件却打不开模型是怎么回事?来看看解决方案吧...
  17. vuepress-theme-reco + Github Actions 构建静态博客,部署到第三方服务器
  18. ESP32S2(12K)-DS18B20数码管显示温度
  19. 配置微信公众号业务域名,解决“非微信官方网页,请确认是否继续访问”问题
  20. 台式计算机用什么网卡,台式机怎样安装网卡驱动,教您电脑安装网卡驱动

热门文章

  1. 孵化器行业拓客的10个经典方法
  2. 浙江浙大中控技术前端面经
  3. 仙剑缘_仙剑缘手游最新单机版客户端下载-仙剑缘单机版v1.3.2 安卓版-腾牛安卓网...
  4. 视频转音频mp3软件
  5. android intent打开各种文件的方法
  6. ‘芯’系物联网,STM32物联网方案一览
  7. django模型ORM笔记
  8. 墨西哥奢华烈酒品牌Clase Azul品牌标识换新;麦当劳中国第十二次荣膺“中国杰出雇主”认证 | 知消...
  9. 尚硅谷Java入门视频教程第十七章——Java9Java10Java11新特性
  10. Java 人工智能编程