[cocos2d-x] 《我所理解的Cocos2d-x》上机实验1-自定义RenderCommand

书中的示例部分实际操作一下,对于内容的理解还是十分有帮助的。

第四章 4.9 示例:自定义RenderCommand

阅读的时候关于顶点数组、着色器程序部分是十分困惑的。这很正常,因为这是后面第七章的内容。在180页开始,会有很详细的介绍。此外,本例中的主要的两个类 ShowPlygonLayer 和 TrangleCommand 有部分方法的实现书中没有展示。可能是作者觉得太简单,没必要。但自己实现的时候还是要去想一想的。

完整代码如下:

TriangleCommand.h

class TriangleCommand : public CustomCommand
{
public:
    TriangleCommand();
    
    void init(int globalOrder, GLProgram* shader, Vec3* vertices, Color4F* colors, GLuint* indices, int indexCount, const Mat4& mv);
    void onDraw();

private:    
    Color4F* _squareColors;
    Vec3* _noMVPVertices;
    GLuint* _indices;
    int _vertexCount;
    Mat4 _mv;
    GLProgram* _shader;
};

TriangleCommand.cpp

void TriangleCommand::init(int globalOrder, GLProgram* shader, Vec3* vertices, Color4F* colors, GLuint* indices, int indexCount, const Mat4& mv)
{
    _globalOrder = globalOrder;
    _shader = shader;
    _noMVPVertices = vertices;
    _squareColors = colors;
    _indices = indices;
    _vertexCount = indexCount;
    _mv = mv;
    
    // 书中只描述没有代码的地方
    func = CC_CALLBACK_0(TriangleCommand::onDraw, this);
}

void TriangleCommand::onDraw()
{
     // 书中的useMaterial方法 就直接用这两行代替了
    _shader->use();
    _shader->setUniformsForBuiltins(_mv);
    
    GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR );
    
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0, _noMVPVertices);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, _squareColors);
    
    glDrawElements(GL_TRIANGLES, _vertexCount, GL_UNSIGNED_INT, _indices);
}

ShowPolygonLayer.h

class ShowPolygonLayer : public Node
{
public:
    CREATE_FUNC(ShowPolygonLayer);
    void setRectangle(Rect& rect);
    
private:
    virtual bool init() override;    
    virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
    
    GLuint _indices[24];
    Color4F  _squareColors[8];
    TriangleCommand _customCommand;
    Vec3 _noMVPVertices[8];
    Vec2 _squareVertices[8];
};

ShowPolygonLayer.cpp

bool ShowPolygonLayer::init()
{
    // 书中没有代码的地方 固定的顶点索引 就是后面indices的值
    // 就是三角形会用到的顶点的索引值
    _indices[0] = 0;
    _indices[1] = 1;
    _indices[2] = 2;
    
    _indices[3] = 1;
    _indices[4] = 2;
    _indices[5] = 3;
    
    _indices[6] = 1;
    _indices[7] = 3;
    _indices[8] = 4;
    
    _indices[9] = 3;
    _indices[10] = 4;
    _indices[11] = 5;
    
    _indices[12] = 4;
    _indices[13] = 5;
    _indices[14] = 6;
    
    _indices[15] = 4;
    _indices[16] = 6;
    _indices[17] = 7;
    
    _indices[18] = 2;
    _indices[19] = 6;
    _indices[20] = 7;
    
    _indices[21] = 0;
    _indices[22] = 2;
    _indices[23] = 7;
    
    // 书中没有代码的地方 按需求应该统一是带透明度的黑色
    // 这里为了看下效果用了彩色
    _squareColors[0] = Color4F::RED;
    _squareColors[1] = Color4F::ORANGE;
    _squareColors[2] = Color4F::YELLOW;
    _squareColors[3] = Color4F::GREEN;
    _squareColors[4] = Color4F::GRAY;
    _squareColors[5] = Color4F::BLUE;
    _squareColors[6] = Color4F::GREEN;
    _squareColors[7] = Color4F::WHITE;
    
    // 书中没有代码的地方 设置着色器程序 这里是参照LayerColor的方法    setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_COLOR_NO_MVP));
    return true;
}

void ShowPolygonLayer::setRectangle(Rect& rect)
{
    // 书中用的是getVisibleSize() 运行后会有一小部分没有渲染 改成WinSize就是全屏幕了
    // 可能和分辨率策略有关 还没有详细看这里
    Size size = Director::getInstance()->getWinSize(); //Director::getInstance()->getVisibleSize();
    
    _squareVertices[0] = Vec2(0, 0);
    _squareVertices[1] = Vec2(size.width, 0);
    _squareVertices[2] = Vec2(rect.origin.x, rect.origin.y);
    _squareVertices[3] = Vec2(rect.origin.x + rect.size.width, rect.origin.y);
    _squareVertices[4] = Vec2(size.width, size.height);
    _squareVertices[5] = Vec2(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height);
    _squareVertices[6] = Vec2(rect.origin.x, rect.origin.y + rect.size.height);
    _squareVertices[7] = Vec2(0, size.height);
}

void ShowPolygonLayer::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    for (int i = 0; i < 8; ++i)
    {
        Vec4 pos;
        pos.x = _squareVertices[i].x;
        pos.y = _squareVertices[i].y;
        pos.z = getPositionZ();
        pos.w = 1;
        
        _modelViewTransform.transformVector(&pos);
        _noMVPVertices[i] = Vec3(pos.x, pos.y, pos.z) / pos.w;
    }
    
    _customCommand.init(_globalZOrder, getGLProgram(), _noMVPVertices, _squareColors, _indices, 24, transform);
    renderer->addCommand(&_customCommand);
}

最后在HelloWorld::init()添加使用

bool HelloWorld::init()
{
    ... ... // 略掉的原有内容

    ShowPolygonLayer* pLayer = ShowPolygonLayer::create();
    Rect rect(200, 100, 100, 100);
    pLayer->setRectangle(rect);
    addChild(pLayer);

    return true;
}

几个卡住的地方:

1、着色器程序的设置。着色器程序是以参数传给自定义的渲染类的,但前提是得先创建了。不然渲染类实际使用的时候是空的。着色器程序的创建是用的LayerColor同样的方法。

2、LayerColor和本例很像,但它没有额外创建一个RenderCommand,直接把onDraw写到了LayerColor类里。然后使用CustomrRender,将onDraw方法作为CustomrRender的回调函数。但本例想做一个可复用的RenderCommand,所以这里有变化。

void LayerColor::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    _customCommand.init(_globalZOrder, transform, flags);
    _customCommand.func = CC_CALLBACK_0(LayerColor::onDraw, this, transform, flags);
    renderer->addCommand(&_customCommand);
    
    for(int i = 0; i < 4; ++i)
    {
        Vec4 pos;
        pos.x = _squareVertices[i].x; pos.y = _squareVertices[i].y; pos.z = _positionZ;
        pos.w = 1;
        _modelViewTransform.transformVector(&pos);
        _noMVPVertices[i] = Vec3(pos.x,pos.y,pos.z)/pos.w;
    }
}

最开始我使用的方法跟这里很像,但是运行一段时间会崩溃,看堆栈信息像是出现了死循环。后来把设置func回调函数的方法放到RenderCommand的init函数里执行,就不会有这个问题了。

    _customCommand.init(_globalZOrder, getGLProgram(), _noMVPVertices, _squareColors, _indices, 24, transform);
    // ❌ 错误 会导致死循环 
    _customCommand.func = CC_CALLBACK_0(TriangleCommand::onDraw, _customCommand);
    renderer->addCommand(&_customCommand);

代码运行效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值