我所理解的cocos2dx - 纹理(上)

  3d图形渲染最重要就是把纹理贴到物体表面,这过程主要发生在着色器工作阶段,使用光栅化阶段插值计算得出纹理坐标从纹理中采样,然后对片段着色,可以处理丰富特效,光照阴影等。

 

光栅化

 作用是将2d图元转为帧缓冲的整数坐标的片段,每个片段包括颜色深度和模版值。首先确认视窗上哪些整数位置的片段会被图元覆盖,然后对图元进行插值计算。这些信息会被送入后续的阶段进行处理,最后结果用于更新帧缓冲上的该位置信息。片段的颜色由片段着色器决定,光栅化会生成一些易变变量。

  多重采样:高分辨率信号以低分辨率表示无法准确运算出3d图形坐标时导致的图形混叠,具体就是锯齿的产生。多重采样就是解决这问题,它是通过以采样点为中心位置,对附近进行采样,共同决定这个点的颜色。多重采样的数据存储在帧缓冲额外的多重采样缓冲区。cocos2dx默认没有开多重采样,要手动设置CCEAGLView为yes开启,并且只能初始化的时候开启。

  纹理坐标:纹理以左下角为原点,一个顶点在纹理中坐标通常使用uv,长宽是纹理的长宽,这是给客户端程序使用的。片段着色器使用坐标st,范围为0-1,这一规化过程在光栅化阶段完成。

 

像素矩阵

  定义:二维像素数组,表示区域内的颜色,深度或者模版值。

  像素矩阵从内存传输到服务端的过程叫解包,从服务端读取到内存叫压包。

  像素存储模式:纹理像素编码用void PixelStorei(enum pname, T param)设置,会影响到像素矩阵数据传输相关指令,初始值4,合法值1248

  纹理数据的传输:客户端->服务端,输入是像素数据,输出是0-1的rgba像素值,在操作的命令中,都会包含一些基本参数:

    format:一个像素矩阵中数据的构成

    data:ubyte或者ushot数组,数组中的元素被按照1,2,3或者4个分量形成一个组

    也就是说,format决定data的分量意义和顺序。最后那个是亮度?

    type:表示每个像素分量的构成(format),以及data数组中每个元素的数据类型(data里数据组成)代表上图每一个分享的大小,565说明是5红5蓝6绿的大小占位,人眼对绿色敏感。

  解包:format描述像素矩阵的数据类型,type决定每个分量的构成,所以所有编码格式的像素矩阵的数量由format和type共同决定。其所有组合方式如下:byte占据一个byte,ushort占据2个。像素矩阵数据的传输,应该适当的选择内存中数据的对齐方式(c++里也有类似的思想),提升数据传输的性能。这里只是简单的对齐,会由8-4-2-1方式进行检查,哪个满足用哪个对齐,其参数为UNPACK_ALIGNMENT。

  unsigned_short_4444,5551,565会被包装成一个ushort,2个byte,16位,数字代表每个分量的构成。例如5551就是rgb和1位alpha,要么透明套么不透明,适合用于字体或者蒙板。

  所有类型,格式的数据被解包之后,他们的byte或者short会被转为浮点型,实际上是执行了归一化计算[-1,1],之后还需要转为rgb格式,主要针对的是LUMINANCE和LUMINANCE_ALPHA,luminance会转化为rgb3个分量。所以最后,每个组都被转化为4个分量。一个像素矩阵就被解包为一个颜色值上传到gl缓冲区。

 

 客户端图像格式

  服务端上为了渲染性能,纹理数据都是没压缩的,而客户端为了节省占用空间,一般进行了压缩,所以在传输前,需要解压图像,转为服务端可支持的格式。

  纹理格式的对应关系:

  图像数据格式转换:客户端需要把不同的图片格式转换成对应的客户端格式,通过convertDataToFormat实现,默认为auto,auto会尝试转为最接近的格式。实际上默认为rgba8888,如果我们不需要如此高质量的图像,可以设置为其他,玩家也看不出。

 

纹理对象和加载纹理

  纹理在片段着色器被使用。

  纹理对象是一个容器,拥有该纹理所需的所有数据,包括像素数据,过滤模式,扩展模式等。

  glGenTextures(GLsizei n, GLuint *textures)创建纹理,n表示创建纹理对象的数量,textures用于保存分配的纹理名称。

  glDeleteTetures(同上)删除

  glBindTeture(GLenum target, GLuint texture)为了操作纹理,需要绑定一个当前操作的纹理,因为opengl不持有纹理的指针。

  glTexImage2D(GLenum target, GLint level, GLenum internalFormat, width, height, border, format, type, pixels)target为纹理的一个面,level为多级纹理的级别,internalFormat为纹理在GL的存储格式,wh尺寸,format表示客户端图像数据的构成和顺序,type是gl中纹理的格式和数据类型,pixels表示客户端的图像数据缓冲对象。

  纹理一旦传输到gl服务端,就会一致驻留在gpu,所以不用时记得删掉。

 

纹理单元和多重纹理

  opengl 支持一个管线中使用多个纹理,使用纹理单元管理多个纹理的使用,每一个纹理对象都被放到一个纹理单元中,用glActiveTexture来激活纹理单元。激活实际上就是设置当前纹理单元,这样后续的命令可以将纹理绑定到这个单元上,通过宾得TextureDN来绑定纹理到纹理单元上。

 

纹理缩放

  纹理被检测到缩放的时候,opengl会使用TEXTURE_MIN_FILTER或者TEXTURE_MAG_FILTER进行纹素过滤。

  纹理缩小:多个紋素放到一个像素点去,将用texture_min_filiter来决定紋素的选择,有2个值:

    GL_NEAREST:选择离坐标中心最近的紋素,速度快,导致严重失真

    GL_LINEAR:选择坐标中心附近一个2*2的区域,进行双线性插值计算,得出一个合理的颜色值,损失性能,每一帧都进行,效果平滑

  纹理放大:一般一个紋素用到多个像素点去,同上,nearest最近点取样,linear4个点求平均。

 

cocos2dx中设置过滤模式

  设置过滤模式的方法有:

    setTexParamters(const TexParam& texParams);  //各种缩放纹理过滤模式,还能设置纹理的重复模式,来决定纹理超出尺寸的采样行为

    setAntiAliansTexParameters();  //锯齿

    setAliasTexParmeters();  //反锯齿

 

多级纹理

  就预设好很多个不同size的图片,后续进行选择,其TEXTURE_MIN_FILTER新增4种模式

    GL_NEAREST_MIPMAP_NEAREST:选择最近级别的纹理进行最近点采样

    GL_NEAREST_MIPMAP_LINEAR:选择最近2个级别的纹理进行最近点采样,然后取线性插值

    GL_LINEAR_MIPMAP_NEAREST:选择最近级别的纹理进行双线性采样

    GL_LINEAR_MIPMAP_LINEAR:选择最近2个级别的纹理进行最近点采样,然后双线性采样,再取2个采样的线性插值

  多级纹理的上传:level参数决定纹理的级别,一般最大尺寸为0。可以使用initWithMipmaps来上传多级纹理。这里可以针对低分辨率的设备避免上传过大的纹理,节省内存

  多级纹理的生成

    glGenerateMipMap()生成,需要手动调用,但每次上传纹理需要执行,有一定性能消耗,但可减少内存占用。

    使用工具,pvrtextool之类

 

纹理压缩

  纹理需要传进gpu里,转为rgb或者rgba来处理,占用大量内存。

  压缩纹理的特点:

    1.解压速度:需要有较快的解压速度

    2.随机读取:有时候只需要纹理的一部分,就需要随机读取的能力

    3.压缩率和图像质量:一般为有损压缩

    4.编码速度:没太大要求,因为一般发生在程序外

  压缩纹理的实现:传统图像压缩使用可变的压缩比率,导致读某位置需要解压更多位置,不利于随机读取。压缩纹理用固定压缩比率,按比率分成多个像素块,每一个2*2或者4*4,然后对一块进行压缩,存在一个像素集合中(codebook),一块索引图(Index Map)中。读取时根据索引找个像素块,解压,读取偏移值信息,称为基于块的压缩算法。

  pvrtc 和 pvrtc2:ios全产品和安卓大部分都支持这2个压缩格式。

  pvrtc只支持pot纹理,网上说这个pot纹理在苹果里有个bug,会导致内存占用增多,大家都希望用npot。而pvrtc2就是增强版,支持了npot,增强画质,支持alpha预乘。两种都支持2bpp和4bpp的压缩比率。

  etc,几乎所有安卓都支持这个。

  支持4bpp,不支持alpha通道。有几个方式让他支持:
    1.将alpha转为可见的灰度图像,拼接在一起,使得纹理高度为原来的2倍。缺点是纹理尺寸变大,限制原纹理的尺寸,有的不支持那么大

    2.alpha通道单独生成一个纹理,用多重纹理技术绑定一起,后面混合即可。

posted on 2017-06-19 20:39  usp10  阅读(2569)  评论(0编辑  收藏  举报

导航