以下一些内容摘自http://www.cnblogs.com/newlist/p/3552340.html
CCRenderTexture,它允许你来动态创建纹理,并且可以在游戏中重用这些纹理。也可用它来读取你想要的纹理信息。
使用 CCRenderTexture非常简单 – 你只需要做以下5步就行了:
- 创建一个新的CCRenderTexture. 这里,你可以指定将要创建的纹理的宽度和高度。.
- 调用 CCRenderTexture:begin. 这个方法会启动OpenGL,并且接下来,任何绘图的命令都会渲染到CCRenderTexture里面去,而不是画到屏幕上。
- 绘制纹理. 你可以使用原始的OpenGL调用来绘图,或者你也可以使用cocos2d对象里面已经定义好的visit方法。(这个visit方法就会调用一些opengl命令来绘制cocos2d对象)
- 调用 CCRenderTexture:end. 这个方法会渲染纹理,并且会关闭渲染至CCRenderTexture的通道。
- 从生成的纹理中创建一个sprite. 你现在可以用CCRenderTexture的sprite.texture属性来轻松创建新的精灵了。
用已有的纹理创建一个纹理:
class RenderTextureSave : public RenderTextureTest { public: RenderTextureSave(); ~RenderTextureSave(); virtual std::string title(); virtual std::string subtitle(); virtual void onTouchesMoved(const std::vector<Touch*>& touches, Event* event); void clearImage(Object *pSender); void saveImage(Object *pSender); private: RenderTexture *_target; Sprite *_brush; };
void RenderTextureSave::onTouchesMoved(const std::vector<Touch*>& touches, Event* event) { auto touch = touches[0]; auto start = touch->getLocation(); auto end = touch->getPreviousLocation(); // 开始渲染目标纹理 _target->begin(); // for extra points, we'll draw this smoothly from the last position and vary the sprite's // scale/rotation/offset float distance = start.getDistance(end); if (distance > 1) { int d = (int)distance; for (int i = 0; i < d; i++) { float difx = end.x - start.x; float dify = end.y - start.y; float delta = (float)i / distance; _brush->setPosition(Point(start.x + (difx * delta), start.y + (dify * delta))); _brush->setRotation(rand() % 360); float r = (float)(rand() % 50 / 50.f) + 0.25f; _brush->setScale(r); /*_brush->setColor(Color3B(CCRANDOM_0_1() * 127 + 128, 255, 255));*/ // Use CCRANDOM_0_1() will cause error when loading libtests.so on android, I don't know why. _brush->setColor(Color3B(rand() % 127 + 128, 255, 255)); // Call visit to draw the brush, don't call draw.. _brush->visit(); } } // 完成渲染将目标缓存转移到离屏缓存中 _target->end(); }
void RenderTextureSave::saveImage(cocos2d::Object *sender) { static int counter = 0; char png[20]; sprintf(png, "image-%d.png", counter); char jpg[20]; sprintf(jpg, "image-%d.jpg", counter); _target->saveToFile(png, Image::Format::PNG); _target->saveToFile(jpg, Image::Format::JPG); auto image = _target->newImage(); auto tex = TextureCache::getInstance()->addImage(image, png); CC_SAFE_DELETE(image); auto sprite = Sprite::createWithTexture(tex); sprite->setScale(0.3f); addChild(sprite); sprite->setPosition(Point(40, 40)); sprite->setRotation(counter * 3); CCLOG("Image saved %s and %s", png, jpg); counter++; }
读取一个纹理信息
//开始准备绘制
this->m_pRenderTexture->begin();
//绘制使用的临时精灵,与原图是同一图片
CCSprite* pTempSpr = CCSprite::createWithSpriteFrame(this->m_imgMan->displayFrame());
pTempSpr->setPosition(ccp(pTempSpr->getContentSize().width / 2, pTempSpr->getContentSize().height / 2));
//绘制
pTempSpr->visit();
//结束绘制
this->m_pRenderTexture->end();
//通过画布拿到这张画布上每个像素点的信息,封装到CCImage中
CCImage* pImage = this->m_pRenderTexture->newCCImage();
那么CCRenderTexture具体是怎么实现对自己目标纹理的渲染呢
其实里面用到了一种技术较FBO(FrameBufferObject),他可以用来把在窗口帧缓冲(framebuffer)的渲染转移到你自己创建的Off screen缓冲中。
跟到代码里面
OnBegin里显示开启opengl,然后有这样两句话:
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_oldFBO);
glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
取出旧的帧缓冲,绑定新的帧缓冲,于是调用完精灵的渲染。纹理信息就被存到了新的帧缓冲中。
OnEnd里
glBindFramebuffer(GL_FRAMEBUFFER, _oldFBO);
纹理信息保存完了自然要把原先的还原回来,然后当要用的时候再从保存好的缓冲中取出来,于是就有了newImage里的这么两句
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_oldFBO);
glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
先将原本的帧缓冲信息保存起来,然后在绑定上先前保存好的精灵的帧缓冲,把里面的信息拿出来用,
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0,0,savedBufferWidth, savedBufferHeight,GL_RGBA,GL_UNSIGNED_BYTE, tempData);
<span style="font-family:Arial;BACKGROUND-COLOR: #ffffff"></span>
偷吃完了要记得插嘴吧,newImage的最后
glBindFramebuffer(GL_FRAMEBUFFER, _oldFBO);
哪里写错,或者写的不好的地方,请大家多点评