回顾 Cocos2d-x 游戏的层次:导演类 CCDirector 直接控制渲染树的根节点--场景(CCScene),场景包含多个层CCLayer),层中包含多个精灵(CCSprite)。实际上,每一个上述的游戏元素都在渲染树中表示为节点(CCNode),游戏元素的归属关系就转换为了节点间的归属关系,进而形成树结构。
Scene Layer Sprite3D 都是继承于 Node 类。
所有都继承Node ,mainloop()函数就会每帧绘制,把所有Node 对象的 投影模型视图矩阵进行整合传给openGL一个一个节点进行绘制。
bool IsInBox(CCPoint point) { CCPoint pointWorld = node1->convertToWorldSpace(point); CCPoint pointTarget = node2->convertToNodeSpace(pointWorld); CCSize contentSize = node2->getContentSize(); if(0 <= pointTarget.x && pointTarget.x <= contentSize.width && 0 <= pointTarget.y && pointTarget.y <= contentSize.height) return true; }
注:上面代码中的point坐标是相对当前节点(也即相对自己)的坐标,比如当前节点A在父节点B中的坐标为(50, 100),当取A的坐标为(0,0)时,取到的是节点A的左下角位置,与节点A在父节点B中的位置无关。 得到在目标节点中的坐标同样也是相对目标节点,所以当要判断节点A是否在目标节点中的时候,只要判断转换得到的坐标的x, y是否在目标节点(0, 0)和(width,height)之间。
以上就是把一个完整的渲染的流程都梳理了一片,下面我给出了流程图,如图3。
图3:
void HelloWorld::visit(cocos2d::Renderer *renderer,const Mat4 &transform,bool transformUpdated)
{
Layer::draw(renderer,transform,transformUpdated);
//send custom command to tell the renderer to call opengl commands
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw,this);
renderer->addCommand(&_customCommand);
}
void HelloWorld::onDraw()
{
//question1: why the triangle goes to the up side
//如果使用对等矩阵,则三角形绘制会在最前面
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
auto glProgram = getGLProgram();
glProgram->use();
//set uniform values,the order of the line is very important
glProgram->setUniformsForBuiltins();
auto size = Director::getInstance()->getWinSize();
//use vao
glBindVertexArray(vao);
GLuint uColorLocation = glGetUniformLocation(glProgram->getProgram(),"u_color");
float uColor[] = {1.0,1.0,1.0};
glUniform4fv(uColorLocation,1,uColor);
// glDrawArrays(GL_TRIANGLES,6);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_BYTE,(GLvoid*)0);
glBindVertexArray(0);
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,6);
CHECK_GL_ERROR_DEBUG();
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
从代码14中,我们看到重写visit()函数,在visit()函数中直接向RenderQueue添加RenderCommand,即“renderer->addCommand(&_customCommand);”,由于此RenderCommand类型为CustomCommand,所以要添加处理图形渲染的回调函数,即“_customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw,this);”,这行代码就是添加回调函数的,onDraw()函数中调用OpengGL的API渲染图形。关于func是如何被调用,可以参考上面的代码12上下文的分析。
2.第二种方法针对个别精灵。
有时候,我们只要对个别精灵进行特效的处理,这个精灵需要使用我们自己编写的Shader,而图层其他的元素按默认处理就行了。这时候就需要第二种方法了。设置好Shader,向精灵添加Shader,最后在重写draw函数,在draw函数中进行特效的处理,如代码15,代码15是《捕鱼达人3》教程第二节的代码。
代码15:
{
...省略了不相关的代码。
// 将vsh与fsh装配成一个完整的Shader文件。
auto glprogram = GLProgram::createWithFilenames("UVAnimation.vsh","UVAnimation.fsh");
// 由Shader文件创建这个Shader
auto glprogramstate = GLProgramState::getOrCreateWithGLProgram(glprogram);
// 给精灵设置所用的Shader
m_Sprite->setGLProgramState(glprogramstate);
//创建海龟所用的贴图。
auto textrue1 = Director::getInstance()->getTextureCache()->addImage("tortoise.png");
//将贴图设置给Shader中的变量值u_texture1
glprogramstate->setUniformTexture("u_texture1",textrue1);
//创建波光贴图。
auto textrue2 = Director::getInstance()->getTextureCache()->addImage("caustics.png");
//将贴图设置给Shader中的变量值u_lightTexture
glprogramstate->setUniformTexture("u_lightTexture",textrue2);
//注意,对于波光贴图,我们希望它在进行UV动画时能产生四方连续效果,必须设置它的纹理UV寻址方式为GL_REPEAT。
Texture2D::TexParams tRepeatParams;
tRepeatParams.magFilter = GL_LINEAR_MIPMAP_LINEAR;
tRepeatParams.minFilter = GL_LINEAR;
tRepeatParams.wrapS = GL_REPEAT;
tRepeatParams.wrapT = GL_REPEAT;
textrue2->setTexParameters(tRepeatParams);
//在这里,我们设置一个波光的颜色,这里设置为白色。
Vec4 tLightColor(1.0,1.0);
glprogramstate->setUniformVec4("v_LightColor",tLightColor);
//下面这一段,是为了将我们自定义的Shader与我们的模型顶点组织方式进行匹配。模型的顶点数据一般包括位置,法线,色彩,纹理,以及骨骼绑定信息。而Shader需要将内部相应的顶点属性通道与模型相应的顶点属性数据进行绑定才能正确显示出顶点。
long offset = 0;
auto attributeCount = m_Sprite->getMesh()->getMeshVertexAttribCount();
for (auto k = 0; k < attributeCount; k++) {
auto meshattribute = m_Sprite->getMesh()->getMeshVertexAttribute(k);
glprogramstate->setVertexAttribPointer(s_attributeNames[meshattribute.vertexAttrib],
meshattribute.size,
meshattribute.type,
GL_FALSE,
m_Sprite->getMesh()->getVertexSizeInBytes(),
(GLvoid*)offset);
offset += meshattribute.attribSizeBytes;
}
//uv滚动初始值设为0
m_LightAni.x = m_LightAni.y = 0;
return true;
}
void FishLayer::draw(Renderer* renderer,uint32_t flags)
{
if(m_Sprite)
{
//乌龟从右向左移动,移出屏幕后就回到最右边
auto s = Director::getInstance()->getWinSize();
m_Sprite->setPositionX(m_Sprite->getPositionX()-1);
if(m_Sprite->getPositionX() < -100)
{
m_Sprite->setPositionX(s.width + 10);
}
auto glprogramstate = m_Sprite->getGLProgramState();
if(glprogramstate)
{
m_LightAni.x += 0.01;
if(m_LightAni.x > 1.0)
{
m_LightAni.x-= 1.0;
}
m_LightAni.y += 0.01;
if(m_LightAni.y > 1.0)
{
m_LightAni.y-= 1.0;
}
glprogramstate->setUniformVec2("v_animLight",m_LightAni);
}
}
Node::draw(renderer,flags);
从代码15中,我们可以看到先使用OpengGL的API创建自己的Shader,然后再把m_sprite的Shader设置为自己的Shader即“m_Sprite->setGLProgramState(glprogramstate);”,这是给精灵设置所用的Shader,这就是针对个别的精灵,而不是整个图层。接着在draw()中,如果精灵已生成,每次调用draw()函数都改变Shader中参数,以达到特别的效果