OpenGL API介绍(四):纹理

您所在的位置:网站首页 opengl使用shader OpenGL API介绍(四):纹理

OpenGL API介绍(四):纹理

2023-04-04 19:49| 来源: 网络整理| 查看: 265

1.纹理概念

纹理不是图片,纹理可以看成图片的超集:

图片中每个元素被称为pixel,pixel只能是颜色值;纹理中每个元素被称为texel,texel可以是任意值,比如:颜色值、粗糙度、金属度、光照度、法线方向等。

texel用于描述三维物体某个顶点的物理属性,从这个角度来看,纹理可以看做三维物体所有顶点的物理属性集合。举个例子,当texel为颜色值,纹理如图:

color texture

这种图被很多教程用来讲解什么是纹理,反而给初学者带来了极大的误导:认为纹理就是贴到物体表面的图片。首先,它只是texel为颜色值的纹理;第二,纹理不仅仅可以贴在物体表面,比如我们要绘制一个具有剖面图的物体,我们还需要对物体内部进行贴图。

若想要在OpenGL中使用纹理,需要把纹理拷贝到纹理对象(texture object)中,以二维纹理的使用为例,步骤如下:

//1.加载纹理 int width, height, nrChannels; unsigned char *data = stbi_load("texture.jpg", &width, &height, &nrChannels, 0); //2.创建纹理对象 unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); //3.拷贝纹理到纹理对象 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); glBindTexture(GL_TEXTURE_2D, 0); 2.纹理坐标

纹理坐标采用标准化坐标系(normalized coordinate),即各个坐标轴的坐标范围是[0,1]。纹理坐标系统按照维度来划分,分为一维坐标系、二维坐标系、三维坐标系。其中二维纹理坐标系最常见,二维纹理坐标系别名UV坐标系或者ST坐标系,U和S指的是横坐标,V和T指的是纵坐标。

在调用OpenGL中的画图函数前,需要为每个顶点指定一个纹理坐标,这样OpenGL就能够根据纹理坐标,从纹理对象(texture object)中查找到这个顶点的物理属性。指定纹理坐标的步骤如下,用户需要把纹理坐标拷贝到VBO内:

//1.创建VBO unsigned int VBO; glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); //2.拷贝纹理坐标到vbo glBufferData(GL_ARRAY_BUFFER, 2*sizeof(vertices), NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); glBindBuffer(GL_ARRAY_BUFFER, 0); 3.纹理映射

纹理映射(texture mapping),也可以称为纹理采样、纹理过滤、纹理贴图,指的是把纹理texel映射到片元的过程。这里需要复习一下渲染管线的知识:

渲染管线

纹理映射发生在片段着色阶段,它需要做的就是根据纹理坐标和纹理数据,计算出每个片元的值。目前的话,我们只知道三角形三个顶点的纹理坐标,怎么计算三角形内片元的纹理坐标呢?这就要引入重心坐标系:

重心坐标重心坐标用途

三角形内的点可以通过重心坐标(α ,β ,γ)唯一表示。以片元的中心点作为片元的坐标,可以计算出片元的纹理坐标Coord = αCoordA + βCoordB + γCoordC)。以Coord为采样坐标,从纹理数据内进行采样。

4.基本采样算法

OpenGL为我们提供了两种最基本的的纹理过滤方法GL_NEAREST和GL_LINEAR。前者表示,使用纹理中距离采样点最近的一个texel作为返回值;后者表示,使用纹理中临近的4个texel,通过加权平均算法得到返回值。

GL_NEARESTGL_LINEAR

两者效果如下:

显示效果比较

OpenGL通过下面的函数设置纹理过滤算法,用户需要在绑定纹理对象后调用下面的函数:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 5.mipmap

在介绍mipmap前,说明一下什么是LOD。LOD(Level of Details):简称为多细节层次,LOD技术根据模型的节点在显示环境中所处的位置和重要度,来决定物体渲染的资源分配,降低非重要物体的面数和细节数,从而获得高效率的渲染计算。

mipmap是LOD思想在纹理映射方面的应用。mipmap的原理是预先生成一系列以2x2为倍数缩小的多级(level)纹理,在采样纹理时会根据摄像机距离的远近,选择使用不同level的贴图。

mipmap

如果贴图的基本尺寸是256x256像素的话,它mipmap就会有8个层级。每个层级是上一层级的四分之一的大小,依次层级大小就是:128x128;64x64;32x32;16x16;8x8;4x4;2x2;1x1。

OpenGL为我们提供了两种level选择方法:MIPMAP_NEAREST和MIPMAP_LINEAR。这两种方法和上面提到的基本采样算法随机组合,产生了4种过滤算法。

mipmap算法说明GL_NEAREST_MIPMAP_NEAREST选择最邻近的 level,然后GL_NEAREST采样;GL_LINEAR_MIPMAP_NEAREST选择最邻近的 level,然后GL_LINEAR采样;GL_NEAREST_MIPMAP_LINEAR选择相邻的两个level,分别使用GL_NEAREST采样后,把结果进行进行加权平均;GL_LINEAR_MIPMAP_LINEAR选择相邻的两个level,分别使用GL_LINEAR采样后,把结果进行进行加权平均;

OpenGL通过下面的函数设置纹理过滤算法,用户需要在绑定纹理对象后调用下面的函数:

//1.生成mipmap glGenerateMipmap(GL_TEXTURE_2D); //2.设置mipmap算法 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 6.纹理环绕

前面提到,纹理坐标采用的是normalized coordinate,这意味着坐标的每个分量值都处于[0.0,1.0]之间。纹理环绕的作用是为了处理超出[0.0,1.0]范围的纹理坐标。OpenGL提供了4种纹理环绕算法:

环绕方式说明GL_REPEAT把纹理坐标进行取余运算,把运算结果作为这个点的实际纹理坐标;GL_MIRRORED_REPEAT把纹理坐标进行取余运算,把运算结果和1的差值作为这个点的实际纹理坐标;GL_CLAMP_TO_EDGE超过[0.0,1.0]范围的坐标采样纹理边界元素值;GL_CLAMP_TO_BORDER超过[0.0,1.0]范围的坐标采样用户指定值。纹理环绕

OpenGL通过下面的函数设置纹理环绕算法,用户需要在绑定纹理对象后调用下面的函数,其中GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T分别表示横轴方向和纵轴方向。

//例子1:设置水平和竖直方向的纹理环绕方式为GL_REPEAT glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); //例子2:设置纹理环绕方式为GL_TEXTURE_BORDER_COLOR float borderColor[] = {1.0f, 1.0f, 0.0f, 1.0f}; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); 7.纹理类型

OpenGL提供了6种基本纹理类型:

纹理类型区别和用途GL_TEXTURE_1D通过1维规范化坐标定位texel。GL_TEXTURE_2D通过2维规范化坐标定位texel,通常用于物体表面贴图。GL_TEXTURE_3D通过3维规范化坐标定位texel,通常用于仿真诸如雾或烟的体积效果,模拟体积3D网格,或存储动画纹理并在这些动画纹理之间平滑混合。GL_TEXTURE_RECTANGLE通过2维非规范化坐标定位texel,过时,建议不要用。GL_TEXTURE_BUFFER通过1维规范化坐标定位texel。GL_TEXTURE_CUBE_MAP通过2维规范化坐标+纹理序号定位texel,通常用于环境贴图,比如天空盒。

后3个纹理类型比较不好理解,在这里进一步介绍一下。

7.1.cube map texture

cube map texture

由6个GL_TEXTURE_2D纹理组成,每个二维纹理有唯一编号,编号类型为int,编号依次递增,每次+1:

纹理编号朝向GL_TEXTURE_CUBE_MAP_POSITIVE_XRightGL_TEXTURE_CUBE_MAP_NEGATIVE_XLeftGL_TEXTURE_CUBE_MAP_POSITIVE_YTopGL_TEXTURE_CUBE_MAP_NEGATIVE_YBottomGL_TEXTURE_CUBE_MAP_POSITIVE_ZBackGL_TEXTURE_CUBE_MAP_NEGATIVE_ZFront

OpenGL中创建并初始化cube map类型纹理对象的方式:

int width, height, nrChannels; unsigned char *data; // 把纹理依次拷贝到cube map texture的6个二维纹理中 for(unsigned int i = 0; i 纹理单元

OpenGL中纹理单元的编号格式如下GL_TEXTUREi,i处于[0,GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)之间,绑定纹理对象到纹理单元的代码如下:

//1.把纹理对象texture绑定到0号纹理单元 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); //2.把0号单元对应的纹理和shader中的textureInShader变量绑定 glUniform1i(glGetUniformLocation(program, "textureInShader"), 0); 9.优化9.1.纹理数组

OpenGL提供了3类纹理数组:

类型说明GL_TEXTURE_1D_ARRAY通过二维坐标定位GL_TEXTURE_2D_ARRAY通过三维坐标定位GL_TEXTURE_2D_MULTISAMPLE_ARRAY通过三维坐标定位

这里提一下GL_TEXTURE_2D_ARRAY和GL_TEXTURE_3D的区别,两者都通过三维坐标定位,都通过 glTexStorage3D()分配内存,区别在于:

三维纹理的z坐标处于[0,1]之间,二维纹理数组的z坐标为[0,N]之间的整数值,N是大0的整数;三维纹理生成多mipmap的时候,z方向会成倍数缩小,而二维纹理数组生成mipmap的时候,z方向值不变。

使用伪代码如下:

//1.创建纹理数组对象 int textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D_ARRAY, textureID); glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, width, height, 2); //2.加载纹理到纹理对象中 for (int i = 0; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3