第六章 纹理

第六章 纹理

纹理映射

使用纹理映射的步骤:

  • 创建纹理对象,为他加载纹理数据
  • 为顶点增加纹理坐标
  • 把纹理图与着色器中将要使用的纹理采样器关联
  • 在着色器中使用纹理采样器来查询纹素值。

基本纹理类型

每个纹理对象表示组成完整纹理的一组图像,每个图像可以是纹素的1维2维或者3维数组,并且许多图像可以放在另一个图像的顶部来形成mipmap金字塔。纹理也可以包括1维或者是2维切片的数组,这样的纹理称为数组纹理。

在这里插入图片描述

其中矩形纹理对象(GL_TEXTURE_RECTANLGE)是2维纹理的特例,表示一个简单的纹素矩阵,没有mipmap并且不能表示纹理数组。一些纹理(texture wrapping)模式不支持矩形纹理。
缓冲纹理(GL_TEXTURE_BUFFER)表示任意纹素的1维数组,没有mipmap

创建和初始化纹理

为纹理对象保留名称

void glGenTextures(GLsizei n, GLuint * texture);

返回n个没有使用的纹理对象名称,不一定是连续的。只标记textures中的名称被使用,只有在第一次绑定之后才能获得纹理状态和纹数。

绑定到纹理目标

void glBindTexture(GLenum target, GLuint texture);

完成三件事:第一,首次使用非0texutre时创建一个新的纹理对象并赋给那个名称。第二,当绑定到已创建的纹理对象时,激活这个对象。第三,当绑定texture为0时,删除激活的纹理单元特定目标关联的所有的绑定。
初始绑定后,纹理对象的状态重置为特定目标的默认状态,比如边界截取模式或最大最小滤波设置。

设置激活的纹理单元

void glActiveTexture(GLenum texture);

glBindTexture将纹理对象绑定到激活的纹理单元,通过glActiveTexture设置激活的纹理单元。

是否是纹理对象

GLboolean glIsTexture(GLuint texture);

删除纹理

void glDeleteTextures(GLsizei n, const GLuint* texture);

初始化存储

void glTexStorage1D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width);
void glTexStorage2D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height);
void glTexStorage3D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth);

glTexStorage1D的target只能是GL_TEXTURE_1D。
glTexStorage2D当target是GL_TEXTURE_1D_ARRAY那么作为1维数组进行使用,width是纹理的范围,height是切片数量;如果target是GL_TEXTURE_2D则作为2维纹理,width、height作为纹理的大小。
glTexStorage3D当target是GL_TEXTURE_2D_ARRAY那么作为2维纹理数组数组进行使用,width、height是每个纹理切片的大小,depth是纹理切片数量;如果target是GL_TEXTURE_3D则作为3维纹理,width、heigh和depth作为纹理大小使用。

这三个函数为纹理创建固定的存储,纹理存储属性包括存储全部在设置的分辨率上以可选择的内部格式(internal format)纹理的所有mipmap级纹素所需要的内存量,分配之后不能重新定义存储,这里指存储的属性是不变的,纹理的内容可以使用glTexSubImage2D()来改变。

void glTexStorage2DMultisample(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);
void glTexStorage3DMultisample(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);

为当前绑定到target的多重采样纹理对象设置纹理存储。对于glTexStorage2DMultisample的target必须是GL_TEXTURE_2D_MULTISAMPLE,width和height设置纹理大小。对于glTexStorage3DMultisample的target必须是GL_TEXTURE_2D_MULTISAMPLE_ARRAY。如果fixedsamplelocation为true那么每个纹素的相同样本使用相同的子纹素位置,如果为false那么每个纹素给定的样本选择空间变化的位置。

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);

可以分配可变的纹理,支持重新定义纹理对象(例如,缩放或者是变化格式)。也可以分别为纹理的一个mipmap级选择性的提供初始图像数据。
internalFormat设置OpenGL用来存储纹理中纹素的格式。如果将一个缓冲对象绑定到GL_PIEXEL_UNPACK_BUFFER绑定点,那么从这个缓冲对象读取数据,data中的数据被解析为读取的偏移。如果没有绑定到GL_PIEXEL_UNPACK_BUFFER,那么data为数据指针,如果是NULL那么纹理的初始值是未定义的。初始纹素的数据格式是通过format和type确定的,OpenGL将其转换为internalFormat。

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);

glTexImage2DMultisample的target必须是GL_TEXTURE_2D_MULTISMAPLE。glTexImage3DMultisample的target必须是GL_TEXTURE_2D_MULTISAMPLE_ARRAY。不需要设置初始数据且不支持mipmap。

内部格式 internalFormat
在这里插入图片描述
在这里插入图片描述

外部格式 format+type
在这里插入图片描述

type通常是:GL_BYTE、GL_UNISGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNALF_FLOAT、GL_FLOAT或者GL_DOUBLE

压缩格式

在这里插入图片描述在这里插入图片描述

代理纹理

代理纹理与纹理对象对应

在这里插入图片描述

代理纹理可以用来测试在组合中使用一些限制时OpenGL实现的性能。如果代理纹理对象上纹理的分配失败,虚拟代理对象上纹理的宽高都是0,可以以此判断能否调用成功。

设置纹理数据

显式设置数据
纹理数据在程序中以从左到右从上到下进行布局。

void glTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint width, GLenum format, GLenum type, const void* data);
void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint width, GLint height, GLenum format, GLenum type, const void* data);
void glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint width, GLint height, GLint depth, GLenum format, GLenum type, const void* data);

level是mipmap的级数。format和type描述data中纹理图像数据的格式和类型。
如果target是1维数组纹理,那么yoffset和height分别设置将要更新的第一层和层数,否则是作为坐标。
如果target是2维数组纹理、cubemap映射或者cubmap映射数组纹理,那么zoffset和depth分别设置将要更新的第一层和层数,否则是作为坐标。

使用Pixel Unpack缓存

对于函数glTexSubImage2D当有缓存对象绑定到GL_PIEXEL_UNPACK_BUFFER时,data被解析为缓存对象的偏移量。

glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buf);
glBufferData(GL_PIXEL_UNPACK_BUFFER, sizeof(tex_data), tex_data, GL_STATIC_DRAW);
glBindTexture(GL_TEXUTRE_2D, tex);
glTexStorage2D(GL_TEXTURE_2D, 4, GL_R8, 8, 8);
glTexSubImage2D(GL_TEXTURE_2D, 
    0,                      // 第一个mipmap级
    0, 0,                   // x, y偏移
    8, 8,                   // 宽度和高度
    GL_RED,                 // 格式
    GL_UNSIGNED_BYTE,       // 类型
    NULL);                  // 数据(缓存里的偏移)

使用缓存来保存纹理数据的优点是缓存对象没有必要马上转换到纹理中,只要它发生在着色器需要数据之前,如果数据在应用程序内存当中,那么glTexSubImage2D函数的语义需要在函数返回之前完成数据拷贝,这阻止了并行的传送。

从帧缓存拷贝数据

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{12}D()本质上等同于调用glReadPixels()然后调用glTexImage{12}D()将数据加载到纹理。

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);

x,y表示帧缓存中要拷贝的数据的偏移,width和height设置拷贝数据的高度和宽度,xoffset、yoffset和zoffset表示纹理中目标区域的原点。

查询纹理数据

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、GL_TEXTURE_CUBE_MAP_NEGTIVE_X、GL_TEXTURE_CUBE_MAP_POSITIVE_Y、GL_TEXTURE_CUBE_MAP_NEGTIVE_Y、GL_TEXTURE_CUBE_MAP_POSITIVE_Z、GL_TEXTURE_CUBE_MAP_NEGTIVE_Z来指定获取cubemap的某一个面的数据。
lod是层次细节数。
format和type是需要的数据像素格式和类型。
image为图像数据保存的地址,如果有缓存绑定到GL_PIXEL_PACK_BUFFER,那么image是缓存中的偏移量。

使用时要注意防止数据溢出,而且如果使用GL_PIXEL_PACK_BUFFER来保存返回数据效率要高一些。

纹理数据布局

默认情况下,内存中的图像数据的排列是从左到右,从上到下布局的。

void glPixelStorei(GLenum pname, GLint param);
void glPixelStoref(GLenum pname, GLfloat param);

pname的取值:

  • Pixel Unpacking: GL_UNPACK_ROW_LENGTH、GL_UNPACK_SWAP_BYTES、GL_UNPACK_SKIP_PIXELS、GL_UNPACK_SKIP_ROWS、GL_UNPACK_SKIP_IMAGES、GL_UNPACK_ALIGNMENT、GL_UNPACK_IMAGE_HEIGHT和GL_UNPACK_LSB_FIRST
  • Pixel Packing: GL_PACK_ROW_LENGTH、GL_PACK_SWAP_BYTES、GL_PACK_SKIP_PIXELS、GL_PACK_SKIP_ROWS、GL_PACK_SKIP_IMAGES、GL_PACK_ALIGNMENT、GL_PACK_IMAGE_HEIGHT和GL_PACK_LSB_FIRST

unpack参数用来设定从客户内存或者是GL_PIXEL_UNPACK_BUFFER中读取数据。
pack参数用来指定如何将数据写入内存。

在这里插入图片描述

采样器对象

采样器对象包含了提取纹素的方法。

创建着色器对象

void glGenSamplers(GLsizei count, GLuint * samplers);

绑定采样器

void glBindSampler(GLuint unit, GLuint sampler);

unit可以是GL_TEXTURE0~GL_TEXTUREi。
将名为sampler的采样器对象绑定到下标用unit给定的采样器单元。如果sampler为0,任何当前绑定到采样器单元的采样器对象被解除绑定并且没有对象绑定到它的位置。

是否是采样器对象

GLboolean glIsSampler(GLenum id);

采样器参数

void glSamplerParameter{fi}(GLuint sampler, GLenum pname, Type param);
void glSamplerParameter{fi}v(GLuint sampler, GLenum pname, const Type* param);
void glSamplerParameterl{i, ui}v(GLuint sampler, GLenum pname, const Type* param);

如果没有采样器绑定到对应的采样单元时,则会使用默认的采样器对象。要修改默认采样器属性可以使用:

void glTexParameter{fi}(GLuint sampler, GLenum pname, Type param);
void glTexParameter{fi}v(GLuint sampler, GLenum pname, const Type* param);
void glTexParameterl{i, ui}v(GLuint sampler, GLenum pname, const Type* param);

删除采样器对象

void glDeleteSamplers(GLsizei count, const GLuint* samplers);

使用纹理

着色器中的纹理使用采样器变量表示,采样器变量是纹理对象的一组图像和采样器对象表示的采样参数的组合。

着色器纹理查询

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, float P);
Gvec4 texture(gsamplerCubeArray tex, vec4 P[, float bias]);

Gvec4表示任意类型向量占位符。
gsampler2D可以表示sampler2D、isampler2D或usampler2D。

纹理坐标的范围是0.0~1.0,如果给定的查询坐标位于范围之外,则使用采样器参数GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T和GL_TEXTURE_WRAP_R将其修改到范围之内。
这三个参数可以的取值为:

  • GL_CLAMP_TO_EDGE:当纹理坐标超出范围,那么纹理边缘的纹素用来形成返回到着色器的值。
  • GL_CLAMP_TO_BORDER:返回GL_TEXTURE_BORDER_COLOR
  • GL_REPEAT:使用小数部分来进行纹理的查询
  • GL_MIRRORED_REPEAT:镜像方式进行重复。整数部分是偶数则用小数部分进行查询,整数部分是奇数是用1减小数部分来查询。

在这里插入图片描述

组织纹理数据

使用swizzle来重新设置纹理数据分量顺序。

static const GLenum abgr_swizzle[] = {GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE};
...
// 一次性设置四个分量
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, abgr_swizzle);
// 只设置Alpha分量
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);

使用多重纹理

每个着色器阶段都支持至少16个纹理,再乘以着色器阶段数目,得到80个纹理。当调用glBindTexture()时,它操作的是绑定到激活纹理单元的纹理,这称为选择器(selector),默认的选择器是0,改变激活纹理selector的函数是glActiveTexture()。

为了使用多重纹理,需要声明多个uniform采样器变量。每个指向一个不同的纹理单元。
使用多重纹理的步骤:

  • 使用glActiveTexture()选择第一个纹理单元
  • 使用glBindTexture()绑定纹理
  • 为其他纹理重复以上过程
  • 设置uniform采样器变量的值为纹理单元的索引

复杂纹理类型

3维纹理

数组纹理

gvec4 texture(gsampler2D tex, vec2 P[, float bias]);
gvec4 texture(gsampler2DArray tex, vec3 P[, float bias]);

对比2维纹理和2维纹理数组的texture函数,P的第三个分量用于指定数组的索引。

立方体映射纹理

立方体映射中采样时,使用的纹理坐标是三维的,看作是来自原点的方向。方向指向用来读取纹理表面的位置。
立方体映射纹理可以组成数组,可以使用GL_TEXTURE_CUBE_MAP_ARRAY纹理目标来创建和修改立方体映射数组纹理。

无缝立方体映射采样

glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);

当开启无缝滤波之后,OpenGL将使用来自相邻立方体映射面的纹素来获取用于滤波的元素。

阴影采样器

阴影采样器纹理坐标有一个附加分量,这个附加分量用来与取得的纹素值作比较的参考值。使用阴影着色器时,纹理函数返回值是一个范围为0.0~1.0的浮点值,表示通过比较操作获取的纹素值的分数。
对于只采样一个纹素值的纹理访问(GL_NEAREST滤波模式,没有mipmaps并且每个纹素一个采样),返回值将是0.0或者是1.0,取决去纹素是否通过比较。
对于多于一个纹素用来构造从着色器返回的值(如采用线性滤波或者多重采样),返回值将为0.0~1.0之间。

通过设置采样器参数GL_TEXTURE_COMPARE_MOD来设置比较方式,如果设置为GL_COMPARE_REF_TO_TEXTURE则使用采样器设置的模式进行比较,具体比较方式通过采样器参数GL_TEXTURE_COMPARE_FUNC进行设置:GL_LEQUAL、GL_GEQUAL、GL_LESS、GL_GREATER、GL_EQUAL、GL_NOTEQUAL、GL_ALWAYS或者是GL_NEVER。

深度模板纹理

缓存纹理

缓存纹理没有内部采样器,并且采样器对象对缓存纹理没有效果。缓存对象与1维对象的不同如下:

  • 1维纹理大小由GL_MAX_TEXTURE_SIZE限制,缓存纹理由GL_MAX_TEXTURE_BUFFER_SIZE限制,通常2G或者更多。
  • 缓存纹理没有滤波、mipmap、纹理坐标wrapping和其它采样器参数
  • 1维纹理坐标是归一化的浮点值,而缓存纹理使用的是未归一化整数纹理坐标

关联缓存对象和纹理

void glTexBuffer(GLenum target, GLenum internalFormat, GLuint buffer);

target必须是GL_TEXTURE_BUFFER,buffer为缓存对象。

void glTexBufferRange(GLenum target, GLenum internalFormat, GLuint buffer, GLintptr offset, GLsizeptr size);

将缓存对象的部分关联到缓存纹理。

vec4 texelFetch(samplerBuffer s, int coord);
ivec4 texelFetch(isamplerBuffer s, int coord);
uvec4 texelFetch(usamplerBuffer s, int coord);

着色器中读取纹理坐标coord处的一个纹素。texelFetch也可以对普通纹理进行读取,如果对非缓存纹理进行采样,那么纹理的采样参数将被忽略,纹理坐标任然作为一个非归一化的整数值来进行解析。

纹理视图

OpenGL可以在多个纹理之间共享一个数据存储,每个都有自己的格式和维数。

创建纹理视图

void glTextureView(GLuint texture, GLenum target, GLuint origTexture, GLenum internalFormat, GLuint minLevel, GLuint numLevels, GLuint minLayer, GLuint numLayers);

为名称为origTexture的纹理创建新视图,必须是已经存在的纹理名称,且纹理数据已经初始化且不变。texture被关联到origTexture存储的数据,并且重新变为目标用target设置的不变纹理。纹理内部格式使用internalFormat,且必须与origTexture兼容。minLevel和numLevels分别为新纹理设置第一个mipmap层和mipmap的层数,minLayer和numLayers设置关联到texture数组纹理的第一个层和层数。新的纹理目标必须与原始纹理目标兼容。

纹理目标兼容性:

在这里插入图片描述

纹理内部各式兼容性:

在这里插入图片描述

使用示例:

例1:

GLuint tex[2];
glGenTexture(2, &tex);
glBindTexture(GL_TEXTURE_2D, tex[0]);
glTexStorage2D(GL_TEXTURE_2D, 10, GL_RGB8, 1024, 1024);
glTextureView(
    tex[1],         // 新纹理视图
    GL_TEXTURE_2D,  // 新视图的目标
    tex[0],         // 原始纹理
    GL_RGB8UI,      // 新格式
    0, 10,          // 所有mipmap
    0, 1            // 只有一层
);

例2:从2维纹理数组中取出一个切片,作为独立的2维纹理使用

GLuint tex[2];
glGenTexture(2, &tex);
glBindTexture(GL_TEXTURE_2D_ARRAY, tex[0]);
glTexStorage2D(GL_TEXTURE_2D_ARRAY, 10, GL_RGB32F, 1024, 1024, 100);
glTextureView(
    tex[1],         // 新纹理视图
    GL_TEXTURE_2D,  // 新视图的目标
    tex[0],         // 原始纹理
    GL_RGB32F,      // 新格式
    0, 10,          // 所有mipmap
    50, 1            // 只有一层
);

删除原始父纹理也是合法的,只要至少存在一个数据视图,就不会删除它。
通过创建纹理的多个数组视图并且将不同的输出绘制到纹理的不同切片,一个数组纹理可以在它的切片中高效的存储多个格式的数据。

压缩纹理

OpenGL当中既可以使用OpenGL内置的压缩算法来压缩数据,也可以直接将压缩好的数据提供给OpenGL。
OpenGL一定支持两个格式组,一个是RGTC(红绿纹理压缩),另一个是BPTC(分块纹理压缩)。

为使用压缩内部格式的纹理实现存储

void glCompressedTexImage1D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imageSize, const void * data);
void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data);
void glCompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data);

滤波

根据使用的变换和纹理映射,屏幕上的一个像素可以对应于一个纹素的一部分,或者是多个纹素的集合,通过滤波来确定如何通过多个纹素值来确定该像素的值。从信号理论的角度出发,纹理需要用数据表示的最高频率的两倍频率来采样原始信号。

线性滤波

线型滤波是用坐标来从离散采样信号选择临近样本的技术,并且使用原始信号的线性近似来替代这些信号。

在放大时,只有一个mipmap级被使用,只对GL_TEXTURE_MAG_FILTER提供两个选项,它们分别是GL_NEAREST和GL_LINEAR,第一个禁用滤波,采用位置最近像素,第二个启用线性滤波。

生成和使用mipmap

使用mipmap时,OpenGL根据被映射对象的大小(单位是像素)自动决定使用纹理图的哪个分辨率级别。

GL_TEXTURE_MIN_FILTER控制在mipmap级数大于0时如何重建纹素,共有6个设置。GL_NEAREST和GL_LINEAR将禁用mipmap,OpenGL将使用纹理的基级数。其他的4个模式控制mipmap如何使用,GL_NEAREST_MIPMAP_NEAREST、GL_NEAREST_MIPMAP_LINEAR、GL_LINEAR_MIPMAP_NEAREST和GL_LINEAR_MIPMAP_LINEAR。这四个模式的结构是GL_{A}MIPMAP{B}。{A}和{B}分别是NEAREST和LINEAR,{A}控制来自每个mipmap级的纹素的构造并且与GL_TEXTURE_MAG_FILTER设置一样的工作。{B}控制mipmap级之间的采样混合。

为了使用mipmap必须提供最大尺寸与1*1图之间所有2的幂纹理级别。如果纹理不是正方形的,当一个维度先到达1的大小,那么会对另一个维度继续创建新的级数,直到两个维度都到达1。

如果不想使用mipmap到1*1纹理的所有变化,可以将GL_TEXTURE_MAX_LEVEL设置为提供的最大级别。

自动生成mipmap

void glGenerateMipmap(GLenum target);

构建的mipmap级由GL_TEXTURE_BASE_LEVEL和GL_TEXTURE_MAP_LEVEL控制。

计算mipmap级别

纹理级别的计算公式:

λ = l o g 2 ρ + l o d b i a s \lambda=log_2\rho+lod_{bias} λ=log2ρ+lodbias

其中 ρ \rho ρ为纹理图像和纹理映射的多边形大小比例因子, l o d b i a s lod_{bias} lodbias是采样器的细节层次偏移,通过glSamplerParameri设置,pname是GL_TEXTURE_LOD_BASE,默认为0,当 λ < = 0 \lambda<=0 λ<=0时使用放大滤波器,当 λ > 0 \lambda>0 λ>0时使用缩小滤波器, λ \lambda λ为mipmap级别。如果纹理图像是64x64,多边形大小为32x32,那么 ρ = 2.0 \rho=2.0 ρ=2.0(不是4)。通过 l o d b i a s lod_{bias} lodbias可以偏移 λ \lambda λ,计算结果通过GL_TEXTURE_MIN_LOD和GL_TEXTURE_MAX_LOD进行截取。

除了以上对 λ \lambda λ进行控制的参数,也可以使用GL_TEXTURE_BASE_LEVEL和GL_TEXTURE_MAX_LEVEL对mipmap的level选择进行进一步控制。GL_TEXTURE_BASE_LEVEL的另一个用法是纹理流,可以根据纹理加载情况控制可以使用的纹理数据。

高级纹理查询

显示细节层次

gvec4 textureLod(gsampler1D tex, float P, float lod);
gvec4 textureLod(gsampler2D tex, vec2 P, float lod);
gvec4 textureLod(gsampler3D tex, vec3 P, float lod);
gvec4 textureLod(gsamplerCube tex, vec3 P, float lod);
gvec4 textureLod(gsampler1DArray tex, vec2 P, float lod);
gvec4 textureLod(gsampler2DArray tex, vec3 P, float lod);
gvec4 textureLod(gsampler2DRect tex, vec2 P, float lod);
gvec4 textureLod(gsamplerCubeArray tex, vec4 P, float lod);

因为不支持mipmap所以samplerBuffer和samplerRect不支持textureLod。

显示梯度设置

gvec4 textureGrad(gsampler1D tex, float P, float dPdx, float dPdy);
gvec4 textureGrad(gsampler2D tex, vec2 P, vec2 dPdx, vec2 dPdy);
gvec4 textureGrad(gsampler3D tex, vec3 P, vec3 dPdx, vec3 dPdy);
gvec4 textureGrad(gsamplerCube tex, vec3 P, vec3 dPdx, vec3 dPdy);
gvec4 textureGrad(gsampler1DArray tex, vec2 P, float dPdx, float dPdy);
gvec4 textureGrad(gsampler2DArray tex, vec3 P, vec2 dPdx, vec2 dPdy);
gvec4 textureGrad(gsamplerCubeArray tex, vec4 P, vec3 dPdx, vec3 dPdy);

偏移后纹理获取

gvec4 textureOffset(gsampler1D tex, float P, int offset[, float bias]);
gvec4 textureOffset(gsampler2D tex, vec2 P, ivec2 offset[, float bias]);
gvec4 textureOffset(gsampler3D tex, vec3 P, ivec3 offset[, float bias]);
gvec4 textureOffset(gsampler1DArray tex, vec2 P, int offset[, float bias]);
gvec4 textureOffset(gsampler2DArray tex, vec3 P, ivec2 dPdx[, float bias]);
gvec4 textureOffset(gsampler2DRect tex, vec2 P, ivec2 dPdx[, float bias]);

投影纹理

gvec4 textureProj(gsampler1D tex, vec2 P[, float bias]);

着色器中的纹理查询

// 
vec2 textureQueryLod(gsampler1D tex, float P);
// 查询mipmap级别数量
int textureQueryLevels(gasampler1D tex);
// 获取纹理大小
int textureSize(gsampler1D tex, int lod);

收集纹理

gvec4 textureGather(gsampler2D tex, vec2P[, int comp]);

从绑定到采样器tex的矩形、二维(数组)或者立方体映射(数组)纹理中采集四个纹素,通常用于建立双线性滤波纹素值,以及返回返回值中的四个纹素中一个选择分量,comp的1、2、3、4分别表示x、y、z、w。

点精灵

点精灵是使用片元着色器的OpenGL点,点内的坐标是二维向量gl_PointCoord,点精灵使用正方形来显示纹理。如果要控制点精灵大小,可以设置gl_PointSize。

可以利用gl_PointCoord和discard关键字来将点精灵显示成圆形。

控制点的外观

void glPointParameter{if}(GLenum pname, TYPE param);
void glPointParameter{if}v(GLenum pname, const TYPE* param);

如果pname是GL_POINT_SPRITE_COORD_ORIGIN,那么param必须是GL_LOWER_LEFT或者是GL_UPPER_LEFT。如果pname是GL_POINT_FADE_THRESHOLD_SIZE那么param必须是一个大于等于0的浮点值(或者包括这样的值得变量地址)。

GL_POINT_FADE_THRESHOLD_SIZE控制点精灵如何反走样,当点的大小在这个阈值之下时,OpenGL有绚丽来停止执行真正的反走样,并且使用混合使点消失在背景中。

渲染到纹理贴图

void glFramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level);
void glFramebufferTexture1D(GLenum target, GLenum attachment, GLenum texturetarget, GLuint texture, GLint level);
void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum texturetarget, GLuint texture, GLint level);
void glFramebufferTexture3D(GLenum target, GLenum attachment, GLenum texturetarget, GLuint texture, GLint level, GLint layer);

target必须是GL_READ_FRAMEBUFFER、GL_DRAW_FRAMEBUFFER或者GL_FRAMEBUFFER。
attachment必须是GL_COLOR_ATTACHMENTi、GL_DEPTH_ATTACHMENT、GL_STENCIL_ATTACHMENT或者GL_DEPTH_STENCIL_ATTACHMENT(这种情况下,纹理内部各式必须是GL_DEPTH_STENCIL)。

如果texture不是0那么glFramebufferTexture1D的texturetarget必须是GL_TEXTURE_1D;glFramebufferTexture2D的texturetarget必须是GL_TEXTURE_2D、GL_TEXTURE_RECTANBLE、GL_TEXTURE_CUBE_MAP_POSITIVE_X、GL_TEXTURE_CUBE_MAP_POSITIVE_Y、GL_TEXTURE_CUBE_MAP_POSITIVE_Z、GL_TEXTURE_CUBE_MAP_NEGTIVE_X、GL_TEXTURE_CUBE_MAP_NEGTIVE_Y、GL_TEXTURE_CUBE_MAP_NEGTIVE_Z;对于glFramebufferTexture3D必须是GL_TEXTURE_3D。

level表示mipmap级别。
layer表示使用的纹理的层。

void glFramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);

关联3维或者1维、2维数组纹理的一层作为帧缓存附件。

丢弃已渲染数据

void glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum * attachments);
void glInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum * attachments, GLint x, GLint y, GLint width, GLint height);

丢弃x、y、width和height限制区域内的全部内容。attachments是GL_DEPTH_ATTACHMENT、GL_STENCIL_ATTACHMENT、GL_DEPTH_STENCIL_ATTACHMENT和GL_COLOR_ATTACHMENTi的组合,numAttachments是数组数量。

void glInvalidateTexImage(GLuint texture, GLint level);
void glInvalidateTexSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint width, GLint height, GLint depth);

丢弃texture制定level的内容。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值