纹理(Texture)就是图片,它用来给物体增加细节,cocos2d-x中使用Texture2D类处理2D纹理贴图,本篇就从cocos2d-x中的Texture2D类介绍openGL纹理。
首先介绍纹理坐标的概念,2D纹理是一个图像数据的二维数组。用2D纹理渲染时,纹理坐标用作纹理图像中的索引。一般来说,在3D内容创作程序中将制作一个网格,每个顶点都有一个纹理坐标。2D纹理的纹理坐标是用一对2D坐标(s,t)指定,有时也称作(u,v)坐标。这些坐标代表用于查找一个纹理贴图的规范化坐标,纹理图像的左下角由纹理坐标(0,0)指定,右上角由纹理坐标(1,1)指定,纹理坐标指明纹理图像的哪个地方采样。之后再所有的其他的像素上进行像素插值。
处理贴图的代码如下:
//生成贴图,并绑定
glGenTextures(1, &_name);
GL::bindTexture2D(_name);
//纹理滤过方式
if (mipmapsNum == 1)
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _antialiasEnabled ? GL_LINEAR : GL_NEAREST);
}else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _antialiasEnabled ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST);
}
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _antialiasEnabled ? GL_LINEAR : GL_NEAREST );
//放置方式
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
首先是生成贴图和绑定。纹理对象是一个容器对象,保存渲染所需的纹理数据,例如图像数据、过滤模式和包装模式。在openGL中,纹理对象是一个无符号整数表示,该整数对象是个句柄,在创建的时候,glGenTextures生成的纹理对象是一个空的容器,用于加载纹理数据和参数。纹理对象在应用程序不再需要它们的时候要删除,使用glDeleteTexures实现。
if(_name != 0)
{
GL::deleteTexture(_name);
_name = 0;
}
然后是绑定纹理,一旦纹理绑定到一个特定纹理目标,纹理对象在删除之前就一直绑定在它的目标。
纹理坐标不依赖解析度,它可以是任何浮点数值,这样openGL需要描述哪个纹理图素对应哪个纹理坐标,openGL也有一个叫做纹理过滤的选项。有多种不同的选项,这里介绍几种:
GL_NEAREST:从最靠近纹理坐标的纹理中获得一个单点样本。会选择最接近纹理坐标中心点的那个像素。
GL_LINEAR:从最靠近纹理坐标的纹理中获得一个双线性样本。他会从纹理坐标的临近纹理像素进行插值,返回一个多个纹理像素的近似值。一个纹理像素距离纹理坐标越近,最终采样颜色越接近。
另外几个过滤选项需要介绍一个mipmap的概念,mipmap的思路是构建一个图像链。mipmap贴图链始于原来指定的图像没后续的每个图像在每个维度上是前一个图像的一半,一直持续到最后达到1*1的纹理,如果图片要发生缩小而且你会担心性能,那么使用mipmap是大部分硬件上的最佳选择
GL_NEAREST_MIPMAP_NEAREST:接收最近的mipmap级别,并使用线性采样。
GL_NEAREST_MIPMAP_LINEAR:在两个mipmap之间进行线性插值,通过近邻插值采样。
GL_LINEAR_MIPMAP_NEAREST:接收最近的mipmap级别,并使用线性插值采样。
GL_LINEAR_MIPMAP_LINEAR:在两个相邻的mipmap进行线性插值,并使用线性插值采样。
左边的采用近邻过滤,右边的采用线性过滤,可以看出,线性过滤更平滑。GL_TEXTURE_MIN_FILTER是设置图片缩小的过滤方式,GL_TEXTURE_MAG_FILTER设置放大的过滤方式。
在openGL 3.0中,立方体过滤是无缝的,也就是说过滤核心跨越立方图不止一个面,核心将会从其覆盖的每个面中获得样本。无缝过滤在各个面形成了边缘形成了更平滑的过滤。
纹理放置模式用于指定纹理超出范围时所发生的行为,openGL es中提供了三种:
GL_ REPEAT:重复纹理
GL_MIRRORED_ REPEAT:镜像重复
GL_CLAMP_TO_EDGE:边缘拉伸
三种不同的效果如下:
接下来就是生成纹理,使用glTexImage2D函数:
glTexImage2D(GL_TEXTURE_2D, i, info.internalFormat, (GLsizei)width, (GLsizei)height, 0, info.format, info.type, data);
第一个参数是纹理目标,2D纹理采用GL_TEXTURE_2D
第二个参数是mipmap的纹理级别,第一个是0,后续递增。
第三个参数是纹理存储格式,一般有GL_RGB,GL_RGBA等等
第四个和第五个是纹理宽高。
第六个参数是为了与openGL兼容的参数,忽略为0即可。
第七个参数是纹理数据格式,一般有GL_RGB,GL_RGBA等等
第八个参数是数据格式,GL_INT,GL_FLOAT等
第九个参数是实际数据,从图片文件到实际数据其实是个复杂的过程,有时我们会采用第三方库来完成,因为图片的格式非常多,处理起来非常麻烦,cocos2d-x在Image类中处理这个事情,总之,到这里,我们可以拿到图片数据,并且把它传入进来。
接下来就是激活并使用纹理。
glActiveTexture(GL_TEXTURE0 + textureUnit);
glBindTexture(GL_TEXTURE_2D, textureId);
这段代码在ccGLStateCache.cpp中,这里有一个纹理单元的概念。一个纹理的位置成为纹理单元,默认激活的纹理单元位置是0,纹理单元的主要目的是让我们在着色器中使用多个纹理,通过激活纹理单元,我们可以给纹理单元绑定相应的纹理,它就像是一个停车位一样,openGL至少提供16个纹理单元可以使用。
通过glActiveTexture来设置当前纹理单元,然后使用glBindTexture来给纹理单元绑定纹理,这样纹理就会被使用了
能力不足,水平有限,如有错误,欢迎指出。