纹理可以简单理解为一张图片,使用纹理可以贴在渲染的图形的可视面上。
为了能够把纹理映射(Map)到渲染的形状上,我们需要指定每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(译注:采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。
1、纹理的环绕方式:
这些选项都可以通过glTexParameteri函数单独对每个坐标轴设置: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
2、纹理的过滤:
纹理坐标不依赖于分辨率(Resolution),它可以是任意浮点值,所以OpenGL需要知道怎样将纹理像素(Texture Pixel,也叫Texel,译注1)映射到纹理坐标。当你有一个很大的物体但是纹理的分辨率很低的时候这就变得很重要了。你可能已经猜到了,OpenGL也有对于纹理过滤(Texture Filtering)的选项。纹理过滤有很多个选项,但是现在我们只讨论最重要的两种:GL_NEAREST和GL_LINEAR。
当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤的选项,比如你可以在纹理被缩小的时候使用邻近过滤,被放大时使用线性过滤。我们需要使用glTexParameter*函数为放大和缩小指定过滤方式。
3、多级渐远纹理:
在我们渲染的场景通过glEnable(GLenum(GL_DEPTH_TEST))开启深度的情况下,远处的物体会比近处的物体更小,但其纹理会拥有与近处物体同样高的分辨率。由于远处的物体可能只产生很少的片段,OpenGL从高分辨率纹理中为这些片段获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段只拾取一个纹理颜色。在小物体上这会产生不真实的感觉,更不用说对它们使用高分辨率纹理浪费内存的问题了。多级渐远纹理就是为了解决这个问题。
多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。同时,多级渐远纹理另一加分之处是它的性能非常好。
一张多级渐远纹理:
opengl通过glGenerateMipmaps 自动根据你创建的纹理生成多级渐远纹理
在渲染中切换多级渐远纹理级别(Level)时,OpenGL在两个不同级别的多级渐远纹理层之间会产生不真实的生硬边界。就像普通的纹理过滤一样,切换多级渐远纹理级别时你也可以在两个不同多级渐远纹理级别之间使用NEAREST和LINEAR过滤。为了指定不同多级渐远纹理级别之间的过滤方式,你可以使用下面四个选项中的一个代替原有的过滤方式:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
4、加载与创建纹理:
加载纹理主要是这2步:
● 为顶点绑定纹理坐标
● 将image转为二进制数据并传入GPU,并设置对应的参数(生成环绕方式、设置多级渐远纹理等)
首先我们需要确定顶点对应的纹理坐标,我们在顶点数组中添加纹理坐标信息(之前已介绍过顶点数组可以包含多种信息,包括顶点、颜色、纹理坐标)
这个顶点数组每一行的后2位就是纹理坐标。
将纹理坐标赋值到着色器中定义的纹理坐标属性中:
此时,size = 2, 步长 = 5*sizeof(float)偏移为 sizeof(float)*3。 我们成功将顶点坐标与纹理坐标关联起来了。
除了纹理坐标(顶点对应取样的位置),我们还需要告诉GPU纹理的数据,下面看我封装的加载纹理的源码:
● 首先第一步,将纹理转换为二进制数据
● 创建并绑定纹理:其实当只有一个纹理时,可以直接调用glBindTexture(GLenum(GL_TEXTURE_2D), 0)绑定纹理就好了,因为有一个默认激活的纹理单元03
● 设置环绕、过滤方式
● glTexImage2D载入纹理:
/*
参数1:纹理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:加载的层次,一般设置为0
参数3:纹理的颜色值GL_RGBA
参数4:宽
参数5:高
参数6:border,边界宽度
参数7:参数应该总是被设为0(历史遗留的问题)
参数8:type
参数9:纹理数据
*/
glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, GLsizei(width), GLsizei(height), 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE),spriteData)
● 如果开启了深度的话,调用glGenerateMipmap自动生成多级渐远纹理
● .释放内存
5、纹理单元
上述情况是一个渲染对应一个纹理单元,当一个渲染需要用到多个纹理单元时,可以使用glUniform1i函数给多个纹理单元赋值,这样的话我们能够在一个片段着色器中设置多个纹理。一个纹理的位置值通常称为一个纹理单元(Texture Unit)。一个纹理的默认纹理单元是0,它是默认的激活纹理单元。
如何使用多个纹理:
var textureID1 = GLuint()
glGenTextures(1, &textureID1)
glActiveTexture(tex)
glBindTexture(GLenum(GL_TEXTURE_2D), textureID1)
var textureID2 = GLuint()
glGenTextures(1, &textureID2)
glActiveTexture(tex)
glBindTexture(GLenum(GL_TEXTURE_2D), textureID2)
在着色器中定义多个纹理对象,
使用glUniform1i给每个纹理单元赋值
这样,就可以在一个纹理中使用多个纹理单元,看起来是这样:
小tip:我们当前没有设置投影矩阵,所以,图片看起来会是上下颠倒的,这是因为我们的屏幕坐标和纹理坐标在Y轴上是相反的。所以,需要反转一下图片:
//2.图片反转2
spriteContext?.translateBy(x: 0, y: CGFloat(height))//向下平移图片的高度
spriteContext?.scaleBy(x: 1, y: -1)//反转图片