纹素和纹理坐标
纹理对象通常是通过纹理图片读取到的,这个数据保存到一个二维数组中,这个数组中的元素成为纹素(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);
参考:
https://blog.csdn.net/wangdingqiaoit/article/details/51457675













网友评论