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

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...