OpenGL纹理API简介

纹素和纹理坐标

纹理对象通常是通过纹理图片读取到的,这个数据保存到一个二维数组中,这个数组中的元素成为纹素(texel),纹素包含颜色值和alpha值。纹理对象的大小的宽度和高度应该为2的整数幂,例如16,32,64。要想获取纹理对象中的纹素,需要使用纹理坐标(texture coordinate)指定。

纹理坐标应该与纹理对象大小无关,这样指定的纹理坐标当纹理对象大小变更时,依然能够工作,比如从256x256大小的纹理,换到512x256时,纹理坐标依然能够工作。因此纹理坐标使用规范化的值,大小范围为[0,1],纹理坐标使用uv表示,如下图所示:


纹理坐标.png

通过指定纹理坐标,可以映射到纹素。例如一个256x256大小的二维纹理,坐标(0.5,1.0)对应的纹素即是(128,256)。

纹理映射时只需要为物体的顶点指定纹理坐标即可,其余部分由片元着色器插值完成,如下图所示:


纹理映射.png

纹素到像素转换问题

  • 一个纹素最终对应屏幕上的多个像素称为放大(magnification)

  • 一个纹素对应屏幕上的一个像素 这种情况不需要滤波方法

  • 一个纹素对应少于一个像素,或者说多个纹素对应屏幕上的一个像素称为缩小(minification)

    放大缩小示意图.png

Mipmaps

当物体在场景中离观察者很远,最终只用一个屏幕像素来显示时,这个像素该如何通过纹素确定呢?如果使用最近邻滤波来获取这个纹素,那么显示效果并不理想。需要使用纹素的均值来反映物体在场景中离我们很远这个效果,对于一个 256×256的纹理,计算平均值是一个耗时工作,不能实时计算,因此可以通过提前计算一组这样的纹理用来满足这种需求。这组提前计算的按比例缩小的纹理就是Mipmaps。Mipmaps纹理大小每级是前一等级的一半,按大小递减顺序排列为:

  • 原始纹理 256×256
  • Mip 1 = 128×128
  • Mip 2 = 64×64
  • Mip 3 = 32×32
  • Mip 4 = 16×16
  • Mip 5 = 8×8
  • Mip 6 = 4×4
  • Mip 7 = 2×2
  • Mip 8 = 1×1

OpenGL中通过函数glGenerateMipmap(GL_TEXTURE_2D);来生成Mipmap,前提是已经指定了原始纹理。
如果直接在不同等级的MipMap之间切换,会形成明显的边缘,因此对于Mipmap也可以同纹素一样使用滤波方法在不同等级的Mipmap之间滤波。要在不同等级的MipMap之间滤波,需要将之前设置的GL_TEXTURE_MIN_FILTER选项更改为以下选项之一:

  • GL_NEAREST_MIPMAP_NEAREST: 使用最接近像素大小的Mipmap,纹理内部使用最近邻滤波。
  • GL_LINEAR_MIPMAP_NEAREST: 使用最接近像素大小的Mipmap,纹理内部使用线性滤波。
  • GL_NEAREST_MIPMAP_LINEAR: 在两个最接近像素大小的Mipmap中做线性插值,纹理内部使用最近邻滤波。
  • GL_LINEAR_MIPMAP_LINEAR: 在两个最接近像素大小的Mipmap中做线性插值,纹理内部使用线性滤波。

使用使用glGenerateMipmap(GL_TEXTURE_2D)产生Mipmap的前提是你已经加载了原始的纹理对象。使用MipMap时设置GL_TEXTURE_MIN_FILTER选项才能起作用,设置GL_TEXTURE_MAG_FILTER的Mipmap选项将会导致无效操作,OpenGL错误码为GL_INVALID_ENUM。

片元着色器中使用纹理对象

在顶点着色器中我们传递了纹理坐标,有了纹理坐标,获取最终的纹素使用是在片元着色器中完成的。由于纹理对象通过使用uniform变量来向片元着色器传递,实际上这里传递的是对应纹理单元(texture unit)的索引号。纹理单元、纹理对象对应关系如下图所示:


纹理单元和纹理对象对应关系图.png

着色器通过纹理单元的索引号索引纹理单元,每个纹理单元可以绑定多个纹理到不同的目标(1D,2D)。OpenGL可以支持的纹理单元数目,一般至少有16个,依次为GL_TEXTURE0 到GL_TEXTURE15,纹理单元最大支持数目可以通过查询GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS常量获取。这些常量值是按照顺序定义的,因此可以采用 GL_TEXTURE0 + i 的形式书写常量,其中整数i在[0, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)范围内。

纹理对象不仅包含纹理数据,还包含采样参数,这些采样参数称之为采样状态(sampling state)。而采样对象(sampler object)就是只包含采样参数的对象,将它绑定到纹理单元时,它会覆盖纹理对象中的采样状态,从而重新配置采样方式。

要使用纹理必须在使用之前激活对应的纹理单元,默认状态下0号纹理单元是激活的,因此即使没有显式地激活也能工作。激活并使用纹理的代码如下:

        ```
        glActiveTexture(GL_TEXTURE0)
        ```

纹理API

  • 读取文件
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);

format: 像素格式
type: 解释参数pixels指向的数据,告诉OpenGL使用缓存区中的什么数据类型来存储颜色分量。像素数据的数据类型
pixels: 图形数据指针
像素格式.png
像素数据的数据格式.png
GLbyte *gltReadTGABits(const char *szFileName, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat);

szFileName: 纹理文件名
iWidth: 宽度地址
iHeight: 高度地址
iComponents: 组件地址
eFormat: 格式地址
  • 载入纹理
void glTextImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, void *data)

target: `GLTETURE_1D`  `GL_TEXTURE_2D`  `GL_TEXTURE_3D`
level:  指定所加载的mip贴图层次,一般设置为0
internalformat: 每个纹理单元中存储多少颜色成分
format: 像素格式(如: GL_RGBA)
type: 像素数据的数据类型(GL_UNSIGNED_BYTE, 每个颜色分量都是一个8位无符号整数)
data: 指向纹理图像数据的指针
  • 纹理对象

    • 分配纹理对象

      glGenTextures(GLsizi n, GLuint *textures);
      
    • 绑定纹理状态

      glBindTexture(GLenum target, GLuint texture);
      
    • 删除绑定纹理

      void glDelteTextures(GLsizei n, GLuint *textures);
      
    • 测试纹理对象是否有效

      GLboolean glIsTexture(GLuint texture);
      
  • 设置纹理的相关参数

glTexParameteri(GLenum target, GLenum pname, GLint param);
  • 过滤方式

    • 邻近过滤(GL_NEAREST)

       // 纹理缩小时,使用邻近过滤
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      
    • 线性过滤(GL_LINEAR)

       // 纹理放大时,使用线性过滤
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      
  • 环绕方式

          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
  • 纹理隧道Demo

参考:
https://blog.csdn.net/wangdingqiaoit/article/details/51457675

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容