您的位置:首页 > 其它

在使用 CCRenderTexture、shader 绘制几何图元时需要注意的一些细节问题

2012-12-27 13:24 706 查看
一直对 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;
};

#endif


ItemRender.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 mark

ItemRender* 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 做更深入细致的探索,敬请关注~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: