一直对 cocos2d 的 opengl 混合机制不太明晰,昨日纠查 bug 的时候连带着注意了一下,
CCNode 中包含了一个 m_glServerState 的成员,这个东西是与 混合开启与否相关联的,
混合默认是开启的。
CCLayerColor、CCSprite 等类型里面包含了一个 m_blendFunc 成员,这个东西是与采用怎么样的混合方式相关联的。
在 CCProtocols.h 的 CCBlendProtocol 的 @brief 注释里面可以看到,
默认是采用 {GL_ONE, GL_ONE_MINUS_SRC_ALPHA} 或
{GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA} 的混合方式,选择哪种与 premultiplied alpha 相关。
当一个场景被绘制的时候,会根据子节点的 z 值来决定绘制的先后顺序,
在具体绘制某个可视子节点的时候,会根据该节点的 m_blendFunc 成员来决定用什么样的方式混合。
cocos2d 只是对 opengles 简化使用的一种封装!这里存在一个问题:
在 CCRenderTexture 上面绘制东西的时候,绘制几何图元 或 拿 sprite 对象执行 visit 动作的时候,
如果不调用 glBlendFunc 来指定混合方式的话,就会沿用绘制上一个 sprite 的混合方式。
显然这就让此次操作的结果带有不确定性,因为谁也没办法预料之前绘制的最后一个 sprite 采用的是何种混合方式。
(这里说的有点儿夸张了,实际上很多数情况下都不会对 sprite 的 blendFunc 做设置)
最稳妥的方式就是:在 CCRenderTexture 上面绘制的东西的时候即时设置一下混合方式,消除不确定性
代码:ccGLBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
需求多变,但是记住默认的混合参数是没有害处的,默认的为 {GL_ONE, GL_ONE_MINUS_SRC_ALPHA}。
另外一个问题就是,该采用何种的 shader?
毫无疑问,我在刚接触 shader 的时候也是碰了一鼻子灰,
经过一连串开发的磨砺,我才逐渐得以一窥全貌
(不敢托大,这里的全貌指的是有了一个大体的正确认识,gles1都还未能摸透就被迫迁移到2,这令我亚历山大)
cocos2d 缓存了一些常用的 shader,分别是用于一些特别的绘制情形。
打个比方来说,现在有一个需求,要绘制 50 个相同颜色的点,
要达到最高的性能,可以用 kCCShader_Position_uColor 这个枚举值所代表的 shader 来画,
这个 uColor 的 u 表示 uniform,具有 “统一” 的意思,
也就是说,不管花多少个点,都只能采用同一种颜色。
但如果我要绘制五十个不同颜色的点呢?
这确实是个问题,很显然已经超出上面那个缓存的 shader 对象所掌管的能力范围了。
不过这依然不是一个难题,用 kCCShader_PositionColor 从缓存里面拿相应的 shader 就能满足需求了~
具体方式是传入一个长度为 50 的颜色数组,再传入一个长度为 50 的位置数组,然后绘制。
挺能的啊,再出个难题!那五光十色的材质是怎么贴出来的呢?
答案也是 shader,而且是具备贴材质能力的 shader,
具体是那种我就不指明了,自己去摸索吧~
(提示:请于之前提及过的两个枚举值的定义处寻找答案)
又扩展了一些知识,当然这些知识是与主体有所关联的,
因为在 CCRenderTexture 上面绘制东西的时候也有关于 shader 方面的东西要注意,
与混合方式差不多的意思,不过这里的是 shader 是否启用 vertexArrayAttribute
没做仔细测试,不清楚 cocos2d 缓存的 kCCShader_Position_uColor 是否默认就启用了 Position 的 vertexArrayAttribute
同上,为了消除不确定因素,这里最好也是使用一下下面的代码:
ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position);
上一些代码来看一看吧(一个专门用来往 RenderTexture 上面画东西的单例类):
ItemRender.h
//// ItemRender.h// DreamStack//// Created by Bruce Yang on 12-12-26.// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.//#ifndef DreamStack_ItemRender_h#define DreamStack_ItemRender_h#include "cocos2d.h"#include "Box2D.h"USING_NS_CC;class ItemRender {public: void drawSolidPolygon(const b2Vec2* vertices, int32 vertexCount); void drawSolidCircle(const b2Vec2& center, float32 radius); static ItemRender* sharedInstance(); private: // 采用 cocos2d 缓存的 shader 对象~ void setupCachedShader(); // 采用由自己亲手创建的 shader 对象~ void setupMyShader(); ItemRender(); ~ItemRender(); static ItemRender* m_pItemRender; CCGLProgram* m_pGLProgram; GLint m_iColorLocation;};#endifItemRender.cpp
//// ItemRender.cpp// DreamStack//// Created by Bruce Yang on 12-12-26.// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.//#include "ItemRender.h"/** * p~ */void ItemRender::drawSolidPolygon(const b2Vec2* vertices, int32 vertexCount) { m_pGLProgram->use(); m_pGLProgram->setUniformForModelViewProjectionMatrix(); ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position); ccGLBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glUniform4f( m_iColorLocation, 1.f, 1.f, 1.f, 1.f); glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices); glDrawArrays(GL_TRIANGLE_FAN, 0, vertexCount); CHECK_GL_ERROR_DEBUG();}void ItemRender::drawSolidCircle(const b2Vec2& center, float32 radius) { m_pGLProgram->use(); m_pGLProgram->setUniformForModelViewProjectionMatrix(); ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position); ccGLBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); const float32 t_fSegsCount = 32.f; int t_iVertsCount = 32; const float32 t_fIncrement = 2.f * b2_pi / t_fSegsCount; float32 theta = 0.f; GLfloat glVertices[t_iVertsCount * 2]; for (int32 i = 0; i < t_fSegsCount; ++ i) { b2Vec2 v = center + radius * b2Vec2(cosf(theta), sinf(theta)); glVertices[i * 2] = v.x; glVertices[i * 2 + 1] = v.y; theta += t_fIncrement; } glUniform4f( m_iColorLocation, 1.f, 1.f, 1.f, 1.f); glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, glVertices); glDrawArrays(GL_TRIANGLE_FAN, 0, t_iVertsCount); CHECK_GL_ERROR_DEBUG();}#pragma markItemRender* ItemRender::sharedInstance() { if (!m_pItemRender) { m_pItemRender = new ItemRender(); } return m_pItemRender;}void ItemRender::setupCachedShader() { m_pGLProgram = CCShaderCache::sharedShaderCache()->programForKey(kCCShader_Position_uColor); m_iColorLocation = glGetUniformLocation(m_pGLProgram->getProgram(), "u_color");}void ItemRender::setupMyShader() { m_pGLProgram = NULL; m_iColorLocation = (GLint)0;}ItemRender::ItemRender() { this->setupCachedShader();}ItemRender::~ItemRender() { }ItemRender* ItemRender::m_pItemRender = 0;
还有就是,带 ccGL- 前缀的方法都是 cocos2d 封装的一层带缓存作用的方法。
其内部机制也非常简单,就是判断一下当前要改变到的值和老值是否相同,不同的话才去修改该值。
后续还会对 cocos2d-x 2.x,opengles 2.0 做更深入细致的探索,敬请关注~