纹理贴图是在栅格化的模型表面上覆盖图像的技术。它是为渲染场景添加真实感的最基本和最重要的方法之一。硬件也为纹理贴图提供了硬件支持,使得它具备实现实时的照片级真实感的超高性能。纹理单元是专为纹理设计的硬件组件,现代显卡通常带有数个纹理单元。

为了在OpenGL/GLSL中有效完成纹理贴图,需要协调好以下几个不同的数据集和机制:

  1. 用于保存纹理图像的纹理对象

  1. 特殊的统一采样器变量,以便顶点着色器访问纹理

  1. 用于保存顶点纹理坐标的缓冲区

  1. 用于将顶点纹理坐标传递给管线的顶点属性

  1. 显卡上的纹理单元

一、如何将纹理图像加载到OpenGL

纹理图像可以是任何图像。在电子游戏和动画电影中,纹理图像通常用于为角色或者生物绘制皮肤和衣服。图像通常存在如.jpg、.png、.gif、.tiff等图像文件中。为了使纹理图像应用于OpenGL管线中的着色器,我们需要从图像中提取颜色并将它们放入OpenGL纹理对象(用于保存纹理图像的内置OpenGL结构)中。

通常将纹理加载到OpenGL应用程序的步骤是:

setp1:创建纹理对象

许多C++库都可以读取和处理图像文件,比如SOIL2。使用SOIL2实例化OpenGL纹理对象并从图像文件中读入数据操作如下。

/*创建一个纹理对象,纹理对象由整型ID标识,因此首先要声明一个GLunit类型的变量来保存它。这里需要注意OpenGL纹理对象所采用的方向左下角的坐标为(0,0),右上角的坐标为(1,1),
这与存储在许多标准图像文件格式中的图像的方向不同,在那些图像中原点位于左上角;此时可以
通过指定SOIL_FLAG_INVERT_Y参数垂直翻转图像来重新定向使其与OpenGL的预期格式相对应)*/
GLunit textureID = SOIL_load_OGL_texture("image.jpg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y)

当然也可以不使用第三方库,一般步骤为:

先使用C++工具读取图像文件数据(比如fopen和fread函数将数据从.bmp图像文件读入到unsigned char类型的数组,比如这里为数组data);

接着生成OpenGL纹理对象,可以使用如下OpenGL命令来创建一个或多个纹理对象。

//创建一个纹理对象。如果要创建多个,则使用GLuint类型的数组。
GLuint textureID;
glGenTextures(1, &textureID);

最后将图像文件数据复制到创建的纹理对象中,可以使用如下命令来完成:

glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0 , GL_BGR, GL_UNSIGNED_BYTE, data)

setp2:调用如下函数以使新创建的纹理对象处于激活状态。

这里需要注意显卡的纹理单元(和相关的采样器),可以对任何我们希望的纹理对象进行采样,也可以在允许时更改。在displasy()时需要指定纹理单元要为当前帧采样的纹理对象。因此在每次绘制对象时都要激活纹理单元并将其绑定到特定纹理对象。例如:

/*指定GL_TEXTURE0,使得第0个纹理单元处于激活状态(纹理单元的数量取决于显卡提供的数量)*/
glActiveTexture(GL_TEXTURE0);
/*其第2个参数就是如上的textureID*/
glBindTexture(GL_TEXTURE_2D, textureID);

setp3:调整纹理设置,比如:

多级渐远纹理贴图(mipmapping)

当纹理图像的分辨率小于所绘制区域的分辨率时,会出现一种很常见的伪影。相反图像纹理的的分辨率大于被绘制区域的分辨率的话可能会出现叠影(叠影是由采样误差引起的),使用多级渐远纹理贴图技术可以很大程度来校正这一采用误差。OpenGL提供了丰富的多级渐远纹理支持。其中一些机制可以让OpenGL自动构建(如下代码用于通知OpenGL生成多级渐远纹理),大多数情况下,OpenGL自动构建多级渐远纹理已足够。

...
//1)如果使用多级渐远纹理贴图则执行如下几行代码
/*其第2个参数就是如上的textureID*/
glBindTexture(GL_TEXTURE_2D, textureID);
/*实际给对象添加纹理时,可以通过多种方法对多级渐远进行采样。在OpenGL中可以通过将
GL_TEXTURE_MIN_FILTER参数设置为所需的缩小方法来选择多级渐远的采用方法,而
GL_LINEAR_MIPMAP_LINEAR便是可选取的方法之一,它表示选择具有与纹理区域最相似的分辨率的2个
多级渐远纹理。然后,它取各自最接近纹理坐标的4个texel,并计算插值,即三线性过滤。*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
//2)如果还使用各向异常型
......

另一些机制可用于构建属于自己的多级渐远纹理级别。对于特殊的场景,可以使用任何一款图像编辑软件自行构建多级渐远纹理,然后通过为每个多级渐远纹理级别重复调用OpenGL的glTexImage2D()函数来创建纹理对象,并将它们添加为多级渐远纹理级别。

2)各向异性过滤(Anisotropic Filtering, AF)

多级渐远纹理贴图有时候看起来比非多级渐远纹理贴图更加模糊,尤其是当被贴图对象以严重倾斜的视角渲染时,可见使用多级渐远纹理贴图在减少伪影的同时也损失了图像细节(这种细节的丢失是因为当物体倾斜时,其图元看起来在一个轴(即沿宽或高)上的尺寸比在另一个轴上更小)。

一种恢复一些丢失细节的方法是使用各向异性过滤技术;标准的多级渐远纹理贴图以各种正方形分辨率(如256像素*256像素)对纹理图像进行采样,而各向异性过滤却以多种矩形分辨率对纹理进行采样。这使得从各种角度观看的纹理都保留尽可能多的细节。

大多数显卡都支持各向异性过滤(成为OpenGL扩展),OpenGL也提供了查询和访问各向异性的方法,只是需要在多级渐远纹理贴图处理后立即添加代码:

...
//1)如果使用多级渐远纹理贴图
......
//2)如果还使用各向异常型则接着执行如下代码
/*glewIsSupported用来测试显卡是否支持各向异性过滤*/
if (glewIsSupported("GL_EXT_texture_min_filter_anisotropic")) {GLfloat anisoSetting = 0.0f;/*如果支持各向异性过滤,将其设置为支持的最大采样程度,这个值通过glGetFloatv获取。并调用glTexParameterf将其应用于激活纹理对象。*/glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoSetting);glTexParameterf(GL_TEXTURE_2D, GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, anisoSetting);
}

二、如何构建顶点的纹理坐标并将其载入到缓冲区

1)构建顶点的纹理坐标

有了上述的将纹理图像加载的OpenGL的方法,还需要指定希望如何将纹理应用于对象的渲染表面。可以通过为模型中的每个顶点指定顶点纹理坐标来完成此操作。

顶点纹理坐标是对纹理图像中像素的引用,用于将3D模型上的点映射到纹理中的位置。纹理图像中的像素被称为texel(纹元),以便将它们与在屏幕上呈现的像素区分开。模型表面上的每个点除了将它定位在3D空间中的坐标(x,y,z)外,还具有顶点纹理坐标,比如最为常见的2D纹理坐标(s,t)用来指定纹理图像中的哪个texel为它提供颜色;这样物体的表面被按照纹理图像"涂画"。

要使用纹理贴图,必须为要添加纹理的对象中的每个顶点提供顶点纹理坐标。OpenGL将使用这些顶点纹理坐标,查找存储在纹理图像中的引用的texel的颜色,来确定模型中每个栅格化像素的颜色。为了确保渲染模型中的每个像素都使用纹理图像中的适当texel进行绘制,顶点纹理坐标也需要放入顶点属性中,以便由光栅着色器进行插值。以这种方式,纹理图像与模型顶点一起被插值或者填充。

对于通过顶点着色器的每组顶点空间坐标(x,y,z),会有一组相应的顶点纹理坐标(s,t),因此需要设置二个缓冲区,一个用于顶点的空间坐标,另一个用于相应的纹理坐标。这样,每次顶点着色器的调用会接收到一个顶点的数据(包括其空间坐标和相应的纹理坐标)。

在2D纹理坐标中,2D纹理图像被设定为矩形,左下角的位置为(0,0),右上角的坐标位置为(1,1);理想情况下纹理坐标应该在[0,1]区间内取值。

/*比如一个立方体的纹理坐标如下所示。这里需要注意的是我们这里是假定落在[0, 1]之间,但是
但是OpenGL实际上支持任何取值范围的纹理坐标,可以通过glTextParameteri()来设置*/
float textureCoordinates[36] ={ 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f};

这里需要注意对于立方体等这样的简单模型,选择纹理坐标比较容易。但是对于具有大量三角形的更复杂的弯曲模型,手动确定它们的纹理坐标是不切实际的。在弯曲的几何形状(例如球面或环面)的情况下,可以通过算法或数学方式计算纹理坐标。使用Maya或者Blender等建模工具构建模型时可以使用"UV映射功能"使得纹理坐标的任务更容易完成。

2)将顶点的纹理坐标载入缓冲区

和顶点的空间坐标类似,将纹理坐标加载到另一个VBO中。比如将纹理坐标放入vbo1中。其中textureCoordinates保存的是纹理坐标。

 glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoordinates), textureCoordinates, GL_STATIC_DRAW);

三、如何在着色器中使用纹理:采样器变量和纹理单元

为了提高性能,可以在显卡硬件中执行纹理处理。这就意味着片着色器需要一种访问我们在C++/OpenGL应用程序中创建的纹理对象的方法。

可以通过在着色器中声明“统一采样器变量”来指示显卡上的纹理单元,从加载的纹理对象中提取或采样texel,如下所示:

/*其中的layout (binding=0)部分用于指定此采样器与第0个纹理单元关联;samp便是声明的变量*/
layout (binding=0) uniform sampler2D samp;

要实际执行纹理处理,还需要注意片段着色器的输出颜色的方式,需要使用从顶点着色器(通过光栅着色器)接收的插值纹理坐标来对纹理对象进行采样。调用texure函数如下:

in vec2 tc; //纹理坐标
...
color = texture(samp, tc);

1)顶点着色器示例

#version 430layout (location=0) in vec3 pos;
layout (location=1) in vec2 texCoord;
out vec2 tc;  //纹理坐标输出到光栅着色器用于插值uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp;  //顶点,着色器中未使用void main(void)
{    gl_Position = proj_matrix * mv_matrix * vec4(pos,1.0);tc = texCoord;
}

2)片段着色器示例

#version 430in vec2 tc;  //输入插值过的纹理坐标
out vec4 color;uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp;void main(void)
{    color = texture(samp, tc);
}

OpenGL:纹理贴图相关推荐

  1. C++ Opengl纹理贴图源码

    C++ Opengl纹理贴图源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1,三方库 ...

  2. OpenGL 纹理贴图

    纹理贴图允许把一幅砖墙图像映射到一个多边形的表面上,并把正面墙画成单个多边形.纹理贴图能够保证当这个多边形变形或渲染时,映射到多边形表面的图像也能够表现出正确的行为. 纹理贴图是一个相当大的主题,并且 ...

  3. 【OpenGL】二十四、OpenGL 纹理贴图 ( 读取文件内容 | 桌面程序添加控制台窗口 | ‘fopen‘: This function may be unsafe 错误处理 )

    文章目录 一.文件读取 二.报错处理 ( 'fopen': This function or variable may be unsafe. ) 三.桌面程序添加控制台窗口 四.相关资源 一.文件读取 ...

  4. Qt5.6.0+OpenGL 纹理贴图首战告捷

    重要的话写在前面~~通过今晚的实验,知道了EBO是不能随便release的~~~一直不要release就可以了,否则vao会失效 Display.h #ifndef DISPLAYWIDGET_H # ...

  5. C++写纹理贴图中OBJ文件和MTL(材质)文件

    在VTK 或者Opengl纹理贴图中都会用到模型文件,当使用vtkOBJImporter函数读取模型数据时,需要对应的MTL文件才能实现纹理贴图,但是网上大多数的OBJ文件和MTL文件都是由模型软件导 ...

  6. 纹理窗口Qt+OpenGL之纹理贴图

    上班之余抽点时间出来写写博文,希望对新接触的朋友有帮助.今天在这里和大家一起学习一下纹理窗口 NeNe的代码中是加载到了一个正方体当中,代码很长.其实单纯的想要纹理贴图是很便利的.具体的纹理贴图技巧在 ...

  7. OpenGL ES教程VI之纹理贴图(原文对照)

    注:又是一篇,是否有人能解释得清楚,2是重复两次,那么是否N就是重复N次呢?接近1.0的坐标值整数部分加上几就是重复几次吗?这个好像之前验证过不一定的. 转自:http://melord.iteye. ...

  8. OpenGL立方体纹理贴图

    OpenGL正方体纹理贴图 0. 写在最前面 1. 正方体顶点属性构建 2. 绑定多个VAO.VBO 3. 创建多个纹理 4. 渲染循环 5. 实现代码 6. 多个立方体纹理贴图 0. 写在最前面 要 ...

  9. opengl生成图片php,(转)使用OpenGL显示图像(七)Android OpenGLES2.0——纹理贴图之显示图片...

    转:http://blog.csdn.net/junzia/article/details/52842816 前面几篇博客,我们将了Android中利用OpenGL ES 2.0绘制各种形体,并在上一 ...

  10. OpenGL总结6-圆柱纹理贴图

    圆柱纹理贴图我采用了OpenGL中的函数gluCylinder,这个函数会生成空心的圆柱,这个圆柱固定生成为底面位于xy面,z=0,顶面从z=0开始向z轴正方向,也就是说圆柱体是冲着我们生成的,如果想 ...

最新文章

  1. 以后国产手机都要装鸿蒙系统吗,如果华为发布鸿蒙手机操作系统,国产软件会能用吗,形成一个生态系统吗?...
  2. 自制纯css下拉导航
  3. 如何批量删除会计科目、供应商、客户的主数据
  4. 初识Nginx服务器
  5. 同软件多个线程设置不同ip_软件测试如何自学?收下这份《2020千锋性能测试入门视频教程》...
  6. 产品运行所需的信息检索失败_为服务业注入新活力,华北工控推出服务机器人专用计算机产品方案...
  7. 【笛卡尔树】【线段树】meetings 会议(P5044)
  8. ajax项目中使用模板
  9. 删除Dataframe前N行或后N行
  10. 数据挖掘-朴素贝叶斯分类
  11. 交互设计实用指南系列(8)—深广度平衡
  12. Nvidia 驱动的安装和卸载(Ubuntu20.04)
  13. 用Acrobat pro DC 自动生成pdf目录——知网硕博论文为例(附带如何下载只有caj的pdf)
  14. 北京计算机研究所面试流程,中科院计算所霸面经历
  15. CentOS7.9安装Nextcloud+ocDownloader+aria2使用Nextcloud网盘做离线下载服务器
  16. 电脑公司GHOST WIN7 装机旗舰版 2013 05
  17. airplay服务器linux,树莓派搭建AirPlay服务实现WiFi无线音响外放
  18. linux图像编辑,Photoflare:Linux下简单的开源图像编辑器
  19. 那些年,在Fragment中犯的错
  20. vue 获取元素宽高

热门文章

  1. fastjson List转JSONArray以及JSONArray转List
  2. css中首字母下沉_CSS首字母大而精美的首字下沉
  3. PS4怪物猎人世界服务器稳定吗,《怪物猎人世界》PS4/PS4 Pro运行对比 游戏帧数不稳定...
  4. 查看svn当前登录的账号(svn.simple)
  5. android手机型号等信息
  6. 【Linux内核编程】 设备管理与文件IO操作
  7. 第一讲——mininet的安装和使用
  8. 参考平面及其高度_设计规范参考数据
  9. git 删除分支相关操作
  10. 巨巨的磁盘(线段树)