第六章 纹理
6.1 纹理映射
- 在应用程序中使用纹理映射的步骤:
- 创建一个纹理对象,为它加载纹素数据
- 为顶点增加纹理坐标
- 把纹理图与着色器中将要使用的纹理采样器关联
- 在着色器中使用纹理采样器来查询纹素值
6.2 基本纹理类型
- 每个纹理对象标识组成完整纹理的一组图像
- 每个图像可以是纹素的1维、2维或者3维数组
- 数组纹理–纹理包含1维或者2维切片的数组,数组的每个元素是一个切片
- 纹理可以使用用于2维和2维数组纹理的多重采样的纹理类型来表示多重采样表面
- 多重采样是一个用于抗锯齿的术语,其中每个纹素(或者像素)被赋予多个独立的颜色,然后再渲染过程中合并这些颜色来生成最终的输出颜色;多重采样纹理对于每个纹素都有几个采样
- 纹理通过纹理单元绑定到OpenGL环境,其中纹理单元用命名为GL_TEXTURE0~i的绑定点来表示
- 只要有一个纹理被绑定到OpenGL环境,则在着色器中可以使用采样器变量来访问,采样器变量用匹配纹理的维数来声明
- 纹理维数(称为纹理目标)和对应的采样器类型如下:
目标 GL_TEXTURE* | 采样器类型 | 维度 |
---|---|---|
1D | sampler1D | 1D |
1D_ARRAY | sampler1DArray | 1D数组 |
2D | sampler2D | 2D |
2D_ARRAY | sampler2DArray | 2D数组 |
2D_MULTISAMPLE | sampler2DMS | 2D多重采样 |
2D_MULTISAMPLE_ARRAY | sampler2DMSArray | 2D多重采样数组 |
3D | sampler3D | 3D |
CUBE | samplerCube | 立方体映射纹理 |
ARRAY | samplerCubeArray | 立方体映射数组 |
RECTANGLE | samplerRect | 2D矩形 |
BUFFER | samplerBuffer | 1D缓存 |
- 矩形纹理对象(GL_TEXTURE_RECTANGLE)是2维纹理的特例,表示一个简单的纹素的矩形,它没有mipmap,并且不能表示纹理数组;一些纹理模式(texture wrapping)不支持矩形纹理
- 缓冲纹理(GL_TEXTURE_BUFFER)表示任意纹素的1维数组;没有mipmap,不能聚合数组;缓冲纹理的存储实际是用一个缓冲对象来表示,因此它的上边界大小比普通的1维纹理大的多;缓冲纹理可以使任意着色器阶段的数据访问而不用把它拷贝进纹理图像
6.3 创建和初始化纹理
OpenGL中使用纹理的第一步:为纹理对象保留名称并把它们绑定到环境的纹理单元
void glGenTextures(GLsizei n, GLuint* textures)
在数组textures中返回n个用于纹理对象的当前没有使用的名称
标记textures中的名称为被使用,它们只有在第一次绑定的时候,才能获取到纹理状态和维数(比如1D、2D或者3D)
0是保留的纹理名,从来不会被glGenTextures()返回void glBindTexture(GLenum target, GLuint texture) 做三件事:
- 当第一次使用无符号整数值texture且不是0时,创建一个新的纹理对象并赋给那个名称
- 当绑定先前创建的纹理对象时,将激活这个纹理对象
- 当绑定texture值为0时,OpenGL将删除与激活的纹理单元特定目标关联的所有的绑定,不留下任何绑定
target的取值如上表,其中只有立方体纹理有些不同:GL_TEXTURE_CUBE_MAP、GL_TEXTURE_CUBE_MAP_ARRAY
OpenGL环境支持多个纹理单元,调用glBindTexture()将纹理对象绑定到激活的纹理单元,维数用target设置;激活的纹理单元可以用glActiveTexture()函数来改变;
一个纹理可以同时绑定到多个纹理单元,这使相同的纹理数据通过表示绑定纹理的纹理单元的不同采样器来读取
void glActiveTexture(GLenum texture) 设置当前激活的纹理单元
GLboolean glIsTexture(GLuint texture) 如果texture是已经被绑定的纹理的名称并且还没有被删除,返回GL_TRUE;如果texture是0或者不存在的纹理名称返回GL_FALSE
void glDeleteTexture(GLsizei n, const GLuint* textures) 删除n个使用数组textures的元素来命名的纹理对象
创建纹理对象后,必须为它们设置存储和最后的数据;纹理对象的每1维都有相关的决定纹理边界的存储函数,它们是glTexStorage1D()、glTexStorage2D()和glTexStorage3D(),分别维1维、2维和3维纹理定义存储;对于数组纹理,用下一个更高维来设置数组的大小
void glTexStorage1D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width) target必须是GL_TEXTURE_1D
void glTexStorage2D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) 如果target是GL_TEXTURE_2D,则为2维纹理设置存储,width和height设置纹理的大小;如果是GL_TEXTURE_1D_ARRAY,则为1维纹理数组设置存储,width设置纹理的范围,height设置数组中切片的数目
void glTexStorage3D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth) 如果target是GL_TEXTURE_3D,则为3维纹理设置存储,width、height和depth为像素数组设置大小;如果是GL_TEXTURE_2D_ARRAY,则为2维数组纹理设置存储,width和height为每个切片设置大小,depth设置数组中切片的数目;
上边三个函数为纹理创建固定的存储;纹理存储的属性包括可以选择的内部格式、纹理的所有mipmap级像素所需的内存量;这只是存储的属性,只要指定的纹理为不可改变,那么存储不能改变;纹理的内容可以使用glTexSubImage2D()来改变
上边三个函数只允许为单采样纹理创建存储;如果采用的是多重采样纹理,则要使用glTexStorage2DMultisample()或者glTexStorage3DMultisample()为纹理创建存储
void glTexStorage2DMultisample(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)
为当前绑定到target的2维多重采样纹理对象设置纹理存储;target必须是GL_TEXTURE_2D_MULTISAMPLE;width和height设置纹理的大小;samples设置纹理采样的数目;如果fixedsamplelocation是GL_TRUE,那么OpenGL为纹理中每个纹素的相同样本使用相同的子纹素位置;如果是GL_FALSE,那么OpenGL为每个纹素给定的样本选择空间变化的位置void glTexStroage3DMultisample(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)
为2维多重采样数组纹理设置存储void glTexImage1D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei border, GLenum format, GLenum type, const void* data)
void glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei border, GLenum format, GLenum type, const void* data)
void glTexImage3D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei border, GLenum format, GLenum type, const void* data)
用来设置可变的存储;可以分别为1D、2D、或者3D纹理的一个mipmap级选择性的提供初始图像数据;也可为1D或者2D数组纹理的一个mipmap级设置存储和图像数据;
参数width、height和depth设置纹素中纹素数组的宽度、高度和深度;对于数组纹理,height设置1D数组纹理中切片数目,depth设置2D数组纹理中切片数目
internamFormat设置OpenGL用来存储纹理中纹素使用的格式
data设置内存中初始纹素数据的位置;如果将一个缓冲绑定到GL_PIXEL_UNPACK_BUFFER绑定点,则从这个缓冲对象读出纹素数据,data被解释为从缓冲对象读取数据的偏移;如果没有缓存绑定到GL_PIXEL_UNPACK_BUFFER,那么data被解释为应用程序内存中的数据指针,如果它为空指针,这种情况下纹理的初始内容是未定义的
- void glTexImage2DMultisample(GLenum target, GLenum samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)
- void glTexImage3DMultisample(GLenum target, GLenum samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)
分别2维和3维数组多重采样纹理设置存储;target分别是GL_TEXTURE_2D_MULTISAMPLE、GL_TEXTURE_2D_MULTISAMPLE_ARRAY
- 不能为多重采样纹理设置初始数据,把数据放进多重采样纹理的唯一办法是把它与帧缓存对象关联并渲染它;多重采样纹理没有mipmap
6.3.1 纹理格式
- 上一节提到的函数参数internalformat,决定了OpenGL用来存储内部纹理数据的格式,它也表示应用程序提供的数据格式和类型的format和type参数
内部格式
- 纹理的内部格式(internal format)是OpenGL用来内部存储开发者给的纹理数据的格式;如果需要,开发者的数据将在图像设置时转换成这种格式
- 完整的内部格式由基本格式的标识符、一个或多个的大小标识符和可选的数据类型组成;基本格式决定了提供的纹理分量;大小标识符决定了用来存储纹理数据的位数;类型标识符用内部格式名表示
- OpenGL默认以无符号归一化格式存储纹理;当数据以无符号归一化格式存储时,纹素的值在内存中以整数存储,整数在读进着色器时转化为浮点数,并且用整数对应的最大值来除,最后生成[0.0, 1.0]的数据传给着色器。如果提供的是_SNORM修改符(例如GL_RGBA8_SNORM),数据是有符号的归一化;此时在内存中的数据是有符号整数,并且在它返回给着色器前,转换为浮点数,并被对应的最大有符号整数除,生成范围在[-1.0, 1.0]的浮点数值并传给着色器
- 类型标识符是I、UI和F,分别表示有符号整数、无符号整数和浮点数据。有符号和无符号整数内部格式被设计与着色器中的有符号或无符号整数采样器类型一起使用(例如isampler2D或者usampler2D)。浮点内部格式是真浮点格式,数据以浮点表示在内存中存储并且使用OpenGL支持的全精度返回给着色器。在这种情况下,纹素可以表示范围在-1.0~1.0范围之外的数据
外部格式
- 外部格式(external format)通过OpenGL API提供数据的格式,用函数的format和type参数来表示
- format由表示通道的一个部分和可选的INTEGER格式指示符组成
- 外部格式如果有_INTEGER后缀,那么传给OpenGL的值被看做没有归一化的整数数据并且使用逐字(verbatim)
- 如果纹理的内部格式是浮点格式,数据将直接转换为浮点,而与传入的数据类型无关
- 如果想在着色器中接受整数,应该使用整数采样器类型、整数内部格式和整数外部格式和类型
- 参数format和参数type一起用来描述内存中的纹理数据
- type的取值:
type | 表示 | 对应类型 |
---|---|---|
GL_BYTE | 有符号byte | GLbyte |
GL_UNSIGNED_BYTE | 无符号byte | GLubyte |
GL_SHORT | 有符号短整数 | GLshort |
GL_UNSIGNED_SHORT | 无符号短整数 | GLushort |
GL_INT | 有符号整数 | GLint |
GL_UNSIGNED_ INT | 无符号整数 | GLuint |
GL_UNALF_FLOAT | 半精度浮点数 | GLhalf |
GL_FLOAT | 全精度浮点数 | GLfloat |
GL_DOUBLE | 双精度浮点数 | GLdouble |
- 除了表示原始类型的标识符,也有几个特殊的标识符用来设置压缩或者混合类型格式;它们可以把数据打包为更大的本地类型,并且直接按顺序排列,不考虑原始类型或者整型的数据边界问题:
6.4 代理纹理
- 每个标准的纹理对象有一个对应的代理纹理对象
- 代理纹理对象目标可以用来测试在组合中使用一些限制时OpenGL实现的性能
6.5 设置纹理数据
6.5.1 显示设置纹理数据
- 纹理数据是format和type组成的数组数据,可以用glTexSubImage1D()、glTexSubImage2D()或者glTexSubImage3D()函数把纹理数据加载进纹理对象
- void glTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* data)
- void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* data)
- void glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* data)
用data中设置的新数据替换纹理的一个区域;level是mipmap级数;format和type描述指针data指向的纹理图像数据的格式和类型
data包含子图像的纹理数据;width、height和depth是将要替换当前纹理图像的全部或者部分区域的大小
xoffset、yoffset和zoffset分别表示x、y、z方向上的纹素偏移
target表示纹理对象已经绑定的纹理目标
6.5.2 使用Pixel Unpack缓存
- 函数glTexSubImage2D()的参数data可以用两种方法之一来解释;第一个是应用程序内存中存储的数据指针;第二个是缓存对象绑定到GL_PIXEL_UNPACK_BUFFER
- 使用GL_PIXEL_UNPACK_BUFFER缓存的优点是应用程序可以自由修改它传给函数的数据,并且可以使传送数据与应用程序运行并行发生
6.5.3 从帧缓存拷贝数据
- 我们可以将帧缓存的一部分读进纹理对象,并用在随后的渲染中;为了实现这个目标,可以使用glCopyTexImage1D()或者glCopyTexImage2D()函数
- void glCopyTexImage1D(GLenum target, GLint level, GLint internalFormat, GLint x, GLint y, GLsizei width, GLint border)
- void glCopyTexImage2D(GLenum target, GLint level, GLint internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
从当前帧缓存中拷贝绑定到激活的纹理单元target的纹理的像素;x和y设置从帧缓存中拷贝源区域的水平和垂直偏移;width和height分别设置为区域的高度和宽度;internalFormat设置结果纹素存储使用的格式;border是保留值,必须是零
- glCopyTexImage()函数的本质是从帧缓存为纹理对象加载数据;与它相比,函数glTexImage2D()则是从参数data中获取数据加载进纹理
- void glCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width)
- void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
- void glCopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
使用来自帧缓存的图像数据替换全部或者部分当前存在的,再绑定到激活的纹理单元的target纹理对象的纹理图像连续的子区域
x和y设置帧缓存中要拷贝的区域的x、y偏移
width和height分别设置要拷贝的区域高度和宽度
xoffset、yoffset和zoffset设置目标纹理中目标区域的原点
6.5.4 从文件加载图像
- 在大多数应用中,把数据存储在格式化的图像文件中,如JPEG、PNG、GIF或者其他图像格式的类型;而OpenGL只能使用原始的像素或者用特殊算法压缩的纹理;因此,应用程序需要将图像文件解码进内存,以致OpenGL可以读取并初始化内部纹理
- 如果将纹理分配为不可改变的对象(如使用了glTexStorage2D()),则要使用纹理子图像命令(如glTexSubImage2D())来设置图像数据
- 不可能对多重采样纹理直接设置图像数据,把数据放进多重采样纹理的唯一办法是把它关联到帧缓存对象并渲染它
6.5.5 查询纹理数据
- void glGetTexImage(GLenum target, GLint lod, GLenum format, GLenum type, GLvoid* image)
从纹理把图像数据读回应用程序内存或者都回缓存对象
从绑定到target的纹理中获取纹理图像;target必须是:
- GL_TEXTURE_1D
- GL_TEXTURE_2D
- GL_TEXTURE_3D
- GL_TEXTURE_1D_ARRAY
- GL_TEXTURE_2D_ARRAY
- GL_TEXTURE_CUBE_MAP_ARRAY
- GL_TEXTURE_RECTANGLE
- GL_TEXTURE_CUBE_MAP_POSITIVE_X(YZ)、GL_TEXTURE_CUBE_MAP_NEGTIVE_X(YZ)可以用来指示一个cubemap映射纹理的对应面
lod层次细节数
format和type是需要的数据像素格式和类型
image可以解释为图像数据将存放在内存中的地址;如果将一个缓存绑定到GL_PIXEL_PACK_BUFFER目标,那么image解释为图像数据存放在缓存中的偏移
- 使用上边函数时必须特别注意:写进image的字节数由当前绑定到target的纹理的维数、格式和类型来决定,有可能返回大量数据,但在用户提供的内存区域上OpenGL不执行边界检查;因此,不正确使用这个函数可能导致缓存溢出,以及不可知的结果发生
6.5.6 纹理数据布局
- OpenGL提供了几个控制命令,可以让开发人员在应用程序中描述数据如何布局
- void glPixelStorei(GLenum pname, GLint param)
- void glPixelStoref(GLenum pname, GLfloat param)
像素存储参数pname设置为param设置的值
6.6 采样器对象
- 通过关联一个采样器变量和一个纹理单元,并且使用GLSL的内置函数从纹理图像中提取纹素,纹理可以被着色器来读取
- 提取纹素的方法取决于一些包含在另一个称为采样器对象的参数
- 类似于将纹理对象绑定到纹理单元,将采样器对象绑定到采样器单元;如果没有将采样器对象绑定到对应的采样器单元,则可以认为纹理对象包括一个用内置的默认值来读取数据的采样器对象
- void glGenSamplers(GLsizei count, GLuint* samplers) 返回count个目前没有使用的采样器对象的名称,存储在数组samplers中;samplers中的名称标为被使用,但是当它们第一次被绑定的时候,需要采样器状态;0是保留值,永远不会被glGenSamplers()返回
- void glBindSampler(GLuint uint, GLuint sampler) 将名称为sampler的采样器对象绑定到下标用uint给定的采样器单元;如果sampler是零,任何当前绑定到采样器单元的采样器对象被解除绑定,并且没有对象绑定在它的位置
- GLboolean glIsSampler(GLenum id) 如果id是已经存在的采样器对象的名称,则返回GL_TRUE
- 注意:glBindSampler()和glBindTexture()之间的差别:
- 采样器没有target参数;由于采样器对象没有内置的维数,所以没有理由在多个采样器对象类型之间区分
- 绑定采样器时用了uint参数,所以不需要选择“激活的采样器单元”,也就没有glActiveSampler()函数
- OpenGL实现支持任意数目的采样器单元而不用保留OpenGL标志
6.6.1 采样器参数
- 每个采样器对象表示一些控制纹素从纹理对象中读取方式的参数
- 采样器对象的参数使用glSamplerParameteri()和glSamplerParameterf()函数来设置,对应整数和浮点数;以及glSamplerParameteriv()和glSamplerParameterfv()函数,对应整数和浮点参数的向量
- void glSamplerParameter{fi}(GLuint sampler, GLenum pname, Type param)
- void glSamplerParameter(fi}v(GLuint sampler, GLenum pname, const Type* param)
- void glSamplerParameter{I,ui}v(GLuint sampler, GLenum pname, const Type* param)
设置名称用sampler指定的采样器对象的pname给出的参数值为param给出的值
对于glSamplerParameteri(),param是一个整数
对于glSamplerParameterf(),param是一个浮点数
对于glSamplerParameteriv(),param是整数数组的地址
对于glSamplerParameterfv(),param是浮点数组的地址
- 当没有采样器对象绑定到采样单元时,每个用来从纹理中读取数据的纹理对象都有默认的采样器对象
- void glTexParameter{fi}(GLenum target, GLenum pname, Type param)
- void glTexParameter{fi}v(GLenum target, GLenum pname, const Type* param)
- void glTexParameterI{i,ui}v(GLenum target, GLenum pname, const Type* param)
为当前绑定到激活纹理单元的target纹理对象设置参数pname为param给定的值
如果pname表示采样器对象的参数之一,那么访问纹理内部默认采样器对象
- void glDeleteSamplers(GLsizei count, const GLuint* samplers) 删除count个存储在数组samplers中的采样器名称
6.7 使用纹理
- 创建和初始化纹理对象并把图像数据加载进纹理对象后,在应用程序中可以使用着色器来从纹理对象中读取数据
- 着色器中的纹理用采样器变量表示
- 每个采样器变量是纹理对象表示的一组图像数据和采样器对象(或者纹理自己的,内部采样器对象)表示的采样参数的组合
- 将纹理对象绑定到纹理单元,采样器对象绑定到对应的采样器单元,它们一起被用来从纹理图像读取数据,这个过程称为采样,使用GLSL内置的texture()函数或者它的其他变种之一来执行
- Gvec4 texture(gsampler1D tex, float P[, float bias])
- Gvec4 texture(gsampler2D tex, vec2 P[, float bias])
- Gvec4 texture(gsampler3D tex, vec3 P[, float bias])
- Gvec4 texture(gsamplerCube tex, vec3 P[, float bias])
- Gvec4 texture(gsampler1DArray tex, vec2 P[, float bias])
- Gvec4 texture(gsampler2DArray tex, vec3 P[, float bias])
- Gvec4 texture(gsampler2DRect tex, vec2 P)
- Gvec4 texture(gsamplerCubeArray tex, vec4 P[, float bias])
tex表示的采样器以P表示的纹理坐标来采样纹素
如果启用mipmapping并且提供bias,它用来偏移确定采样mipmap的细节层次计算
返回值是采样的纹理数据的向量
传递给该函数的参数必须是dynamically uniform
- 在许多GLSL函数原型中,可以看到gvec4,它表示任意类型的vec4向量,g是一个表示任意类型的占位符
6.7.1 纹理坐标
- 纹理坐标是采样图像的纹理内的坐标;它经常是逐顶点提供纹理坐标,然后在结果几何体的表面上插值来提供逐片元坐标;在片元着色器中使用这个坐标从纹理读取和获取颜色用于片元结果
- 纹理占有沿每一个轴范围为0.0~1.0的域;应用程序负责提供纹理坐标,以顶点输入的形式传递给顶点着色器,然后在传给片元着色器之前OpenGL会在每个多边形的面上进行插值;
- 如果传递的纹理坐标在0.01.0的范围外,必须将它们修改这个范围;OpenGL中有几个方法实现这一点,用采样器参数GL_TEXTURE_WRAP_S、GL_TEXTURE_WARP_T和GL_TEXTURE_WRAP_R来控制;这三个参数控制的范围在0.01.0之外的纹理坐标被OpenGL分别处理为纹理域的S、T和R坐标轴;每一维的边界截取模式可以设置为GL_CLAMP_TO_DEGE、GL_CLAMP_TO_BORDER、GL_REPEAT或者GL_MIRRORED_REPEAT;这四种模式的意义如下:
- 如果模式是GL_CLAMP_TO_EDGE,当纹理坐标超出范围0.1~1.0,纹理边缘的纹素用来形成返回到着色器的值
- 如果模式是GL_CLAMP_TO_BORDER,读取纹理范围之外的坐标将生成用来形成最终值的纹理常数边界颜色;意思是该默模式返回的纹素来自纹理的虚拟边界,它是一个颜色常量;这个颜色默认是黑色,可以通过设置GL_TEXTURE_BORDER_COLOR采样器参数的值来改变这个颜色
- 如果clamping模式是GL_REPEAT,包括纹理,认为它无限重复;其实,只有纹理坐标的小数部分用来查询纹素,而整数部分被丢弃
- 模式GL_MIRRORED_REPEAT是一个特殊的模式,纹理可以以镜像的方式来重复;整数部分是偶数的纹理坐标只考虑小数部分;整数部分是奇数的纹理坐标是1.0减去小数部分来形成最终坐标
6.7.2 组织纹理数据
- 纹理swizzle是一组纹理参数,用于每个纹理的通道,可以通过传递纹理swizzle参数名和需要的数据源,使用函数glTexParameterri()来设置
- 纹理swizzle参数是GL_TEXTURE_SWIZZLE_R(GBA);它们对应的数据源可以是:GL_RED、GL_GREEN、GL_BLUE、GL_ALPHA、GL_ONE或者GL_ZERO
6.7.3 使用多重纹理
- 为了在着色器中使用多重纹理,需要声明多个uniform采样器变量;每个指向一个不同的纹理单元;在应用程序部分,uniform采样器看起来向uniform整数;使用标准的glGetActiveUniform()函数来遍历,也可以使用函数glUniform1i()来改变它的值;分配给uniform采样器的整数值是它指向纹理单元的索引
- 使用多重纹理的步骤:
- 使用glActiveTexture()来选择第一个纹理单元
- 然后使用glBindTexture()来绑定纹理
- 为每个纹理单元重复前两个过程
- 设置uniform采样器变量的值为纹理单元的索引,通过glUniform1i()来设置
6.8 复杂纹理类型
6.8.1 3为纹理
- 3维纹理可以看做3维网格中的纹素体积;为了创建3维纹理,需要生成一个纹理对象名,以及把它绑定到GL_TEXTURE_3D目标;只要被绑定,使用glTexStorage3D()或者glTexImage3D()来维纹理空间创建存储
- 使用3维纹理坐标从着色器中读取3维纹理;3维纹理典型的使用示例是一些医学影响或者流体模拟领域的体渲染
6.8.2 数组纹理
6.8.3 立方体映射纹理
立方体映射例子——天空盒
用于环境映射的立方体映射
无缝立方体映射采样
- glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS)
6.8.4 阴影采样器
- 阴影采样器纹理坐标有一个附加分量,这个附加分量是用来与取得的纹素值做比较的参考值
- 使用阴影采样器时,纹理函数返回的值是一个范围为0.0~1.0的浮点值,表示通过比较操作获取到的纹素值的分数
- 对于只采样一个纹素值的纹理访问(使用GL_NEAREST滤波模式,没有mipmaps,并且每个纹素一个采样),返回值将是0.0或者1.0,这取决于纹素是否通过比较
- 如果多于一个纹素用来构造从着色器返回的值(例如滤波模式是线性,或者使用多重采样纹理),值可能是0.0~1.0之间的任意数,这取决于通过比较操作的纹素数
- 阴影纹理函数如下:
- float texture(gsampler1DShadow tex, vec3 P[, float bias])
- float texture(gsampler2DShadow tex, vec3 P[, float bias])
- float texture(gsamplerCubeShadow tex, vec4 P[, float bias])
- float texture(gsampler1DArrayShadow tex, vec3 P[, float bias])
- float texture(gsampler2DArrayShadow tex, vec4 P[, float bias])
- float texture(gsampler2DArrayShadow tex, vec3 P)
- float texture(gsamplerCubeArrayShadow tex, vecP P, float compare)
采样绑定到tex表示纹理单元的纹理坐标用P设置的阴影纹理;返回值是一个浮点数,表示通过阴影比较操作的样本与取得的纹素数据的比值分数
- 为了使采样器启用比较函数,调用glSamplerParameteri(),pname设置为GL_TEXTURE_COMPARE_MOD,而param设置为GL_COMAPRE_REF_TO_TEXTURE;为了将它禁用,设置param为GL_NONE
- pname设置为GL_TEXTURE_COMPARE_FUNC,param设置为以下比较函数之一:
- GL_LEQUAL
- GL_GEQUAL
- GL_LESS
- GL_GREATER
- GL_EQUAL
- GL_NOTEQUAL
- GL_ALWAYS
- GL_NEVER
6.8.5 深度模板纹理
- 纹理也可以存取深度盒模板值,每个纹素一个,使用纹理格式GL_DEPTH_STENCIL;这是帧缓存存储深度z分量盒模板值的典型方法
- 从深度模板纹理采样的时候,着色器默认读取深度;当应用程序设置GL_DEPTH_STENCIL_TEXTURE_MODE为GL_STENCIL_COMPONENTS时,着色器也可以读取模板值,并且着色器必须使用整数采样器类型
6.8.6 缓存纹理
缓存纹理没有内部采样器,并且采样器对象对缓存纹理没有效果;缓存纹理和1维纹理之间的不同如下:
- 1维纹理的大小由GL_MAX_TEXTURE_SIZE的值限制,而缓存纹理由GL_MAX_TEXTURE_BUFFER_SIZE的值限制,通常是2GB或者更多
- 1维纹理支持滤波、mipmaps、纹理坐标wrapping和其他采样器参数,而缓存纹理没有
- 1维纹理的纹理坐标是归一化的浮点值,而缓存纹理使用未归一化整数纹理坐标
void glTexBuffer(GLenum target, GLenum internalFormat, GLuint buffer)
把buffer命名的缓存对象的存储关联到激活的纹理单元target目标的缓存纹理
target必须是GL_TEXTURE_BUFFER
buffer的数据存储解释为由internalFormat确定类型的元素数组;如果buffer是零,那么将断开激活的缓存纹理与它的数据存储之间已经存在的关联
- 将缓存对象的一部分关联到缓存纹理,可以使用glTexBufferRange()
- void glTexBufferRange(GLenum target, GLenum internalFormat, GLuint buffer, GLintptr offset, GLsizeiptr size)
为名为buffer的缓存对象关联存储的一部分
在offset处开始,结束于绑定到激活纹理单元的target目标的缓存纹理的size字节
target必须是GL_TEXTURE_BUFFER
buffer的数据存储解释为由internalFormat确定类型的元素数组;如果buffer是零,则断开缓存纹理和他的数据存储之间现有的关联
offset必须是实现决定的GL_TEXTURE_OFFSET_ALIGNMENT常量的整数倍
- 为了在着色器总访问缓存纹理,必须创建一个uniform变量samplerBuffer,然后和函数texelFetch()一起使用来读取单个采样
- gvec4 texelFetch(gsamlperBuffer s, int coord) 在绑定到s的纹理中查询纹理坐标coord处的一个纹素