1、 深入分析Cocos2d-x 2.0中的“纹理” 另:本章所用Cocos2d-x版本为: cocos2d-2.0-x-2.0.2 @ Aug 30 2012 http://cn.cocos2d-x.org/download 大家好,最近工作实在是太忙了,公司项目这个月要进行对外测试,大伙都是忙的昏天黑地的,每天很少有时间写新博文,但每天看到博文的回复和排名,心中很是感谢,正是因为各位的支持才能让我保持旺盛的战斗力。当然,在此还要感谢一下亲爱的老婆大人对我的支持,小乖每天闹困时主动的哄小乖安睡,否则咱咋能有时间写这些文字,小乖一岁半了,长得非常可爱,虽然在京
2、仍然漂着,仍然买不起房,仍然只是一个程序员,但是我知道我已经得到了人生中最幸福的东西。 好了,感叹之后,今天我们来学习Cocos2d-x中的“纹理”。之前有几篇文章都是谈及图片方面的,也是我刻意的安排: (1)。如何利用Cocos2d-x开发一款游戏?(2)。游戏开发之图片元素。(3)。Cocos2d-x中图字原理之深入分析。(4)。红孩儿纹理打包器。(5)。CCImage深入分析。(6)。词典类CCDictionary深入分析。 在第一篇里我首先提到了“图元素的管理工具”。这是为什么呢?因为2D游戏画面是由图片构成的。了解好图片从美术绘制到加载到游
3、戏以及使用和释放的整个过程是非常重要的,他关系着游戏的运行效率,内存占用等重要关键问题。有一个好的方案指导工作流程对于项目的成功是一项重要的保证。所以,请各位好好管理你们的图片资源。在第2篇里对于图片拼合的用途和意义做了启蒙。第3篇里我们深入了解了写汉字时Cocos2d-x是怎么使用图片来进行绘制的,它里面CCTextureAtlas和CCSpriteBatchNode 两个类告诉我们图片拼合原理与实现。而第4篇介绍了我开发的一款图片拼合工具。随后我会更新为最新版提供给大家使用。新增了PLIST方式导出和自动进行边缘空白裁剪的功能,免费而更强大!欢迎大家到下载。而后面两篇,其实是为了引出今天的
4、文章而做的铺垫。 本博为什么要花这么多时间在纹理上呢?因为本博一次再一次的看到很多的Cocos2d开发者在说:“为什么我的游戏占内存这么大?”。“为什么我的游戏跑起来效率这么低?”。其实这里面最根本的问题是你思想上没有对资源进行优化的一种流程在里面。在进行游戏开发的过程中,你被美术牵着鼻子跑,你没有自然而然的想到该怎么进行图片的格式,尺寸,拼合,复用方式,以及绘制时的方式。这些因素对游戏产生的具体影响在你的脑子里还没有留下完整的概念。所以,我希望经过对于这些源码的分析,让大家去了解这些因素,从而学会思考如何优化自已的游戏。好了,大家先将今天关于纹理代码的分析博文学习
5、完,下一篇我讲对于这些优化因素做一个具体的讲解。 当一张图片被加载到内存后,它是以纹理的形式存在的。纹理是什么东西呢?纹理就是一块内存,这块内存中存放的是按照指定的像素格式填充的图片像素信息。它被最终作为三角面着色所依据的数据源。 我们来看一下cocos2d-x中的libcocos2d库,其下有许多目录,找到textures展开,可以看到有CCTexture2D,CCTextureAtlas,CCTextureCache,CCTexturePVR四个类。 这四个类的功能分别是: CCTexture2D: 纹理,即图片加载入内
6、存后供CPU和GPU操作的贴图对象。 CCTexturePVR:处理PVR文件生成纹理的类,提示: 大家可以用它解析愤怒的小鸟中的图片。 CCTextureCache:纹理管理器,负责加载图片并对生成的纹理进行管理。通过“字典”来进行快速的查询。 CCTextureAtlas:纹理块管理器,如果图片是由多个小图块组成的,则纹理块管理器用来存储这些小图块的相关信息,以方便绘制相应图块。 为了让大家更好的学习纹理,在讲解纹理的代码之前我已经先给大家分析了本章用到的两个功能类: CCImage和CCDictionary。这两个类分别在纹理模块中担任加载图片和管理
7、纹理指针的作用。希望大家先顶一下这两篇贴子之后再开始下面的代码学习,你一定会感到非常容易。
一.CCTexture2D:
好,咱们现在开始看CCTexture2D:
[cpp] view plaincopy
1. #ifndef __CCTEXTURE2D_H__
2. #define __CCTEXTURE2D_H__
3.
4. #include
8、8. 9. //Cocos2d命名空间 10. NS_CC_BEGIN 11. //需要用到CCImage,这里声明一下。 12. class CCImage; 13. 14. //纹理格式:即每个纹理中的像素单位分别是怎么为颜色值进行实际内存分配的。这个非常重要,我们在进行游戏开发的过程中,会常常与各种图片类型打交通。每种图片往往也有各自的像素格式。但当它们一旦加载到游戏中后,就会根据我们的要求变成以下某种类型的纹理。不同的纹理格式所占据的内存大小可能不同,我们要根据实际情况和需求来选择相应的纹理格式。比如我们用RGBA8888纹理格式来创建纹理,它占
9、据的内存容量很大,如果我们要显示的纹理中没有ALPHA值,那就不应该使用带ALPHA通道的纹理格式。我们就可以改成RGB565像素格式。 15. 16. typedef enum { 17. 18. //32位真彩色,最真但最耗内存 19. kCCTexture2DPixelFormat_RGBA8888, 20. //24位真彩色,去掉了ALPHA通道 21. kCCTexture2DPixelFormat_RGB888, 22. //16位色,将RGB压缩在一个字中。绿色多了1位,因为人眼对绿色更敏
10、感。 23. kCCTexture2DPixelFormat_RGB565, 24. //8位色,只存ALPHA值,做遮罩图用 25. kCCTexture2DPixelFormat_A8, 26. //8位色,只存灰度或者强度值,做灰度图用 27. kCCTexture2DPixelFormat_I8, 28. //16位色,只存ALPHA值与强度值,双功能 29. kCCTexture2DPixelFormat_AI88, 30. //16位色,RGBA四通道各占4位。 3
11、1. kCCTexture2DPixelFormat_RGBA4444, 32. //16位色,RGB三通道各占5位,多1位留做ALPHA镂空使用 33. kCCTexture2DPixelFormat_RGB5A1, 34. // PVR的PVRTC4压缩格式 35. kCCTexture2DPixelFormat_PVRTC4, 36. // PVRTC的PVRTC2压缩格式 37. kCCTexture2DPixelFormat_PVRTC2, 38. 39. //默
12、认格式RGBA8888 40. kCCTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_RGBA8888, 41. 42. // 为了兼容性而保留的枚举值 43. kTexture2DPixelFormat_RGBA8888 = kCCTexture2DPixelFormat_RGBA8888, 44. kTexture2DPixelFormat_RGB888 = kCCTexture2DPixelFormat_RGB888, 45. kTexture2DP
13、ixelFormat_RGB565 = kCCTexture2DPixelFormat_RGB565, 46. kTexture2DPixelFormat_A8 = kCCTexture2DPixelFormat_A8, 47. kTexture2DPixelFormat_RGBA4444 = kCCTexture2DPixelFormat_RGBA4444, 48. kTexture2DPixelFormat_RGB5A1 = kCCTexture2DPixelFormat_RGB5A1, 49. kTexture2DPixelForm
14、at_Default = kCCTexture2DPixelFormat_Default 50. 51. } CCTexture2DPixelFormat; 52. 53. //需要使用Shader代码片段,这里声明一下 54. class CCGLProgram; 55. 56. //定义了纹理的一些参数 57. typedef struct _ccTexParams { 58. GLuint minFilter;//纹理过滤器:缩小过滤器 59. GLuint magFilter;//纹理过滤器:
15、放大过滤器 60. GLuint wrapS;//横向纹理寻址模式 61. GLuint wrapT;//纵向纹理寻址模式 62. } ccTexParams; 63. 64. 65. // CCTexture2D类可以方便的从图片,文本或raw数据文件中创建OpenGL所用贴图,创建的贴图会自动转为2的幂次方大小,所以要注意对于贴图坐标的影响。 66. 67. class CC_DLL CCTexture2D : public CCObject 68. { 69. public: 70.
16、 //构造 71. CCTexture2D(); 72. //析构 73. virtual ~CCTexture2D(); 74. //取得纹理的描述 75. const char* description(void); 76. 77. //释放数据 78. void releaseData(void *data); 79. //保存数据 80. void* keepData(void *data, unsigned int length); 81. 82. //由数
17、据指针和指定的像素格式,图片宽高,来生成OpenGL贴图。 83. bool initWithData(const void* data, CCTexture2DPixelFormat pixelFormat, unsigned int pixelsWide, unsigned int pixelsHigh, const CCSize& contentSize); 84. 85. //在指定的位置绘制贴图 86. void drawAtPoint(const CCPoint& point); 87. //纹制贴图上的一个图像块 88. vo
18、id drawInRect(const CCRect& rect); 89. 90. //由CCImage指针生成OpenGL贴图 91. bool initWithImage(CCImage * uiImage); 92. 93. //由一个字符串生成OpenGL贴图。 94. bool initWithString(const char *text, const CCSize& dimensions, CCTextAlignment hAlignment, CCVerticalTextAlignment vAlignm
19、ent, const char *fontName, float fontSize); 95. //由一个字符串和指定的字体与大小生成OpenGL贴图 96. bool initWithString(const char *text, const char *fontName, float fontSize); 97. 98. //如果支持PVR的压缩格式 99. #ifdef CC_SUPPORT_PVRTC 100. //由一个PVR压缩格式的数据生成OpenGL贴图 101. bool initWithP
20、VRTCData(const void *data, int level, int bpp, bool hasAlpha, int length, CCTexture2DPixelFormat pixelFormat); 102. #endif // CC_SUPPORT_PVRTC 103. 104. //从普通PVR文件生成OpenGL贴图 105. bool initWithPVRFile(const char* file); 106. 107. //设置贴图参数 108. void setTexPara
21、meters(ccTexParams* texParams); 109. 110. //设置为抗锯齿的贴图过滤方式(线性过滤) 111. void setAntiAliasTexParameters(); 112. 113. //设置为非抗锯齿的贴图过滤方式(最近点采样) 114. void setAliasTexParameters(); 115. 116. 117. //生成多级贴图: 由图片数据生成一系列尺寸为2的幂次方直至当前贴图大小的贴图。系统会根据距离自动选择纹理图片。可以解决大图
22、片显示在小空间时的闪烁问题。 118. void generateMipmap(); 119. 120. //取得像素格式名称 121. const char* stringForFormat(); 122. 123. //返回当前贴图色深,即每个像素占多少位 124. unsigned int bitsPerPixelForFormat(); 125. 126. //通过参数贴图格式返回纹理色深 127. unsigned int bitsPerPixelForFormat(CCTe
23、xture2DPixelFormat format); 128. 129. 130. //静态函数,用于设置默认带ALPHA通道的贴图像素格式。则图片创建为贴图时,如果有ALPHA通道,则生成此默认贴图像素格式。 131. static void setDefaultAlphaPixelFormat(CCTexture2DPixelFormat format); 132. 133. //静态函数,取得默认带ALPHA通道的贴图像素格式。 134. static CCTexture2DPixelFormat defa
24、ultAlphaPixelFormat(); 135. 136. //静态函数,设置载入PVR时是否开启ALPHA渐变,默认不开启,则ALPHA值只有是与否,无渐变。 137. static void PVRImagesHavePremultipliedAlpha(bool haveAlphaPremultiplied); 138. 139. //取得图片大小(以像素为单位) 140. const CCSize& getContentSizeInPixels(); 141. //是否有ALPHA渐变值
25、142. bool hasPremultipliedAlpha(); 143. //是否有多级贴图 144. bool hasMipmaps(); 145. private: 146. //加载一个带ALPHA渐变的图片生成OpenGL贴图 147. bool initPremultipliedATextureWithImage(CCImage * image, unsigned int pixelsWide, unsigned int pixelsHigh); 148. 149. //ALPHA渐变开关
26、 150. bool m_bPVRHaveAlphaPremultiplied; 151. 152. //贴图格式变量及get接口 153. CC_PROPERTY_READONLY(CCTexture2DPixelFormat, m_ePixelFormat, PixelFormat) 154. //贴图宽度及get接口 155. CC_PROPERTY_READONLY(unsigned int, m_uPixelsWide, PixelsWide) 156. //贴图高度及get接口 157.
27、 CC_PROPERTY_READONLY(unsigned int, m_uPixelsHigh, PixelsHigh) 158. 159. //OpenGL贴图索引及get接口 160. CC_PROPERTY_READONLY(GLuint, m_uName, Name) 161. 162. //横向贴图坐标终点。因为图片如果不是2的幂次方,图片大小会小于贴图的大小,贴图一定是2的幂次方嘛,这时候横向的贴图坐标终点不是1.0。 163. CC_PROPERTY(GLfloat, m_fMaxS, MaxS)
28、 164. //纵向贴图坐标终点。 165. CC_PROPERTY(GLfloat, m_fMaxT, MaxT) 166. //图片大小及get接口 167. CC_PROPERTY_READONLY(CCSize, m_tContentSize, ContentSize) 168. 169. // ALPHA渐变开关 170. bool m_bHasPremultipliedAlpha; 171. // 多级纹理开关 172. bool m_bHasMipmaps;
29、173. 174. //Shader代码片段指针 175. CC_PROPERTY(CCGLProgram*, m_pShaderProgram, ShaderProgram); 176. }; 177. 178. NS_CC_END 179. 180. #endif //__CCTEXTURE2D_H__ 再来看CCTexture2D.cpp: [cpp] view plaincopy 1. #include "CCTexture2D.h" 2. #include "ccConfig.h"
30、3. #include "ccMacros.h" 4. #include "CCConfiguration.h" 5. #include "platform/platform.h" 6. #include "platform/CCImage.h" 7. #include "CCGL.h" 8. #include "support/ccUtils.h" 9. #include "platform/CCPlatformMacros.h" 10. #include "textures/CCTexturePVR.h" 11. #include "CCDi
31、rector.h" 12. #include "shaders/CCGLProgram.h" 13. #include "shaders/ccGLStateCache.h" 14. #include "shaders/CCShaderCache.h" 15. //这里定义是否使用可变纹理 16. #if CC_ENABLE_CACHE_TEXTURE_DATA 17. #include "CCTextureCache.h" 18. #endif 19. //Cocos2d-x命名空间 20. NS_CC_BEGIN 21.
32、 22. 23. //静态全局的默认贴图像素格式。缺省为kCCTexture2DPixelFormat_Default,即RGBA8888。 24. static CCTexture2DPixelFormat g_defaultAlphaPixelFormat = kCCTexture2DPixelFormat_Default; 25. 26. //静态全局的PVR是否有ALPHA渐变的开关变量,默认为否。 27. static bool PVRHaveAlphaPremultiplied_ = false; 28. 29. //构造函数。
33、 30. CCTexture2D::CCTexture2D() 31. : m_uPixelsWide(0) 32. , m_uPixelsHigh(0) 33. , m_uName(0) 34. , m_fMaxS(0.0) 35. , m_fMaxT(0.0) 36. , m_bHasPremultipliedAlpha(false) 37. , m_bHasMipmaps(false) 38. , m_bPVRHaveAlphaPremultiplied(true) 39. , m_pShaderProgram(NULL)
34、40. { 41. } 42. //析构 43. CCTexture2D::~CCTexture2D() 44. { 45. //如果使用可变纹理,删除此可变纹理中的数据。 46. #if CC_ENABLE_CACHE_TEXTURE_DATA 47. VolatileTexture::removeTexture(this); 48. #endif 49. //打印日志。 50. CCLOGINFO("cocos2d: deallocing CCTexture2D %u.", m_uName); 51. //释放所用到
35、的Shader代码片段 52. CC_SAFE_RELEASE(m_pShaderProgram); 53. //释放OpenGL所用到的贴图。 54. if(m_uName) 55. { 56. ccGLDeleteTexture(m_uName); 57. } 58. } 59. //取得当前纹理的贴图像素格式。 60. CCTexture2DPixelFormat CCTexture2D::getPixelFormat() 61. { 62. return m_ePi
36、xelFormat; 63. } 64. //取得贴图宽度。 65. unsigned int CCTexture2D::getPixelsWide() 66. { 67. return m_uPixelsWide; 68. } 69. //取得贴图高度。 70. unsigned int CCTexture2D::getPixelsHigh() 71. { 72. return m_uPixelsHigh; 73. } 74. //取得贴图索引。 75. GLuint CCTexture2D::ge
37、tName() 76. { 77. return m_uName; 78. } 79. //取得图片大小(以点为单位) 80. CCSize CCTexture2D::getContentSize() 81. { 82. // CC_CONTENT_SCALE_FACTOR宏返回的是在不同屏幕下的点与像素的比率。Mac电脑上返回1.而使用Retina显示屏的iphone上返回2。 83. CCSize ret; 84. ret.width = m_tContentSize.width / CC_CONTENT_SCA
38、LE_FACTOR(); 85. ret.height = m_tContentSize.height / CC_CONTENT_SCALE_FACTOR(); 86. 87. return ret; 88. } 89. //取得图片大小(以像素为单位) 90. const CCSize& CCTexture2D::getContentSizeInPixels() 91. { 92. return m_tContentSize; 93. } 94. //取得横向的贴图坐标终点 95. GLflo
39、at CCTexture2D::getMaxS() 96. { 97. return m_fMaxS; 98. } 99. //设置横向的贴图坐标终点 100. void CCTexture2D::setMaxS(GLfloat maxS) 101. { 102. m_fMaxS = maxS; 103. } 104. //取得纵向的贴图坐标终点 105. GLfloat CCTexture2D::getMaxT() 106. { 107. return m_fMaxT; 108. }
40、109. //设置纵向的贴图坐标终点 110. void CCTexture2D::setMaxT(GLfloat maxT) 111. { 112. m_fMaxT = maxT; 113. } 114. //所用到的Shader代码片段。 115. CCGLProgram* CCTexture2D::getShaderProgram(void) 116. { 117. return m_pShaderProgram; 118. } 119. //设置用到的Shader代码片段。 120. void CCTex
41、ture2D::setShaderProgram(CCGLProgram* pShaderProgram) 121. { 122. CC_SAFE_RETAIN(pShaderProgram); 123. CC_SAFE_RELEASE(m_pShaderProgram); 124. m_pShaderProgram = pShaderProgram; 125. } 126. //释放数据 127. void CCTexture2D::releaseData(void *data) 128. { 129. f
42、ree(data); 130. } 131. //保存数据 132. void* CCTexture2D::keepData(void *data, unsigned int length) 133. { 134. //这里只是使用CC_UNUSED_PARAM宏用一下length,没什么实际功能。作者给出这个函数是预备未来供子类重载。 135. CC_UNUSED_PARAM(length); 136. return data; 137. } 138. //是否有ALPHA渐变的通道数据。 139. bool CCT
43、exture2D::hasPremultipliedAlpha() 140. { 141. return m_bHasPremultipliedAlpha; 142. } 143. //由数据指针创建指定大小和格式的贴图。取得创建成功后图片在贴图中的实际区域 。 144. bool CCTexture2D::initWithData(const void *data, CCTexture2DPixelFormat pixelFormat, unsigned int pixelsWide, unsigned int pixelsHigh, const CCS
44、ize& contentSize) 145. { 146. //如果是RGBA8888格式或者大小正好就是2的幂次方。像素数据按四字节(DWORD)对齐。否则按1字节(BYTE)进行对齐。 147. if( pixelFormat == kCCTexture2DPixelFormat_RGBA8888 || ( ccNextPOT(pixelsWide)==pixelsWide && ccNextPOT(pixelsHigh)==pixelsHigh) ) 148. { 149. glPixelStorei(GL_UNPACK_AL
45、IGNMENT,4); 150. } 151. else 152. { 153. glPixelStorei(GL_UNPACK_ALIGNMENT,1); 154. } 155. //产生一个OpenGL的贴图索引。 156. glGenTextures(1, &m_uName); 157. //将此贴图绑定为GL_TEXTURE_2D纹理。 158. ccGLBindTexture2D(m_uName); 159. //设置OpenGL中的贴图的过滤参数。 160.
46、 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); 161. glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 162. //设置贴图的横向纹理寻址模式为边缘截取模式。总是忽略边界。 163. glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); 164. //设置贴图的纵向纹理寻址模式为边缘截取模式。总是忽略
47、边界。 165. glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); 166. 167. //这里根据不同的像素格式来生成不同的OpenGL所用的贴图。注意:传入的宽和高在成功生成贴图后会返回实际贴图的宽和高。如果图片不是2的幂次方,这个数值会改成2的幂次方。比如你传入的图片宽高是148x245,则调用完成后宽高会转成256x256。 168. switch(pixelFormat) 169. { 170. case kCCTextur
48、e2DPixelFormat_RGBA8888: 171. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 172. break; 173. case kCCTexture2DPixelFormat_RGB888: 174. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)pixe
49、lsWide, (GLsizei)pixelsHigh, 0, GL_RGB, GL_UNSIGNED_BYTE, data); 175. break; 176. case kCCTexture2DPixelFormat_RGBA4444: 177. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); 178. b
50、reak; 179. case kCCTexture2DPixelFormat_RGB5A1: 180. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data); 181. break; 182. case kCCTexture2DPixelFormat_RGB565: 183. glTexImage2D(






