Cocos2d-x 2.0 渲染到紋理深入分析

你的財神爺發表於2017-12-07

       現在我們將重新認識一下RenderTexture。在TestCpp中它做為獨立的演示場景存在,名稱為RenderTextureTest。我們先來看一下最重要的類CCRenderTexture。它是用於管理渲染目標紋理的類,與紋理不同的是,它必須是屬於

開啟CCRenderTexture.h:


[cpp] view plain copy
  1. //影象格式列舉,可以儲存為JPG和PNG兩種格式  
  2. typedef enum eImageFormat  
  3. {  
  4.     kCCImageFormatJPEG      = 0,  
  5.     kCCImageFormatPNG       = 1,  
  6. } tCCImageFormat;  
  7. //由結點派生  
  8. class CC_DLL CCRenderTexture : public CCNode   
  9. {  
  10.     //精靈成員變數及存取介面  
  11.     CC_PROPERTY(CCSprite*, m_pSprite, Sprite)  
  12. public:  
  13.     //構造  
  14.     CCRenderTexture();  
  15.     //析構  
  16.     virtual ~CCRenderTexture();  
  17.   
  18.     //建立一個渲染目標紋理。引數指定大小,畫素格式和深度模板緩衝格式。內部呼叫create實現。  
  19.     CC_DEPRECATED_ATTRIBUTE static CCRenderTexture * renderTextureWithWidthAndHeight(int w ,int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat);  
  20.   
  21.     //建立一個渲染目標紋理。引數指定大小,畫素格式。內部呼叫create實現。  
  22.     CC_DEPRECATED_ATTRIBUTE static CCRenderTexture * renderTextureWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat);  
  23.   
  24.     //建立一個渲染目標紋理。引數指定大小.。內部呼叫create實現。  
  25.     CC_DEPRECATED_ATTRIBUTE static CCRenderTexture * renderTextureWithWidthAndHeight(int w, int h);  
  26.   
  27.     //第一個函式的create實現。  
  28.     static CCRenderTexture * create(int w ,int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat);  
  29.   
  30.     //第二個函式的create實現。  
  31.     static CCRenderTexture * create(int w, int h, CCTexture2DPixelFormat eFormat);  
  32.   
  33.     //第三個函式的create實現。  
  34.     static CCRenderTexture * create(int w, int h);  
  35.   
  36.     //初始化,引數為大小和畫素格式。  
  37.     bool initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat);  
  38.   
  39.     //初始化,引數為大小和畫素格式,深度模板緩衝格式。  
  40.     bool initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat);  
  41.   
  42.     //開始渲染到當前目標紋理。  
  43.     void begin();  
  44.   
  45.     //清空顏色緩衝的值為指定值。  
  46.     void beginWithClear(float r, float g, float b, float a);  
  47.   
  48.     //清空顏色緩衝和深度的值為指定值。  
  49.     void beginWithClear(float r, float g, float b, float a, float depthValue);  
  50.   
  51.     //清空顏色緩衝和深度,模版值緩衝的值為指定值。  
  52.     void beginWithClear(float r, float g, float b, float a, float depthValue, int stencilValue);  
  53.   
  54.     //LUA中呼叫的結束函式。  
  55.     inline void endToLua(){ end();};  
  56.   
  57.     //結束渲染到當前目標紋理。  
  58.     void end();  
  59.   
  60.     //清空目標紋理的顏色為指定色  
  61.     void clear(float r, float g, float b, float a);  
  62.   
  63.     //清空目標紋理的深度值  
  64.     void clearDepth(float depthValue);  
  65.   
  66.     //清空目標紋理的模板緩衝值  
  67.     void clearStencil(int stencilValue);  
  68.   
  69.     //由目標紋理的資料產生出CCImage例項。  
  70.     CCImage* newCCImage();  
  71.   
  72.     //儲存目標紋理到相應圖片檔案。  
  73.     bool saveToFile(const char *szFilePath);  
  74.   
  75.     //儲存目標紋理到相應圖片檔案,指定影象格式。  
  76.     bool saveToFile(const char *name, tCCImageFormat format);  
  77.       
  78.     //監聽訊息,儲存目標紋理。  
  79.     void listenToBackground(CCObject *obj);  
  80.   
  81. protected:  
  82.     //FBO物件,即幀緩衝區,一幀中畫素資料儲存的緩衝區域。可參看http://www.cnblogs.com/aokman/archive/2010/11/14/1876987.html  
  83.     GLuint       m_uFBO;  
  84.     //深度緩衝。  
  85.     GLuint       m_uDepthRenderBufffer;  
  86.     //儲存舊的FBO物件。  
  87.     GLint        m_nOldFBO;  
  88.     //使用的紋理。  
  89.     CCTexture2D* m_pTexture;  
  90.     //用於儲存當前紋理資料的可變紋理物件。  
  91.     CCImage*     m_pUITextureImage;  
  92.     //畫素格式  
  93.     GLenum       m_ePixelFormat;  
  94. };  

然後是CPP:

[cpp] view plain copy
  1. //構造  
  2. CCRenderTexture::CCRenderTexture()  
  3. : m_pSprite(NULL)  
  4. , m_uFBO(0)  
  5. , m_uDepthRenderBufffer(0)  
  6. , m_nOldFBO(0)  
  7. , m_pTexture(0)  
  8. , m_pUITextureImage(NULL)  
  9. , m_ePixelFormat(kCCTexture2DPixelFormat_RGBA8888)  
  10. {  
  11.     //設定監聽EVENT_COME_TO_BACKGROUND事件,如果響應呼叫CCRenderTexture::listenToBackground函式。  
  12.     CCNotificationCenter::sharedNotificationCenter()->addObserver(this,  
  13.                                                                              callfuncO_selector(CCRenderTexture::listenToBackground),  
  14.                                                                              EVENT_COME_TO_BACKGROUND,  
  15.                                                                              NULL);  
  16. }  
  17. //析構  
  18. CCRenderTexture::~CCRenderTexture()  
  19. {  
  20.     //釋放FBO  
  21.     glDeleteFramebuffers(1, &m_uFBO);  
  22.     //釋放深度緩衝  
  23.     if (m_uDepthRenderBufffer)  
  24.     {  
  25.         glDeleteRenderbuffers(1, &m_uDepthRenderBufffer);  
  26.     }  
  27.     //釋放  
  28.     CC_SAFE_DELETE(m_pUITextureImage);  
  29.     //移除監聽響應函式。  
  30.     CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVENT_COME_TO_BACKGROUND);  
  31. }  
  32. //監聽訊息,儲存目標紋理。  
  33. void CCRenderTexture::listenToBackground(cocos2d::CCObject *obj)  
  34. {  
  35. //如果使用可變紋理。  
  36. #if CC_ENABLE_CACHE_TEXTURE_DATA  
  37.     //釋放上一個m_pUITextureImage  
  38.     CC_SAFE_DELETE(m_pUITextureImage);  
  39.     // 產生當前渲染目標的CCImage  
  40.     m_pUITextureImage = newCCImage();  
  41.     //如果成功則將紋理m_pTexture中資料填充到可變紋理。  
  42.     if (m_pUITextureImage)  
  43.     {  
  44.         const CCSize& s = m_pTexture->getContentSizeInPixels();  
  45.         VolatileTexture::addDataTexture(m_pTexture, m_pUITextureImage->getData(), kTexture2DPixelFormat_RGBA8888, s);  
  46.     }   
  47.     else  
  48.     {  
  49.         CCLOG("Cache rendertexture failed!");  
  50.     }  
  51. #endif  
  52. }  
  53. //取得精靈成員  
  54. CCSprite * CCRenderTexture::getSprite()  
  55. {  
  56.     return m_pSprite;  
  57. }  
  58. //設定精靈成員。  
  59. void CCRenderTexture::setSprite(CCSprite* var)  
  60. {  
  61.     m_pSprite = var;  
  62. }  
  63. //建立一個渲染目標紋理。引數指定大小,畫素格式。內部呼叫create實現。  
  64. CCRenderTexture * CCRenderTexture::renderTextureWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat)  
  65. {  
  66.     return CCRenderTexture::create(w, h, eFormat);  
  67. }  
  68. //上面的create實現。   
  69. CCRenderTexture * CCRenderTexture::create(int w, int h, CCTexture2DPixelFormat eFormat)  
  70. {  
  71.     //建立一個渲染目標紋理。  
  72.     CCRenderTexture *pRet = new CCRenderTexture();  
  73.     //呼叫相應的初始化函式。  
  74.     if(pRet && pRet->initWithWidthAndHeight(w, h, eFormat))  
  75.     {  
  76.         //成功後交由記憶體管理器進行管理。  
  77.         pRet->autorelease();  
  78.         return pRet;  
  79.     }  
  80.     //不成功則釋放置空返回NULL。  
  81.     CC_SAFE_DELETE(pRet);  
  82.     return NULL;  
  83. }  
  84. //建立一個渲染目標紋理。引數指定大小,畫素格式和深度模板緩衝格式。內部呼叫create實現。  
  85. CCRenderTexture * CCRenderTexture::renderTextureWithWidthAndHeight(int w ,int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat)  
  86. {  
  87.     return CCRenderTexture::create(w, h, eFormat, uDepthStencilFormat);  
  88. }  
  89. //上面的create實現。  
  90. CCRenderTexture * CCRenderTexture::create(int w ,int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat)  
  91. {  
  92.     //建立一個渲染目標紋理。  
  93.     CCRenderTexture *pRet = new CCRenderTexture();  
  94.     //呼叫相應的初始化函式。  
  95.     if(pRet && pRet->initWithWidthAndHeight(w, h, eFormat, uDepthStencilFormat))  
  96.     {  
  97.         pRet->autorelease();  
  98.         return pRet;  
  99.     }  
  100.     CC_SAFE_DELETE(pRet);  
  101.     return NULL;  
  102. }  
  103. //建立一個渲染目標紋理。引數指定大小。內部呼叫create實現。  
  104. CCRenderTexture * CCRenderTexture::renderTextureWithWidthAndHeight(int w, int h)  
  105. {  
  106.     return CCRenderTexture::create(w, h);  
  107. }  
  108. //上面的create實現。  
  109. CCRenderTexture * CCRenderTexture::create(int w, int h)  
  110. {  
  111.     //建立一個渲染目標紋理。  
  112.     CCRenderTexture *pRet = new CCRenderTexture();  
  113.     //呼叫相應的初始化函式。  
  114.     if(pRet && pRet->initWithWidthAndHeight(w, h, kCCTexture2DPixelFormat_RGBA8888, 0))  
  115.     {  
  116.         pRet->autorelease();  
  117.         return pRet;  
  118.     }  
  119.     CC_SAFE_DELETE(pRet);  
  120.     return NULL;  
  121. }  
  122. //初始化,引數為大小和畫素格式格式。  
  123. bool CCRenderTexture::initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat)  
  124. {  
  125.     return initWithWidthAndHeight(w, h, eFormat, 0);  
  126. }  
  127.     //初始化,引數為大小和畫素格式,深度模板緩衝格式。  
  128. bool CCRenderTexture::initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat)  
  129. {  
  130.     //有效性判斷。  
  131.     CCAssert(m_ePixelFormat != kCCTexture2DPixelFormat_A8, "only RGB and RGBA formats are valid for a render texture");  
  132.     //  
  133.     bool bRet = false;  
  134.     //使用do –while結構來保證出錯及時中斷退出  
  135.     do   
  136.     {  
  137.         //寬高要乘以縮放值。  
  138.         w *= (int)CC_CONTENT_SCALE_FACTOR();  
  139.         h *= (int)CC_CONTENT_SCALE_FACTOR();  
  140.         //儲存當前的FBO物件。  
  141.         glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);  
  142.         //建立臨時變數儲存紋理的真實大小。  
  143.         unsigned int powW = 0;  
  144.         unsigned int powH = 0;  
  145.         //看是否支援紋理的非2冪次方,做相應的處理。  
  146.         if( CCConfiguration::sharedConfiguration()->supportsNPOT() ) {  
  147.         //如果支援就用w,h做為紋理大小。  
  148.             powW = w;  
  149.             powH = h;  
  150.         } else {  
  151.         //如果不支援要轉換為2的冪次方大小。  
  152.             powW = ccNextPOT(w);  
  153.             powH = ccNextPOT(h);  
  154.         }  
  155.         //為畫素申請記憶體。每畫素4位元組。  
  156.         void *data = malloc((int)(powW * powH * 4));  
  157.         //記憶體申請失敗則中斷退出。  
  158.         CC_BREAK_IF(! data);  
  159.         //清空記憶體為0。  
  160.         memset(data, 0, (int)(powW * powH * 4));  
  161.         //儲存畫素格式。  
  162.         m_ePixelFormat = eFormat;  
  163.         //建立一個新的紋理。  
  164.         m_pTexture = new CCTexture2D();  
  165.         //由上面的畫素資料和大小,畫素格式資訊填充紋理。  
  166.         if (m_pTexture) {  
  167.                 m_pTexture->initWithData(data, (CCTexture2DPixelFormat)m_ePixelFormat, powW, powH, CCSizeMake((float)w, (float)h));  
  168.                 free( data );  
  169.         } else {  
  170.                 free( data );  // ScopeGuard (a simulated Finally clause) would be more elegant.  
  171.                 break;  
  172.         }  
  173.         //取得當前的FBO物件。  
  174.         GLint oldRBO;  
  175.         glGetIntegerv(GL_RENDERBUFFER_BINDING, &oldRBO);  
  176.         // 建立新的FBO並繫結。  
  177.         glGenFramebuffers(1, &m_uFBO);  
  178.         glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO);  
  179.   
  180.         //設定將幀緩衝區顏色資料輸出到紋理。  
  181.         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTexture->getName(), 0);  
  182.         //如果有使用深度緩衝。  
  183.         if (m_uDepthRenderBufffer != 0)   
  184.         {  
  185.             //建立一個渲染目標深度緩衝區並繫結。  
  186.             glGenRenderbuffers(1, &m_uDepthRenderBufffer);  
  187.             glBindRenderbuffer(GL_RENDERBUFFER, m_uDepthRenderBufffer);  
  188.           //設定當前渲染目標緩衝氏的畫素格式和大小。  
  189.             glRenderbufferStorage(GL_RENDERBUFFER, uDepthStencilFormat, (GLsizei)powW, (GLsizei)powH);  
  190.             //設定將幀緩衝區深度資料輸出到這個新建立的深度緩衝區。  
  191.             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_uDepthRenderBufffer);  
  192.   
  193.             //如果深度緩衝格式是24位深度,8位模版緩衝,則設定將幀緩衝區的模版資料輸出到這個新建立的深度緩衝區中。  
  194.             if (uDepthStencilFormat == CC_GL_DEPTH24_STENCIL8)  
  195.                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_uDepthRenderBufffer);  
  196.         }  
  197.   
  198.   
  199.         // 檢查上面操作是否正常執行。  
  200.         CCAssert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Could not attach texture to framebuffer");  
  201.         //設定紋理不使用抗距齒模糊。  
  202.         m_pTexture->setAliasTexParameters();  
  203.         //由紋理建立精靈成員。  
  204.         m_pSprite = CCSprite::createWithTexture(m_pTexture);  
  205.         //釋放紋理。  
  206.         m_pTexture->release();  
  207.         //精靈設定Y映象並放入到當前結點下。  
  208.         m_pSprite->setScaleY(-1);  
  209.         this->addChild(m_pSprite);  
  210.         //設定ALPHA混合方案。  
  211.         ccBlendFunc tBlendFunc = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA };  
  212.         m_pSprite->setBlendFunc(tBlendFunc);  
  213.         //還原所用的  
  214.         glBindRenderbuffer(GL_RENDERBUFFER, oldRBO);  
  215.         glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);  
  216.         bRet = true;  
  217.     } while (0);  
  218.     return bRet;  
  219.       
  220. }  
  221. //開始渲染到當前目標紋理。  
  222. void CCRenderTexture::begin()  
  223. {  
  224.     // 儲存當前矩陣  
  225.     kmGLPushMatrix();  
  226.     //取得紋理的大小。  
  227.     const CCSize& texSize = m_pTexture->getContentSizeInPixels();  
  228.   
  229.     // 取得裝置的視窗大小,計算出視口和投影矩陣  
  230.     CCDirector *director = CCDirector::sharedDirector();  
  231.     CCSize size = director->getWinSizeInPixels();  
  232.     float widthRatio = size.width / texSize.width;  
  233.     float heightRatio = size.height / texSize.height;  
  234.     glViewport(0, 0, (GLsizei)texSize.width, (GLsizei)texSize.height);  
  235.     kmMat4 orthoMatrix;  
  236.     kmMat4OrthographicProjection(&orthoMatrix, (float)-1.0 / widthRatio,  (float)1.0 / widthRatio,  
  237.         (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1 );  
  238.     kmGLMultMatrix(&orthoMatrix);  
  239.     //取得當前的FBO  
  240.     glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);  
  241.     glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO);  
  242. }  
  243. //清空顏色緩衝。  
  244. void CCRenderTexture::beginWithClear(float r, float g, float b, float a)  
  245. {  
  246.     //開始渲染到目標紋理。  
  247.     this->begin();  
  248.   
  249.     //臨時變數,用於儲存當前幀的色彩緩衝的清空值資料。  
  250.     GLfloat    clearColor[4];  
  251. glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor);   
  252.   
  253.     //按指定清空值數值清空色彩緩衝區。  
  254.     glClearColor(r, g, b, a);  
  255.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  256.   
  257.     //恢復色彩緩衝的清空值資料。  
  258.     glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);       
  259. }  
  260. //清空顏色緩衝和深度緩衝的值為指定值。  
  261. void CCRenderTexture::beginWithClear(float r, float g, float b, float a, float depthValue)  
  262. {  
  263.     //開始渲染到目標紋理。  
  264.     this->begin();  
  265.   
  266.     //臨時變數,用於儲存當前幀的各緩衝的清空值資料。  
  267.     GLfloat clearColor[4];  
  268.     GLfloat depthClearValue;  
  269.     glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor);  
  270.     glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue);  
  271.   
  272.     //按指定清空值數值清空各繪衝區。  
  273.     glClearColor(r, g, b, a);  
  274.     glClearDepth(depthValue);  
  275.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  276.   
  277.     //恢復各緩衝的清空值資料。  
  278.     glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);  
  279.     glClearDepth(depthClearValue);  
  280. }  
  281. //清空顏色緩衝和深度,模版值緩衝的值為指定值。  
  282. void CCRenderTexture::beginWithClear(float r, float g, float b, float a, float depthValue, int stencilValue)  
  283. {  
  284.     //開始渲染到目標紋理。  
  285.     this->begin();  
  286.   
  287.     //臨時變數,用於儲存當前幀的各緩衝的清空值資料。  
  288.     GLfloat clearColor[4];  
  289.     GLfloat depthClearValue;  
  290.     int stencilClearValue;  
  291.     glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor);  
  292.     glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue);  
  293.     glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilClearValue);  
  294.     //按指定清空值數值清空各繪衝區。  
  295.     glClearColor(r, g, b, a);  
  296.     glClearDepth(depthValue);  
  297.     glClearStencil(stencilValue);  
  298.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);  
  299.   
  300.     // 恢復各緩衝的清空值資料。  
  301.     glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);  
  302.     glClearDepth(depthClearValue);  
  303.     glClearStencil(stencilClearValue);  
  304. }  
  305. //結束渲染到當前目標紋理。  
  306. void CCRenderTexture::end()  
  307. {  
  308.     //還原舊的FBO  
  309.     glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);  
  310.     //恢復矩陣  
  311.     kmGLPopMatrix();  
  312.     //取得裝置。  
  313.     CCDirector *director = CCDirector::sharedDirector();  
  314.     //取得視窗大小。  
  315.     CCSize size = director->getWinSizeInPixels();  
  316.   
  317.     // 還原視口及投影矩陣。  
  318.     glViewport(0, 0, GLsizei(size.width * CC_CONTENT_SCALE_FACTOR()), GLsizei(size.height * CC_CONTENT_SCALE_FACTOR()));  
  319.     if ( director->getProjection() == kCCDirectorProjection3D && CC_CONTENT_SCALE_FACTOR() != 1 )  
  320.     {  
  321.         glViewport((GLsizei)(-size.width/2), (GLsizei)(-size.height/2), (GLsizei)(size.width * CC_CONTENT_SCALE_FACTOR()), (GLsizei)(size.height * CC_CONTENT_SCALE_FACTOR()));  
  322.     }  
  323.     director->setProjection(director->getProjection());  
  324. }  
  325. //清空目標紋理的顏色為指定色  
  326. void CCRenderTexture::clear(float r, float g, float b, float a)  
  327. {  
  328.     //開始渲染到目標紋理並清空目標紋理的顏色為指定色。  
  329.     this->beginWithClear(r, g, b, a);  
  330.     //結束渲染到目標紋理。  
  331.     this->end();  
  332. }  
  333. //清空目標紋理的深度值為指定值。  
  334. void CCRenderTexture::clearDepth(float depthValue)  
  335. {  
  336.     //開始渲染到目標紋理。  
  337.     this->begin();  
  338.     // 取得當前深度緩衝清空值。  
  339.     GLfloat depthClearValue;  
  340.     glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue);  
  341.     //設定新深度清空值並清空模板緩衝。  
  342.     glClearDepth(depthValue);  
  343.     glClear(GL_DEPTH_BUFFER_BIT);  
  344.   
  345.     //還用深度緩衝清空值。  
  346.     glClearDepth(depthClearValue);  
  347.     //結束渲染到目標紋理。  
  348.     this->end();  
  349. }  
  350. //清空目標紋理的模版緩衝值為指定色  
  351. void CCRenderTexture::clearStencil(int stencilValue)  
  352. {  
  353.     // 取得當前模板緩衝清空值。  
  354.     int stencilClearValue;  
  355.     glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilClearValue);  
  356.     //設定新模板清空值並清空模板緩衝。  
  357.     glClearStencil(stencilValue);  
  358.     glClear(GL_STENCIL_BUFFER_BIT);  
  359.   
  360.     //還原模板緩衝清空值。  
  361.     glClearStencil(stencilClearValue);  
  362. }  
  363.   
  364. //儲存目標紋理到相應圖片檔案。  
  365. bool CCRenderTexture::saveToFile(const char *szFilePath)  
  366. {  
  367.     bool bRet = false;  
  368.     //取得當前渲染目標紋理產生的CCImage。  
  369.     CCImage *pImage = newCCImage();  
  370.     if (pImage)  
  371.     {   //儲存到圖片。  
  372.         bRet = pImage->saveToFile(szFilePath, kCCImageFormatJPEG);  
  373.     }  
  374.     //釋放pImage  
  375.     CC_SAFE_DELETE(pImage);  
  376.     return bRet;  
  377. }  
  378. //儲存目標紋理到相應圖片檔案,可以指定畫素格式。  
  379. bool CCRenderTexture::saveToFile(const char *fileName, tCCImageFormat format)  
  380. {  
  381.     bool bRet = false;  
  382.     //只允許JPG和PNG格式  
  383.     CCAssert(format == kCCImageFormatJPEG || format == kCCImageFormatPNG,  
  384.              "the image can only be saved as JPG or PNG format");  
  385.     //取得當前渲染目標紋理產生的CCImage。  
  386.     CCImage *pImage = newCCImage();  
  387.     if (pImage)  
  388.     {  
  389.         //取得路徑。  
  390.         std::string fullpath = CCFileUtils::sharedFileUtils()->getWriteablePath() + fileName;  
  391.         //儲存到圖片。  
  392.         bRet = pImage->saveToFile(fullpath.c_str(), true);  
  393.     }  
  394.     //釋放pImage  
  395.     CC_SAFE_DELETE(pImage);  
  396.     //返回成敗。  
  397.     return bRet;  
  398. }  
  399.   
  400. //由目標紋理的資料產生出CCImage例項。/  
  401. CCImage* CCRenderTexture::newCCImage()  
  402. {  
  403.     //有效性判斷。  
  404.     CCAssert(m_ePixelFormat == kCCTexture2DPixelFormat_RGBA8888, "only RGBA8888 can be saved as image");  
  405.     if (NULL == m_pTexture)  
  406.     {  
  407.         return NULL;  
  408.     }  
  409.     //取得紋理的大小。  
  410.     const CCSize& s = m_pTexture->getContentSizeInPixels();  
  411.   
  412.     //取得寬高。   
  413.     int nSavedBufferWidth = (int)s.width;  
  414.     int nSavedBufferHeight = (int)s.height;  
  415.     //定義臨時指標變數。  
  416.     GLubyte *pBuffer = NULL;  
  417.     GLubyte *pTempData = NULL;  
  418.     CCImage *pImage = new CCImage();  
  419.     //do-while流程結構保證出錯及時退出。  
  420.     do  
  421. {  
  422.         //建立最終結果的畫素資料陣列的記憶體。  
  423.         CC_BREAK_IF(! (pBuffer = new GLubyte[nSavedBufferWidth * nSavedBufferHeight * 4]));  
  424.   
  425.         //申請臨時畫素資料陣列的記憶體。  
  426.         if(! (pTempData = new GLubyte[nSavedBufferWidth * nSavedBufferHeight * 4]))  
  427.         {  
  428.             delete[] pBuffer;  
  429.             pBuffer = NULL;  
  430.             break;  
  431.         }  
  432.         //開始渲染到目標紋理。  
  433.         this->begin();  
  434.         //設定畫素按位元組對齊。  
  435.         glPixelStorei(GL_PACK_ALIGNMENT, 1);  
  436.         //將畫素寫入到pTempData中。  
  437.         glReadPixels(0,0,nSavedBufferWidth, nSavedBufferHeight,GL_RGBA,GL_UNSIGNED_BYTE, pTempData);  
  438.         this->end();  
  439.   
  440.         //將pTempData從下往上拷到pBuffer中,為什麼呢?因為前面initWithWidthAndHeight中精靈成員設定了Y映象,所以影象是上下反的。   
  441.         for (int i = 0; i < nSavedBufferHeight; ++i)  
  442.         {  
  443.             memcpy(&pBuffer[i * nSavedBufferWidth * 4],   
  444.                 &pTempData[(nSavedBufferHeight - i - 1) * nSavedBufferWidth * 4],   
  445.                 nSavedBufferWidth * 4);  
  446.         }  
  447.         //填充pImage  
  448.         pImage->initWithImageData(pBuffer, nSavedBufferWidth * nSavedBufferHeight * 4, CCImage::kFmtRawData, nSavedBufferWidth, nSavedBufferHeight, 8);  
  449.     } while (0);  
  450.     //釋放申請的記憶體  
  451.     CC_SAFE_DELETE_ARRAY(pBuffer);  
  452.     CC_SAFE_DELETE_ARRAY(pTempData);  
  453.   
  454.     return pImage;  
  455. }  

       這個渲染目標紋理的核心功能類解析完之後,我們就沒有什麼還不可以理解的了。現在我們來開啟RenderTextureTest.h:

[cpp] view plain copy
  1. //演示用的層,做為基類使用  
  2. class RenderTextureTest : public CCLayer  
  3. {  
  4. public:  
  5.     //載入當前層時的處理  
  6.     virtual void onEnter();  
  7.     //取得標題。  
  8.     virtual std::string title();  
  9.     //取得副標題。  
  10.     virtual std::string subtitle();  
  11.     //重新啟動當前演示  
  12.     void restartCallback(CCObject* pSender);  
  13.     //下一個演示  
  14.     void nextCallback(CCObject* pSender);  
  15.     //上一個演示  
  16.     void backCallback(CCObject* pSender);  
  17. };  

對應CPP:

[cpp] view plain copy
  1. //場景索引  
  2. static int sceneIdx = -1;   
  3. //最大的演示層數量,四個  
  4. #define MAX_LAYER    4  
  5. //建立相應的演示層  
  6. CCLayer* createTestCase(int nIndex)  
  7. {  
  8.     //根據索引建立相應的演示層例項。  
  9.     switch(nIndex)  
  10.     {  
  11.     case 0: return new RenderTextureSave();  
  12.     case 1: return new RenderTextureIssue937();  
  13.     case 2: return new RenderTextureZbuffer();      
  14.     case 3: return new RenderTextureTestDepthStencil();  
  15.     }  
  16.   
  17.     return NULL;  
  18. }  
  19. //下一個演示例項  
  20. CCLayer* nextTestCase()  
  21. {  
  22.     sceneIdx++;  
  23.     sceneIdx = sceneIdx % MAX_LAYER;  
  24.   
  25.     CCLayer* pLayer = createTestCase(sceneIdx);  
  26.     pLayer->autorelease();  
  27.   
  28.     return pLayer;  
  29. }  
  30. //上一個演示例項  
  31. CCLayer* backTestCase()  
  32. {  
  33.     sceneIdx--;  
  34.     int total = MAX_LAYER;  
  35.     if( sceneIdx < 0 )  
  36.         sceneIdx += total;      
  37.   
  38.     CCLayer* pLayer = createTestCase(sceneIdx);  
  39.     pLayer->autorelease();  
  40.   
  41.     return pLayer;  
  42. }  
  43. //重新執行當前的演示例項。  
  44. CCLayer* restartTestCase()  
  45. {  
  46.     CCLayer* pLayer = createTestCase(sceneIdx);  
  47.     pLayer->autorelease();  
  48.   
  49.     return pLayer;  
  50. }  
  51.   
  52. //載入當前層時的處理。  
  53. void RenderTextureTest::onEnter()  
  54. {  
  55.     //先呼叫基類的相應函式。  
  56.     CCLayer::onEnter();  
  57.     //取得螢幕大小  
  58.     CCSize s = CCDirector::sharedDirector()->getWinSize();  
  59.     //建立相應的文字標籤。  
  60.     CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 26);  
  61.     //將文字標籤放到當前層下。  
  62.     addChild(label, 1);  
  63.     //將文字標籤放置在螢幕中央上部。  
  64.     label->setPosition( ccp(s.width/2, s.height-50) );  
  65.     //取得副標題。  
  66.     std::string strSubtitle = subtitle();  
  67.     if( ! strSubtitle.empty() )   
  68.     {  
  69.         //如果副標題文字有效,建立相應的文字標籤顯示副標題。  
  70.         CCLabelTTF* l = CCLabelTTF::create(strSubtitle.c_str(), "Thonburi", 16);  
  71.         //將副標題放入當前層下並放在主標題之下位置。  
  72.         addChild(l, 1);  
  73.         l->setPosition( ccp(s.width/2, s.height-80) );  
  74.     }      
  75.     //建立用於控制動畫演示的選單項  
  76.     //用於點選返回上一個動畫演示的選單項。  
  77.     CCMenuItemImage *item1 = CCMenuItemImage::create("Images/b1.png""Images/b2.png"this, menu_selector(RenderTextureTest::backCallback) );  
  78.     //用於點選重新執行當前動畫演示的選單項。  
  79.     CCMenuItemImage *item2 = CCMenuItemImage::create("Images/r1.png","Images/r2.png"this, menu_selector(RenderTextureTest::restartCallback) );  
  80.     //用於點選重新執行下一個動畫演示的選單項。  
  81.     CCMenuItemImage *item3 = CCMenuItemImage::create("Images/f1.png""Images/f2.png"this, menu_selector(RenderTextureTest::nextCallback) );  
  82.     //由上面三個選單項建立選單。  
  83.     CCMenu *menu = CCMenu::create(item1, item2, item3, NULL);  
  84.     //設定選單和各選單項的位置。  
  85.     menu->setPosition( CCPointZero );  
  86.     item1->setPosition( ccp( s.width/2 - item2->getContentSize().width*2, item2->getContentSize().height/2) );  
  87.     item2->setPosition( ccp( s.width/2, item2->getContentSize().height/2) );  
  88.     item3->setPosition( ccp( s.width/2 + item2->getContentSize().width*2, item2->getContentSize().height/2) );  
  89.     //將選單放入到當前層下。  
  90.     addChild(menu, 1);  
  91. }  
  92. //響應重啟當前演示的函式。  
  93. void RenderTextureTest::restartCallback(CCObject* pSender)  
  94. {  
  95.     //建立一個新的場景,建立一個新的當前演示層並放入到這個場景中。  
  96.     CCScene* s = new RenderTextureScene();  
  97.     s->addChild(restartTestCase());   
  98.     //執行建立的場景。  
  99.   
  100.     CCDirector::sharedDirector()->replaceScene(s);  
  101.     s->release();  
  102. }  
  103. //響應進行下一個演示的函式。  
  104. void RenderTextureTest::nextCallback(CCObject* pSender)  
  105. {  
  106.     //建立一個新的場景,建立下一個演示層並放入到這個場景中。  
  107.     CCScene* s = new RenderTextureScene();  
  108.     s->addChild( nextTestCase() );  
  109.     //執行建立的場景。  
  110.     CCDirector::sharedDirector()->replaceScene(s);  
  111.     s->release();  
  112. }  
  113. //響應進行上一個演示的函式。  
  114. void RenderTextureTest::backCallback(CCObject* pSender)  
  115. {  
  116.     //建立一個新的場景,建立上一個演示層並放入到這個場景中。  
  117.     CCScene* s = new RenderTextureScene();  
  118.     s->addChild( backTestCase() );  
  119.     //執行建立的場景。  
  120.     CCDirector::sharedDirector()->replaceScene(s);  
  121.     s->release();  
  122. }   
  123. //取得標題。  
  124. std::string RenderTextureTest::title()  
  125. {  
  126.     return "No title";  
  127. }  
  128. //取得副標題。  
  129. std::string RenderTextureTest::subtitle()  
  130. {  
  131.     return "";  
  132. }  

第一個演示:儲存渲染目標紋理。

說明:在螢幕上觸屏移動,可以不斷的使用隨機頂點色的火球點圖片精靈來做為刷子繪製線條。點選右邊的“Clear”可以將螢幕清為隨機色。而這些影象都是輸出到紋理的。點選“SaveImage”可以儲存這個紋理為PNG或JPG。

截圖


程式碼:

[cpp] view plain copy
  1. //由上面的類派生出的類,可以將目標紋理儲存成為圖片。  
  2. class RenderTextureSave : public RenderTextureTest  
  3. {  
  4. public:  
  5.     //構造  
  6.     RenderTextureSave();  
  7.     //析構  
  8.     ~RenderTextureSave();  
  9.     //取得標題  
  10.     virtual std::string title();  
  11.     //取得副標題。  
  12.     virtual std::string subtitle();  
  13.     //響應觸屏時移動事件  
  14.     virtual void ccTouchesMoved(CCSet* touches, CCEvent* event);  
  15.     //清除圖片影象  
  16.     void clearImage(CCObject *pSender);  
  17.     //儲存紋理到圖片  
  18.     void saveImage(CCObject *pSender);  
  19.   
  20. private:  
  21.     //目標紋理  
  22.     CCRenderTexture *m_pTarget;  
  23.     //代表刷子的精靈  
  24.     CCSprite *m_pBrush;  
  25. };  

對應CPP:

[cpp] view plain copy
  1. //構選  
  2. RenderTextureSave::RenderTextureSave()  
  3. {  
  4.     //取得螢幕大小  
  5.     CCSize s = CCDirector::sharedDirector()->getWinSize();  
  6.   
  7.     //建立一個目標紋理,用於把場景渲染到其中。  
  8.     m_pTarget = CCRenderTexture::create(s.width, s.height, kCCTexture2DPixelFormat_RGBA8888);  
  9.     //佔用它,對其引用計數器加一。  
  10.     m_pTarget->retain();  
  11.     //設定目標紋理的位置放在螢幕中央。  
  12.     m_pTarget->setPosition(ccp(s.width / 2, s.height / 2));  
  13.   
  14.     //將目標紋理放入到當前層下。  
  15.     this->addChild(m_pTarget, -1);  
  16.   
  17.     //建立一個刷子的精靈,載入的是火球的圖片。  
  18.     m_pBrush = CCSprite::create("Images/fire.png");  
  19.     //佔用它,對其引用計數器加一。  
  20.     m_pBrush->retain();  
  21.     //設定精靈為紅色  
  22.     m_pBrush->setColor(ccRED);  
  23.     //設定透明度為20%  
  24.     m_pBrush->setOpacity(20);  
  25.     //設定當前層響應觸屏事件。  
  26.     this->setTouchEnabled(true);  
  27.   
  28.     //建立一個選單項文字的字型,大小為16。  
  29.     CCMenuItemFont::setFontSize(16);  
  30.     //建立兩個選單項文字,分別為“儲存影象”和“清空影象”。  
  31.     CCMenuItem *item1 = CCMenuItemFont::create("Save Image"this, menu_selector(RenderTextureSave::saveImage));  
  32.     CCMenuItem *item2 = CCMenuItemFont::create("Clear"this, menu_selector(RenderTextureSave::clearImage));  
  33.     //由這兩個選單項建立選單,並放入當前層下。  
  34.     CCMenu *menu = CCMenu::create(item1, item2, NULL);  
  35.     this->addChild(menu);  
  36.     //設定選單項按縱向排列。  
  37.     menu->alignItemsVertically();  
  38.     //設定選單的位置。  
  39.     menu->setPosition(ccp(s.width - 80, s.height - 30));  
  40. }  
  41. //取得標題。  
  42. string RenderTextureSave::title()  
  43. {  
  44.     return "Touch the screen";  
  45. }  
  46. //取得副標題。  
  47. string RenderTextureSave::subtitle()  
  48. {  
  49.     return "Press 'Save Image' to create an snapshot of the render texture";  
  50. }  
  51. //清除圖片影象。  
  52. void RenderTextureSave::clearImage(cocos2d::CCObject *pSender)  
  53. {  
  54.     //對目標紋理呼叫clear即可以將其清空為相應的顏色。  
  55.     //這裡對,R,G,B,A的色彩分量做了0~1間的隨機,產生出隨機的色彩。  
  56.     m_pTarget->clear(CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1());  
  57. }  
  58. //儲存影象。  
  59. void RenderTextureSave::saveImage(cocos2d::CCObject *pSender)  
  60. {  
  61.     //建立一個計數器,用來統計儲存的次數。  
  62.     static int counter = 0;  
  63.     //要儲存檔案的名稱。  
  64.     char png[20];  
  65.     sprintf(png, "image-%d.png", counter);  
  66.     char jpg[20];  
  67.     sprintf(jpg, "image-%d.jpg", counter);  
  68.     //對目標紋理呼叫saveToFile將紋理上的影象儲存為PNG和JPG兩種格式的圖片。  
  69.     m_pTarget->saveToFile(png, kCCImageFormatPNG);  
  70.     m_pTarget->saveToFile(jpg, kCCImageFormatJPEG);  
  71.     //取得紋理對應的CCImage例項指標  
  72.     CCImage *pImage = m_pTarget->newCCImage();  
  73.     //將PNG圖片和CCImage例項中的資訊載入到紋理管理器中。  
  74.     CCTexture2D *tex = CCTextureCache::sharedTextureCache()->addUIImage(pImage, png);  
  75.     //刪除pImage  
  76.     CC_SAFE_DELETE(pImage);  
  77.     //由紋理tex建立精靈sprite。  
  78.     CCSprite *sprite = CCSprite::createWithTexture(tex);  
  79.     //設定縮放值為原來0.3倍  
  80.     sprite->setScale(0.3f);  
  81.     //將精靈放到當前層下。  
  82.     addChild(sprite);  
  83.     //設定精靈位置。  
  84.     sprite->setPosition(ccp(40, 40));  
  85.     sprite->setRotation(counter * 3);  
  86.     //列印日誌  
  87.     CCLOG("Image saved %s and %s", png, jpg);  
  88.     //計數器加一  
  89.     counter++;  
  90. }  
  91. //析構  
  92. RenderTextureSave::~RenderTextureSave()  
  93. {  
  94.     //對佔用的目標紋得和刷子精靈的引用計數減一。  
  95.     m_pBrush->release();  
  96.     m_pTarget->release();  
  97.     //紋理管理器釋放不用的紋理。  
  98.     CCTextureCache::sharedTextureCache()->removeUnusedTextures();  
  99. }  
  100. //響應觸屏時移動事件  
  101. void RenderTextureSave::ccTouchesMoved(CCSet* touches, CCEvent* event)  
  102. {  
  103.     //取得觸點  
  104.     CCTouch *touch = (CCTouch *)touches->anyObject();  
  105.     //取得觸點的當前位置。  
  106.     CCPoint start = touch->getLocation();  
  107.     //取得觸點的上一個位置。  
  108.     CCPoint end = touch->getPreviousLocation();  
  109.     // 開始渲染到目標紋理  
  110.     m_pTarget->begin();  
  111.   
  112.     //取得當前位置和上一個位置的距離。   
  113.     float distance = ccpDistance(start, end);  
  114.     //如果距離大於1畫素。  
  115.     if (distance > 1)  
  116.     {  
  117.         int d = (int)distance;  
  118.         //則在上一個位置和當前位置間連一條線,每個畫素渲染一下刷子精靈。  
  119.         for (int i = 0; i < d; i++)  
  120.         {  
  121.             //計算X和Y方向的偏移。  
  122.             float difx = end.x - start.x;  
  123.             float dify = end.y - start.y;  
  124.             //通過偏移計算斜率。  
  125.             float delta = (float)i / distance;  
  126.             //通過直線公式計數出連像上第i個位置的畫素位置設定給精靈。  
  127.             m_pBrush->setPosition(ccp(start.x + (difx * delta), start.y + (dify * delta)));  
  128.              //設定一個隨機的旋轉值給精靈。  
  129.             m_pBrush->setRotation(rand() % 360);  
  130.              //在給一個限定範圍的隨機縮放大小給精靈。  
  131.             float r = (float)(rand() % 50 / 50.f) + 0.25f;  
  132.             m_pBrush->setScale(r);  
  133.             /*m_pBrush->setColor(ccc3(CCRANDOM_0_1() * 127 + 128, 255, 255));*/  
  134.             // Use CCRANDOM_0_1() will cause error when loading libtests.so on android, I don't know why.  
  135.             //設定一個隨機的顏色給精靈。  
  136.             m_pBrush->setColor(ccc3(rand() % 127 + 128, 255, 255));  
  137.             // Call visit to draw the brush, don't call draw..  
  138.             //將精靈繪製出來。  
  139.             m_pBrush->visit();  
  140.         }  
  141.     }  
  142.   
  143.     //結束渲染到紋理,則由精靈構成的連線影象就被渲染到紋理中了。  
  144.     m_pTarget->end();  
  145. }  

第二個演示:渲染小球到目標紋理。

說明:將縱向排列的兩個小球的精靈渲染到目標紋理上,在場景中左邊一列顯示兩個小球,右邊一列顯示目標紋理。

截圖


程式碼:

[cpp] view plain copy
  1. //由上面的類派生出的類,。  
  2. class RenderTextureIssue937 : public RenderTextureTest  
  3. {  
  4. public:  
  5.     //構造  
  6.     RenderTextureIssue937();  
  7.     //取得標題  
  8.     virtual std::string title();  
  9.     //取得副 標題  
  10.     virtual std::string subtitle();  
  11. };  

對應CPP:

[cpp] view plain copy
  1. //構造  
  2. RenderTextureIssue937::RenderTextureIssue937()  
  3. {  
  4.     //建立一個色層做為背景放入到當前層下。  
  5.     CCLayerColor *background = CCLayerColor::create(ccc4(200,200,200,255));  
  6.     addChild(background);  
  7.     //建立第一個精靈  
  8.     CCSprite *spr_premulti = CCSprite::create("Images/fire.png");  
  9.     spr_premulti->setPosition(ccp(16,48));  
  10.     //建立第二個精靈。  
  11.     CCSprite *spr_nonpremulti = CCSprite::create("Images/fire.png");  
  12.     spr_nonpremulti->setPosition(ccp(16,16));  
  13.     //建立一個渲染目標紋理。  
  14.     CCRenderTexture *rend = CCRenderTexture::create(32, 64, kCCTexture2DPixelFormat_RGBA8888);  
  15.     //如果失敗則返回  
  16.     if (NULL == rend)  
  17.     {  
  18.         return;  
  19.     }  
  20.     //開始渲染影象到目標紋理。  
  21.     rend->begin();  
  22.     //先渲染第一個精靈。  
  23.     spr_premulti->visit();  
  24.     //再渲染第二個精靈。  
  25.     spr_nonpremulti->visit();  
  26.     //結束渲染到紋理。  
  27.     rend->end();   
  28.     //取得螢幕大小。  
  29.     CCSize s = CCDirector::sharedDirector()->getWinSize();  
  30.     //設定第一個精靈的位置。  
  31.     spr_premulti->setPosition(ccp(s.width/2-16, s.height/2+16));  
  32.     //設定第二個精靈的位置。  
  33.     spr_nonpremulti->setPosition(ccp(s.width/2-16, s.height/2-16));  
  34.     //設定目標紋理的位置。  
  35.     rend->setPosition(ccp(s.width/2+16, s.height/2));  
  36.     //將上面三個結點都放到當前層之下。  
  37.     addChild(spr_nonpremulti);  
  38.     addChild(spr_premulti);  
  39.     addChild(rend);  
  40. }  
  41. //取得標題。  
  42. std::string RenderTextureIssue937::title()  
  43. {  
  44.     return "Testing issue #937";  
  45. }  
  46. //取得副標題。  
  47. std::string RenderTextureIssue937::subtitle()  
  48. {  
  49.     return "All images should be equal...";  
  50. }  

第三個演示:渲染不同Z值的精靈到目標紋理。

說明:在螢幕上建立了一系列不同Z值的文字標籤和精靈,當觸屏按下鬆開後將螢幕畫面繪製到目標紋理,建立一個精靈使用這個目標紋理並在螢幕上執行一個顯示到漸隱的動畫。

截圖


程式碼:

[cpp] view plain copy
  1. //由上面的類派生出的類,。  
  2. class RenderTextureZbuffer : public RenderTextureTest  
  3. {  
  4. public:  
  5.     //構造  
  6.     RenderTextureZbuffer();  
  7.     //響應觸屏的一些事件  
  8.     //按下並移動事件的響應  
  9.     virtual void ccTouchesMoved(CCSet* touches, CCEvent* event);  
  10.     //按下事件的響應  
  11.     virtual void ccTouchesBegan(CCSet* touches, CCEvent* event);  
  12.     //抬起事件的響應  
  13.     virtual void ccTouchesEnded(CCSet* touches, CCEvent* event);  
  14.     //取得標題。  
  15.     virtual std::string title();  
  16.     //取得副標題。  
  17.     virtual std::string subtitle();  
  18.     //渲染螢幕快照  
  19.     void renderScreenShot();  
  20.   
  21. private:  
  22.     //使用的精靈批次優化處理結點。  
  23.     cocos2d::CCSpriteBatchNode *mgr;;  
  24.     //用到的若干精靈。  
  25.     cocos2d::CCSprite *sp1;  
  26.     cocos2d::CCSprite *sp2;  
  27.     cocos2d::CCSprite *sp3;  
  28.     cocos2d::CCSprite *sp4;  
  29.     cocos2d::CCSprite *sp5;  
  30.     cocos2d::CCSprite *sp6;  
  31.     cocos2d::CCSprite *sp7;  
  32.     cocos2d::CCSprite *sp8;  
  33.     cocos2d::CCSprite *sp9;  
  34. };  

對應CPP:

[cpp] view plain copy
  1. //構造  
  2. RenderTextureZbuffer::RenderTextureZbuffer()  
  3. {  
  4.     //設定當前層響應觸屏事悠揚。  
  5.     this->setTouchEnabled(true);  
  6.     //取得螢幕大小。  
  7.     CCSize size = CCDirector::sharedDirector()->getWinSize();  
  8.     //建立文字標籤。  
  9.     CCLabelTTF *label = CCLabelTTF::create("vertexZ = 50""Marker Felt", 64);  
  10.     //設定文字標籤的位置並放入到當前層下。  
  11.     label->setPosition(ccp(size.width / 2, size.height * 0.25f));  
  12.     this->addChild(label);  
  13.     //建立第二個文字標籤。  
  14.     CCLabelTTF *label2 = CCLabelTTF::create("vertexZ = 0""Marker Felt", 64);  
  15.     //設定文字標籤的位置並放入到當前層下。  
  16.     label2->setPosition(ccp(size.width / 2, size.height * 0.5f));  
  17.     this->addChild(label2);  
  18.     //建立第三個文字標籤。  
  19.     CCLabelTTF *label3 = CCLabelTTF::create("vertexZ = -50""Marker Felt", 64);  
  20.     //設定文字標籤的位置並放入到當前層下。  
  21.     label3->setPosition(ccp(size.width / 2, size.height * 0.75f));  
  22.     this->addChild(label3);  
  23.     //分別對三個文字標籤設定不同的頂點Z值。  
  24.     label->setVertexZ(50);  
  25.     label2->setVertexZ(0);  
  26.     label3->setVertexZ(-50);  
  27.     //由circle.plist   
  28.     CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("Images/bugs/circle.plist");  
  29.     //建立相應的精靈批次優化結點。  
  30.     mgr = CCSpriteBatchNode::create("Images/bugs/circle.png", 9);  
  31.     this->addChild(mgr);  
  32.     //建立9個精靈  
  33.     sp1 = CCSprite::createWithSpriteFrameName("circle.png");  
  34.     sp2 = CCSprite::createWithSpriteFrameName("circle.png");  
  35.     sp3 = CCSprite::createWithSpriteFrameName("circle.png");  
  36.     sp4 = CCSprite::createWithSpriteFrameName("circle.png");  
  37.     sp5 = CCSprite::createWithSpriteFrameName("circle.png");  
  38.     sp6 = CCSprite::createWithSpriteFrameName("circle.png");  
  39.     sp7 = CCSprite::createWithSpriteFrameName("circle.png");  
  40.     sp8 = CCSprite::createWithSpriteFrameName("circle.png");  
  41.     sp9 = CCSprite::createWithSpriteFrameName("circle.png");  
  42.     //將其加入到批次優化結點中,給予不同的Z值。  
  43.     mgr->addChild(sp1, 9);  
  44.     mgr->addChild(sp2, 8);  
  45.     mgr->addChild(sp3, 7);  
  46.     mgr->addChild(sp4, 6);  
  47.     mgr->addChild(sp5, 5);  
  48.     mgr->addChild(sp6, 4);  
  49.     mgr->addChild(sp7, 3);  
  50.     mgr->addChild(sp8, 2);  
  51.     mgr->addChild(sp9, 1);  
  52.     //對這九個精靈設定不同的頂點Z值。  
  53.     sp1->setVertexZ(400);  
  54.     sp2->setVertexZ(300);  
  55.     sp3->setVertexZ(200);  
  56.     sp4->setVertexZ(100);  
  57.     sp5->setVertexZ(0);  
  58.     sp6->setVertexZ(-100);  
  59.     sp7->setVertexZ(-200);  
  60.     sp8->setVertexZ(-300);  
  61.     sp9->setVertexZ(-400);  
  62.     //設定第九個放大2倍,頂點色為黃色。  
  63.     sp9->setScale(2);  
  64.     sp9->setColor(ccYELLOW);  
  65. }  
  66. //取得標題。  
  67. string RenderTextureZbuffer::title()  
  68. {  
  69.     return "Testing Z Buffer in Render Texture";  
  70. }  
  71. //取得副標題。  
  72. string RenderTextureZbuffer::subtitle()  
  73. {  
  74.     return "Touch screen. It should be green";  
  75. }  
  76. //按下事件的響應  
  77. void RenderTextureZbuffer::ccTouchesBegan(cocos2d::CCSet *touches, cocos2d::CCEvent *event)  
  78. {  
  79.     //遍歷所有的觸點。  
  80.     CCSetIterator iter;  
  81.     CCTouch *touch;  
  82.     for (iter = touches->begin(); iter != touches->end(); ++iter)  
  83.     {  
  84.         //將九個精靈放在觸點位置。  
  85.         touch = (CCTouch *)(*iter);  
  86.         CCPoint location = touch->getLocation();  
  87.   
  88.         sp1->setPosition(location);  
  89.         sp2->setPosition(location);  
  90.         sp3->setPosition(location);  
  91.         sp4->setPosition(location);  
  92.         sp5->setPosition(location);  
  93.         sp6->setPosition(location);  
  94.         sp7->setPosition(location);  
  95.         sp8->setPosition(location);  
  96.         sp9->setPosition(location);  
  97.     }  
  98. }  
  99. //按下並移動事件的響應。  
  100. void RenderTextureZbuffer::ccTouchesMoved(CCSet* touches, CCEvent* event)  
  101. {  
  102.     //遍歷所有的觸點。  
  103.     CCSetIterator iter;  
  104.     CCTouch *touch;  
  105.     for (iter = touches->begin(); iter != touches->end(); ++iter)  
  106.     {//將九個精靈放在觸點位置。  
  107.   
  108.         touch = (CCTouch *)(*iter);  
  109.         CCPoint location = touch->getLocation();  
  110.   
  111.         sp1->setPosition(location);  
  112.         sp2->setPosition(location);  
  113.         sp3->setPosition(location);  
  114.         sp4->setPosition(location);  
  115.         sp5->setPosition(location);  
  116.         sp6->setPosition(location);  
  117.         sp7->setPosition(location);  
  118.         sp8->setPosition(location);  
  119.         sp9->setPosition(location);  
  120.     }  
  121. }  
  122.   
  123. //抬起事件的響應  
  124. void RenderTextureZbuffer::ccTouchesEnded(CCSet* touches, CCEvent* event)  
  125. {  
  126.     //渲染螢幕快照  
  127.     this->renderScreenShot();  
  128. }  
  129. //渲染螢幕快照  
  130. void RenderTextureZbuffer::renderScreenShot()  
  131. {  
  132.     //建立一個512,512大小的渲染目標紋理。  
  133.     CCRenderTexture *texture = CCRenderTexture::create(512, 512);  
  134.     //如果無效直接返回。  
  135.     if (NULL == texture)  
  136.     {  
  137.         return;  
  138.     }   
  139.     //設定錨點為左下角。  
  140.     texture->setAnchorPoint(ccp(0, 0));  
  141.     //開始渲染到目標紋理。  
  142.     texture->begin();  
  143.     //將當前層渲染一遍。  
  144.     this->visit();  
  145.     //結束渲染到目標紋理。  
  146.     texture->end();  
  147.     //建立一個精靈。  
  148.     CCSprite *sprite = CCSprite::createWithTexture(texture->getSprite()->getTexture());  
  149.     //設定精靈的位置,透明度,Y方向翻轉。  
  150.     sprite->setPosition(ccp(256, 256));  
  151.     sprite->setOpacity(182);  
  152.     sprite->setFlipY(1);  
  153.     //將精靈放入到當前層的最前面。  
  154.     this->addChild(sprite, 999999);  
  155.     //設定為綠色。  
  156.     sprite->setColor(ccGREEN);  
  157.     //讓精靈執行一個動畫序列,表現為漸漸消失。  
  158.     sprite->runAction(CCSequence::create(CCFadeTo::create(2, 0),  
  159.                                           CCHide::create(),  
  160.                                           NULL));  
  161. }  

第四個演示:測試模版緩衝目標紋理。

說明

關於模版緩衝可以先看一下博文:

http://www.cnblogs.com/aokman/archive/2010/12/13/1904723.html

本例是開啟模版測試,然後繪製球精靈,但只寫A通過模版測試,對通過的畫素位置設定模版值為1,之後向右上移動1/4大小球的位置,對模版值為0的區域設定通過來繪製球精靈,這樣的效果約會有1/4缺失。

    注意:不幸的是,經測試所有版本似乎都無效,我不知道是不是哪裡存在BUG,還是我的錯誤。

截圖

現實是這樣的:


按程式碼的做法我用Photoshop做了一個結果圖,本應是這樣:


程式碼:

[cpp] view plain copy
  1. //由上面的基類派生出的類,。  
  2. class RenderTextureTestDepthStencil : public RenderTextureTest  
  3. {  
  4. public:  
  5.     //構造  
  6.     RenderTextureTestDepthStencil();  
  7.     //取得標題。  
  8.     virtual std::string title();  
  9.     //取得副標題。  
  10.     virtual std::string subtitle();  
  11. };  

對應CPP:

[cpp] view plain copy
  1. //構造  
  2. RenderTextureTestDepthStencil::RenderTextureTestDepthStencil()  
  3. {  
  4.     //取得螢幕大小。  
  5.     CCSize s = CCDirector::sharedDirector()->getWinSize();  
  6.     //建立一個精靈,設定其位置,10倍放大。  
  7.     CCSprite *sprite = CCSprite::create("Images/fire.png");  
  8.     sprite->setPosition(ccp(s.width * 0.25f, 0));  
  9.     sprite->setScale(10);  
  10.     //建立一個目標紋理,用於儲存深度緩衝。  
  11.     CCRenderTexture *rend = CCRenderTexture::create(s.width, s.height, kCCTexture2DPixelFormat_RGBA4444, CC_GL_DEPTH24_STENCIL8);  
  12.     //設定模版緩衝的掩碼值。  
  13.     glStencilMask(0xFF);  
  14.     //清空目標紋理的深度緩衝和模板緩衝為0。  
  15.     rend->beginWithClear(0, 0, 0, 0, 0, 0);  
  16.     //開啟模版測試。  
  17.     glEnable(GL_STENCIL_TEST);  
  18.     //設定繪製影象的畫素部分總是可以通過模版測試  
  19.     glStencilFunc(GL_ALWAYS, 1, 0xFF);  
  20.     //設定更新操作為:  
  21.     //1,模板測試失敗時保持當前的模板值不變。  
  22.     //2,模板測試通過,深度測試失敗時保持當前的模板值不變。  
  23.     //3,模板測試通過,深度測試通過時將當前的模板值設定為參考值。  
  24.     glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);  
  25.     //設定對顏色緩衝區的R,G,B,A位是否執行寫入操作。這裡只寫A。  
  26.     glColorMask(0, 0, 0, 1);  
  27.     //將精靈渲染一遍。  
  28.     sprite->visit();  
  29.     //設定精靈位置向右上偏移一些  
  30.     sprite->setPosition(ccpAdd(sprite->getPosition(), ccpMult(ccp(sprite->getContentSize().width * sprite->getScale(), sprite->getContentSize().height * sprite->getScale()), 0.5)));  
  31.     //設定比較條件,如果畫素參考值不等於模板值通過測試。  
  32.     glStencilFunc(GL_NOTEQUAL, 1, 0xFF);  
  33.     //設定對顏色緩衝區的R,G,B,A位執行寫入操作。  
  34.     glColorMask(1, 1, 1, 1);  
  35.     //精靈再渲染一遍。  
  36.     sprite->visit();  
  37.     //結束渲染到目標紋理。  
  38.     rend->end();  
  39.     //關閉模版緩衝測試。  
  40.     glDisable(GL_STENCIL_TEST);  
  41.     //設定目標紋理的位置。  
  42.     rend->setPosition(ccp(s.width * 0.5f, s.height * 0.5f));  
  43.     //將目標紋理放到當前層下。  
  44.     this->addChild(rend);  
  45. }  
  46. //標題。  
  47. std::string RenderTextureTestDepthStencil::title()  
  48. {  
  49.     return "Testing depthStencil attachment";  
  50. }  
  51. //取得副標題。  
  52. std::string RenderTextureTestDepthStencil::subtitle()  
  53. {  
  54.     return "Circle should be missing 1/4 of its region";  
  55. }  

 

    上面這個例子如果真存在問題,希望Cocos2d-x開發組能有看到並做下修改。最後程式碼餘下的就是場景了:

[cpp] view plain copy
  1. //演示用的場景  
  2. class RenderTextureScene : public TestScene  
  3. {  
  4. public:  
  5.     //執行場景時的處理  
  6.     virtual void runThisTest();  
  7. };  

對應CPP:

[cpp] view plain copy
  1. //執行演示場景。  
  2. void RenderTextureScene::runThisTest()  
  3. {  
  4.     //建立下一個演示層放入到當前的場景中。  
  5.     CCLayer* pLayer = nextTestCase();  
  6.     addChild(pLayer);  
  7.     //執行當前場景。  
  8.     CCDirector::sharedDirector()->replaceScene(this);  
  9. }  

     渲染到紋理,就是這麼個東西了。很有用的技術,希望各位好好掌握。

相關文章