《OpenGL宝典》--纹理
创始人
2024-05-30 09:22:26
0

文章目录

  • 创建并初始化纹理
    • 创建纹理
    • 更新纹理数据
  • 纹理目标和类型
  • 从着色器中读取纹理数据
    • 采样器类型
    • 使用texelFetch内置函数从着色器读取纹理
    • 使用texture()函数从着色器读取纹理
    • 获取更多信息
  • 控制纹理数据的读取方式
    • 使用采样器对象存储采样器包装和过滤模式的参数
      • 创建一个或多个采样器
      • 设置采样器对象参数
      • 绑定采样器到纹理单元
      • 设置存储在纹理对象内的采样器对象
    • 纹理对象参数
      • 纹理过滤
        • 过滤器
      • 纹理环绕
        • 设置采样器纹理环绕方式
    • 使用多个纹理
      • 将纹理绑定到特定纹理单元
      • 着色器中的采样器统一变量引用不同单元
    • mipmap
      • 层数设置,数据输入,限制层数使用
      • mip贴图过滤
      • 构建mip层
  • 数组纹理
    • 2D数组纹理与3D纹理的区别
    • 加载2D数组纹理
    • 使用数组纹理
  • 在着色器中向纹理写入数据
    • 使用image变量表示纹理的单个图像
      • 声明图像单元
      • 读取存储数据
      • 将纹理层绑定到图像单元
      • 在着色器使用图像单元绑定的图像

创建并初始化纹理

创建纹理

GLuint texture;
glCreateTextures(GL_TEXTURE_2D, 1, &texture);
glTextureStorage2D(	texture,		//纹理对象1,				//mipmap等级GL_RGBA32F,		//纹理数据类型256,256);		//纹理大小
glBindTexture(GL_TEXTURE_2D,texture);

更新纹理数据

glTextureSubImage2D(	texture,	//纹理对象0,			//等级0, 0,		//偏移量256, 256,	//大小GL_RGBA,	//通道类型GL_FLOAT,	//数据类型data);		//数据指针

glTextureSubImage2D将保留原始纹理数据副本,因此可将data指向的内存释放。

如果只是想将纹理初始化为一个固定值,则可使用glClearTexSubImage()

void glClearTexSubImage(	GLuint texture,	//纹理对象GLint  level,	//想要清除的mip贴图层GLint  xoffset,	//待清除区域起始偏移量GLint  yoffset,	//GLint  zoffset,	//GLsizei width,	//区域尺寸GLsizei height,	//GLsizei depth,	//GLenum  format,	//通道类型GLenum  type,	//数据类型const void *data);//单纹素数据

纹理目标和类型

纹理目标GL_TEXTURE_*
1D/2D/3D
RECTANGLE矩形纹理(2D纹理的子集)
1D_ARRAY/2D_ARRAY聚合到单个对象中的纹理*
CUBE_MAP/CUBE_MAP_ARRAY六个正方形图像的集合/数组纹理
BUFFER特殊的纹理类型
2D_MULTISAMPLE/2D_MULTISAMPLE_ARRAY多采样抗锯齿

从着色器中读取纹理数据

采样器类型

常用的有sampler2D, sampler2Darray, samplerCube, samplerCubearray, sampler2DMS, sampler2DMSArray
sampler2D表示符点数据,若要表示有符号整数数据、无符号整数数据,则使用isampler2D,usampler2D

使用texelFetch内置函数从着色器读取纹理

vec4 texelFetch(sampler1D s, int P, int lod);//P为读取位置,lod为mip层
vec4 texelFetch(sampler2D s, ivec2 P, int lod);
ivec4 texelFetch(isampler2D s, ivec2 P, int lod);
uvec4 texelFetch(usampler1D s, ivec3 P, int lod);

返回值都为4通道,若只返回一个通道,则G、B默认值为0,A默认值为1。

使用texture()函数从着色器读取纹理

texture()与texelFetch()的区别主要是:
texture采用函数会涉及归一化、过滤以及插值等复杂操作,基本无法得到某一确切像素的值。
texelFetch 使用的是未归一化的坐标直接访问纹理中的纹素,不执行任何形式的过滤和插值操作,坐标范围为实际载入纹理图像的宽和高。

gvec4 texture(	gsampler2D sampler,vec2 P,[float bias]);

获取更多信息

使用textureSize返回纹理大小

int textureSize(sampler1D sampler, int lod);
ivec2 textureSize(sampler2D sampler, int lod);
ivec3 textureSize(gsampler3D sampler, int lod);

使用textureSamples()了解多重采样纹理包含多少样本

int textureSamples(sampler2DMS sampler);

控制纹理数据的读取方式

使用采样器对象存储采样器包装和过滤模式的参数

创建一个或多个采样器

void glCreateSamplers(GLsizei n, GLuint *samplers);

设置采样器对象参数

void glSamplerParameter*(	GLuint sampler,	//采样器对象名称GLenum pname,	//指定采样器参数的符号名称参数类型(与*有关) param);	//指定pname的值

GLenum pname
GL_TEXTURE_WRAP_S,
GL_TEXTURE_WRAP_T,
GL_TEXTURE_WRAP_R,
GL_TEXTURE_MIN_FILTER,
GL_TEXTURE_MAG_FILTER,
GL_TEXTURE_BORDER_COLOR,
GL_TEXTURE_MIN_LOD,
GL_TEXTURE_MAX_LOD,
GL_TEXTURE_LOD_BIAS ,
GL_TEXTURE_COMPARE_MODE,
GL_TEXTURE_COMPARE_FUNC.

绑定采样器到纹理单元

void glBindSampler(GLuint uint, GLuint sampler);

设置存储在纹理对象内的采样器对象

每个纹理都有一个默认采样参数,当没有采样器对象绑定到相应纹理时,则将使用默认采样参数。可以使用如下函数设置默认采样参数

//Texture直接作用于纹理对象
void glTextureParameterf(	GLuint texture,GLenum pname,GLfloat param);
//Tex作用于指定绑定点的纹理对象----glBindTexture(GLenum target,GLuint texture);				
void glTexParameterf(		GLenum target,GLenum pname,GLfloat param);

纹理对象参数

纹理过滤

从拉伸或收缩的纹理图中计算颜色片段的过程称为纹理过滤(texture filtering)。

过滤器

利用采样器参数函数,支持我们在放大和缩小的情况下分别设置构造纹素值

放大过滤器参数名称:GL_TEXTURE_MAG_FILTER
缩小过滤器参数名称:GL_TEXTURE_MIN_FILTER

最近邻过滤:GL_NEAREST
线性过滤:GL_LINEAR

设置采样器的过滤器

glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

直接设置纹理的默认过滤器

//Texture直接作用于纹理对象
void glTextureParameterf(	GLuint texture,GLenum pname,GLfloat param);
//Tex作用于指定绑定点的纹理对象----glBindTexture(GLenum target,GLuint texture);				
void glTexParameterf(		GLenum target,GLenum pname,GLfloat param);

纹理环绕

正常情况,我们只会在0.0~1.0之间指定纹理坐标,如果纹理坐标在此范围外,OpenGL会根据采样器对象中指定的当前纹理环绕模式方式进行设置。

设置采样器纹理环绕方式

使用如下函数:

glSampleParameter*(	GLuint sampler,GLenum pname,*      param)

纹理环绕参数有:

GL_TEXTURE_WRAP_S		//1D/2D/3D
GL_TEXTURE_WRAP_T		//2D/3D
GL_TEXTURE_WRAP_R		//3D

纹理环绕值有:

GL_REPEAT			//重复
GL_MIRRORED_REPEAT	//镜面重复			
GL_CLAMP_TO_EDGE	//超出部分对边缘采样
GL_CLAMP_TO_BORDER	//超出部分使用边框颜色
GL_MIRROR_CLAMP_TO_EDGE	//	只在(-1.0~0.0, 1.0~2.0上镜面重复,其余部分使用边缘颜色)

其中边框颜色使用如下函数设置。

glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, color);

使用多个纹理

使用函数glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units);得到所有着色器阶段每次可访问的最大纹理单元数量
在这里插入图片描述

将纹理绑定到特定纹理单元

glBindTexture()只能将纹理绑定到glActiveTexture(GL_TEXTURE0 + i) 绑定的纹理点上。

void glBindTextureUnit(	GLuint unit,	//想要我们绑定的以零为基准的索引GLuint texture);//// 方法1:
glActiveTexture(GL_TEXTURE0 + i); // active proper texture unit before binding
glBindTexture(GL_TEXTURE_2D, textures[i].id);
// 方法2:
glBindTextureUnit(i, textures[i].id);

着色器中的采样器统一变量引用不同单元

方法一:在程序中获取采样器地址,并为它设置引用单元数

glUniform1i(glGetUniformLocation(shader, name), unit);

方法二:在着色器代码中使用binding配置限定符设置。

layout(binding = 1) uniform sampler2D texture_diffuse1;

mipmap

层数设置,数据输入,限制层数使用

mip贴图层通过如下函数输入2D纹理mipmap数据。

void glTexSubImage2D(	GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const void * pixels);

level参数指定图像数据应用于哪一层。

但在输入数据前,首先需要开辟对应层数的空间。
使用如下函数开辟纹理空间时,设置levels的值将确定该纹理有多少层。(0表示未使用mipmap)

void glTexStorage2D(	GLenum target,GLsizei levels,GLenum internalformat,GLsizei width,GLsizei height);

同时可以使用如下函数限制渲染过程中可使用的mip层。

glTextureParameteri(texture, GL_TEXTURE_BASE_LEVEL, 0);
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, 4);

mip贴图过滤

GL_NEAREST
GL_LINEAR
GL_LEAREST_MIPMAP_NEAREST		//选择最邻近的mip层,并执行最邻近过滤
GL_NEAREST_MIPMAP_LINEAR
GL_LINEAR_MIPMAP_NEARSET
GL_LINEAR_MIPMAP_LINEAR			//在mip层之间使用线性插值并执行线性过滤(三线性插值)

构建mip层

使用glTexSubImage2D函数输入mipmap图其他层数不大方便,使用OpenGL构建纹理十分方便。

void glGenerateMipmap(	GLenum target);
void glGenerateTextureMipmap(GLuint texture);如:
glGenerateMipmap(GL_TEXTURE_2D);

但使用快速构建会一定程度影响程序运算速度,在性能关键型应用程序中需要优先考虑加载自己预先构建的mip贴图。

数组纹理

多个1D纹理可映射为1D数组纹理
多个2D纹理、立方体纹理可映射为2D数组纹理
不能创建3D数组纹理(OpenGL不支持)

2D数组纹理与3D纹理的区别

2D数组纹理的层之间不能进行过滤操作(线性过滤等),但2D数组纹理拥有比3D纹理更大的空间。

加载2D数组纹理

GLuint texture2Ds;
glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &texture2Ds);
void glTextureStorage3D(	GLuint texture,			//texture2DsGLsizei levels,			//GLenum internalformat,	//内部格式GLsizei width,GLsizei height,GLsizei depth);			//数组元素 或 层
//输入数据
for(int i=0;ivoid glTextureSubImage3D(	GLuint texture,		GLint level,GLint xoffset,		//0GLint yoffset,		//0GLint zoffset,		//层数GLsizei width,		//图片宽GLsizei height,		//图片高GLsizei depth,		//如果输入为2D图形,depth = 1GLenum format,		//输入数据的格式GLenum type,		//输入数据的类型const void *pixels);	//数据指针
}

使用数组纹理

layout (binding = 0) uniform sampler2DArray texture2DArray;
void main()
{color = texture( texture2DArray, vec3( vec2(x,y), float(layer) ) );//texture( 2D数组纹理采样器, vec3( vec2采样点, float(采样层数) ) );
}

注意:采样层数为整数,但在texture输入采样位置时,要转化为float值。

在着色器中向纹理写入数据

使用image变量表示纹理的单个图像

声明图像单元

uniform image2D my_image;

读取存储数据

//从P指定的坐标处读取image数据
vec4 imageLoad(readonly image2D image, ivec2 P);
//将data的数据存入P指定的image中
void imageStore(image2D image, ivec2 P, vec4 data);其他:
有符号整数数据(i)和无符号整数(u)数据图像的存取
ivec4 imageLoad(readonly iimage2D image, ivec2 P);
void imageStore(iimage2D image, ivec2 P, ivec4 data);

注意:P为整数型,加载时不执行过滤(与textelFetch()函数一样)。

将纹理层绑定到图像单元

void glBindImageTexture(	GLuint unit,GLuint texture,GLint level,GLboolean layered,GLint layer,GLenum access,GLenum format);

在着色器使用图像单元绑定的图像

rgba32ui为格式限定符,见书P125

layout (binding = 0 , rgba32ui) readonly uniform uimage2D image_in;
layout (binding = 1) uniform writeonly uimage2D image_out;void main(){//将image_in 中的数据复制入 image_outivec2 P = ivec2(gl_FragCoord.xy);uvec4 data = imageLoad(image_in, P);imageStore(image_out, P, ~data);
}

相关内容

热门资讯

梦游星空初中作文800字(推... 梦游星空初中作文800字 篇一梦游星空我有一个梦想,那就是能够梦游星空。每当夜晚来临,我总是被漆黑的...
致已逝的七年级作文700字推... 致已逝的七年级作文700字 第一篇童年,美好而纯真。可是,它终将会逝去。我童年时最大的乐趣,莫过于和...
秋雨作文-秋雨的作文【精选3... 秋雨作文-秋雨的作文 篇一秋雨的作文秋天是一个多雨的季节,而秋雨更是秋天的象征。秋雨给大地带来了滋润...
初一的心事(经典3篇) 初一的心事 篇一初一的心事总是特别多,仿佛一瞬间从小学生变成了初中生,面对着新的学校、新的环境和新的...
告别同学的作文初一推荐25篇 告别同学的作文初一 第一篇时光匆匆如流水,转眼间,六年过去,我们即将毕业,昔日的时光不再重现,昔日的...
意外之喜作文(经典6篇) 意外之喜作文 篇一意外之喜总是让人心生欢喜和感激,它们常常出现在我们生活的某个角落,给我们带来意想不...
小兔子花初中作文【通用5篇】 小兔子花初中作文 篇一小兔子花我家后院有一个小花园,里面有各种各样的花。其中最引人注目的就是一株小兔...
不曾后悔初中作文(推荐5篇) 不曾后悔初中作文 篇一初中三年,是人生中最为关键的阶段之一。在这段时间里,我经历了许多挑战和困难,但...
我和我的祖国作文初一【精彩6... 我和我的祖国作文初一 篇一我和我的祖国我和我的祖国有着深厚的情感纽带。祖国是我们生活的土地,是我们成...
严母作文600字【最新3篇】 严母作文600字 篇一:严母的教育方式严母作文600字 篇二:严母的爱与关怀严母作文600字 篇三严...
如何指导初中学生写作文(优质... 如何指导初中学生写作文 篇一初中学生写作文是培养学生语言表达能力和思维逻辑能力的重要途径之一。然而,...
我的初中生活作文(经典6篇) 我的初中生活作文 篇一初中生活是我人生中的一个重要阶段,它塑造了我成长的轨迹,给予了我许多难忘的回忆...
掌心里的爱作文【优秀5篇】 掌心里的爱作文 篇一掌心里的爱爱是一种无私的情感,它无所不在,无处不在。在我们的生活中,有很多形式的...
全世界化成一滴蓝色的眼泪 初... 全世界化成一滴蓝色的眼泪 初中生作文 篇一全世界化成一滴蓝色的眼泪蓝色的眼泪从天空中滴落,汇聚成一滴...
我印象最深的人七年级写老师的... 我印象最深的人七年级写老师的作文 篇一我印象最深的人是我的语文老师,她是我七年级的班主任。她是一个非...
我属于你初一作文(推荐5篇) 我属于你初一作文 篇一我属于你初一作文初中生活,是我人生中重要的一个阶段。刚踏入初中的时候,我有些紧...
绿荫下的光斑初一作文(优秀3... 绿荫下的光斑初一作文 篇一绿荫下的光斑初一作文初一的暑假,我和家人来到了一个风景如画的小镇度假,这里...
初一想象作文【优选6篇】 初一想象作文 篇一翱翔的翅膀我有一双神奇的翅膀,它们是我独一无二的特殊能力。当我激动或兴奋的时候,这...
初一开学作文(最新6篇) 初一开学作文 篇一我的初一开学心情初一开学,对于我来说是一次特别的经历。我迫不及待地等待着这一天的到...
我是钻石初一作文【经典5篇】 我是钻石初一作文 篇一钻石初一是我人生中的一个重要转折点。回想起以前的日子,我觉得自己就像一个粗糙的...