3.x的渲染机制改动比较大,改变了之前在每帧遍历所有的Node树节点时通过draw()方法中的直接渲染,而是通过生成渲染指令并将渲染指令发送到渲染队列,在每一帧结束时进行延迟渲染,这样就把游戏循环和渲染分离开来。
Cocos2d-x提供了一个RenderTexture类来支持把帧缓冲中的数据渲染到一块纹理之上,通过它可以来实现游戏截图,获取纹理上的像素信息等效果,在3.x的RenderTexutrue则是使用了GroupCommand组渲染指令,将其内部一系列的渲染指令放到一个渲染队列中,这样这个组内的渲染指令将单独使用一个渲染队列,不受其他渲染指令的影响(如渲染的优先级)。
我使用的是Cocos2d-x3.2的版本,RenderTexture类在2d/misc-nodes/CCRenderTexture.h/.cpp下。
把源码都加了注释,可以通过RenderTexture整个类的构造->初始化->绘制过程->析构,来大致了解一下RenderTextrue是如何将这缓冲的数据渲染到纹理的工作流程。
CCRenderTexture.h文件
#ifndef __CCRENDER_TEXTURE_H__
#define __CCRENDER_TEXTURE_H__
#include "2d/CCNode.h"
#include "2d/CCSprite.h"
#include "platform/CCImage.h"
#include "renderer/CCGroupCommand.h"
#include "renderer/CCCustomCommand.h"
NS_CC_BEGIN
class EventCustom;
class CC_DLL RenderTexture : public Node
{
protected:
//flags: whether generate new modelView and projection matrix or not
//是否保持矩阵
bool _keepMatrix;
Rect _rtTextureRect;
Rect _fullRect;
Rect _fullviewPort;
//帧缓冲对象
GLuint _FBO;
//深度渲染缓冲对象
GLuint _depthRenderBufffer;
//记录默认帧缓冲对象
GLint _oldFBO;
//纹理对象
Texture2D* _texture;
Texture2D* _textureCopy; // a copy of _texture
//Image对象
Image* _UITextureImage;
//客户端像素格式
Texture2D::PixelFormat _pixelFormat;
// code for "auto" update
//清屏标识位
GLbitfield _clearFlags;
//颜色缓冲区清屏色
Color4F _clearColor;
//深度缓冲区清屏值
GLclampf _clearDepth;
//模板缓冲区清屏值
GLint _clearStencil;
//是否自动绘制
bool _autoDraw;
/** The Sprite being used.
The sprite, by default, will use the following blending function:
GL_ONE, GL_ONE_MINUS_SRC_ALPHA.
The blending function can be changed in runtime by calling:
- renderTexture->getSprite()->setBlendFunc((BlendFunc){
GL_ONE, GL_ONE_MINUS_SRC_ALPHA});
*/
//渲染到纹理时用到的精灵
Sprite* _sprite;
//组渲染指令(将下面所有的渲染指令放到同一个渲染队列中执行)
GroupCommand _groupCommand;
//开始时清屏渲染指令
CustomCommand _beginWithClearCommand;
//清理深度缓冲区渲染指令
CustomCommand _clearDepthCommand;
//清屏渲染指令
CustomCommand _clearCommand;
//开始渲染指令
CustomCommand _beginCommand;
//结束渲染指令
CustomCommand _endCommand;
//将纹理保存到文件的渲染指令
CustomCommand _saveToFileCommand;
Mat4 _oldTransMatrix, _oldProjMatrix;
Mat4 _transformMatrix, _projectionMatrix;
};
// end of textures group
/// @}
NS_CC_END
#endif //__CCRENDER_TEXTURE_H__
CCRenderTexture.cpp文件
#include "2d/CCRenderTexture.h"
#include "base/ccUtils.h"
#include "platform/CCImage.h"
#include "platform/CCFileUtils.h"
#include "2d/CCGrid.h"
#include "base/CCEventType.h"
#include "base/CCConfiguration.h"
#include "base/CCConfiguration.h"
#include "base/CCDirector.h"
#include "base/CCEventListenerCustom.h"
#include "base/CCEventDispatcher.h"
#include "renderer/CCGLProgram.h"
#include "renderer/ccGLStateCache.h"
#include "renderer/CCTextureCache.h"
#include "renderer/CCRenderer.h"
#include "renderer/CCGroupCommand.h"
#include "renderer/CCCustomCommand.h"
#include "CCGL.h"
NS_CC_BEGIN
// implementation RenderTexture
RenderTexture::RenderTexture()
: _FBO(0)
, _depthRenderBufffer(0)
, _oldFBO(0)
, _texture(0)
, _textureCopy(0)
, _UITextureImage(nullptr)
, _pixelFormat(Texture2D::PixelFormat::RGBA8888)
, _clearFlags(0)
, _clearColor(Color4F(0,0,0,0))
, _clearDepth(0.0f)
, _clearStencil(0)
, _autoDraw(false)
, _sprite(nullptr)
, _keepMatrix(false)
, _rtTextureRect(Rect::ZERO)
, _fullRect(Rect::ZERO)
, _fullviewPort(Rect::ZERO)
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
// Listen this event to save render texture before come to background.
// Then it can be restored after coming to foreground on Android.
//使用事件机制(observer)来监听切到后台和切回前台事件,设置响应的回调方法
auto toBackgroundListener = EventListenerCustom::create(EVENT_COME_TO_BACKGROUND, CC_CALLBACK_1(RenderTexture::listenToBackground, this));
_eventDispatcher->addEventListenerWithSceneGraphPriority(toBackgroundListener, this);
auto toForegroundListener = EventListenerCustom::create(EVENT_COME_TO_FOREGROUND, CC_CALLBACK_1(RenderTexture::listenToForeground, this));
_eventDispatcher->addEventListenerWithSceneGraphPriority(toForegroundListener, this);
#endif
}
RenderTexture::~RenderTexture()
{
CC_SAFE_RELEASE(_sprite);
CC_SAFE_RELEASE(_textureCopy);
//释放帧缓冲对象
glDeleteFramebuffers(1, &_FBO);
if (_depthRenderBufffer)
{
//释放深度渲染缓冲对象
glDeleteRenderbuffers(1, &_depthRenderBufffer);
}
CC_SAFE_DELETE(_UITextureImage);
}
/**
* 进入游戏后台的事件回调
*
* @param event 事件对象
*/
void RenderTexture::listenToBackground(EventCustom *event)
{
// We have not found a way to dispatch the enter background message
// before the texture data are destroyed.
// So we disable this pair of message handler at present.
#if 0
//使用了纹理缓存,Android Activiy切到后台时会将纹理缓存释放,切回前台时重新加载
#if CC_ENABLE_CACHE_TEXTURE_DATA
//释放之前的渲染目标
CC_SAFE_DELETE(_UITextureImage);
// to get the rendered texture data
//创建新的渲染目标,是一个Image对象
_UITextureImage = newImage(false);
if (_UITextureImage)
{
//获取纹理的大小
const Size& s = _texture->getContentSizeInPixels();
//在Android平台Activity切换到后台时,纹理将被释放,记录下切换到后台时的纹理信息,
//以便切回前台时重新创建
VolatileTextureMgr::addDataTexture(_texture, _UITextureImage->getData(), s.width * s.height * 4, Texture2D::PixelFormat::RGBA8888, s);
if ( _textureCopy )
{
VolatileTextureMgr::addDataTexture(_textureCopy, _UITextureImage->getData(), s.width * s.height * 4, Texture2D::PixelFormat::RGBA8888, s);
}
}
else
{
CCLOG("Cache rendertexture failed!");
}
//释放帧缓冲对象
glDeleteFramebuffers(1, &_FBO);
_FBO = 0;
#endif
#endif
}
/**
* 游戏切回前台时间回调
*
* @param event 事件对象
*/
void RenderTexture::listenToForeground(EventCustom *event)
{
#if 0
#if CC_ENABLE_CACHE_TEXTURE_DATA
// -- regenerate frame buffer object and attach the texture
//检查帧缓冲绑定状态,返回到_obdFBO中
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_oldFBO);
//生成帧缓冲对象
glGenFramebuffers(1, &_FBO);
//绑定帧缓冲对象
glBindFramebuffer(GL_FRAMEBUFFER, _FBO);
//不使用抗锯齿模糊
_texture->setAliasTexParameters();
if ( _textureCopy )
{
_textureCopy->setAliasTexParameters();
}
//将帧缓冲数据输出到纹理
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture->getName(), 0