• cocos2dx 3.x ccPositionTextureColor_vert与ccPositionTextureColor_noMVP_vert


    在cocos2dx 2.x中,如果我们要对sprite更换片段shader,写成:

    myProgram->initWithByteArrays(ccPositionTextureColor_vert, myFragSource);

    但是到3.x中,上面做法会导致显示出来的sprite坐标不对。

    于是看3.x代码,发现在3.x中,Sprite的默认shader不再是kCCShader_PositionTextureColor(即

    GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR),而是改成了GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP(见Sprite::initWithTexture(...)函数实现)。

    因此应该用:

    myProgram->initWithByteArrays(ccPositionTextureColor_noMVP_vert, myFragSource);

    显示就正常了。

    对比一下ccPositionTextureColor_vert和ccPositionTextureColor_noMVP_vert的代码:

    const char* ccPositionTextureColor_vert = STRINGIFY(

    attribute vec4 a_position;

    attribute vec2 a_texCoord;

    attribute vec4 a_color;

    #ifdef GL_ES

    varying lowp vec4 v_fragmentColor;

    varying mediump vec2 v_texCoord;

    #else

    varying vec4 v_fragmentColor;

    varying vec2 v_texCoord;

    #endif

    void main()

    {

        gl_Position = CC_MVPMatrix * a_position;

        v_fragmentColor = a_color;

        v_texCoord = a_texCoord;

    }

    );

    const char* ccPositionTextureColor_noMVP_vert = STRINGIFY(

    attribute vec4 a_position;

    attribute vec2 a_texCoord;

    attribute vec4 a_color;

    #ifdef GL_ES

    varying lowp vec4 v_fragmentColor;

    varying mediump vec2 v_texCoord;

    #else

    varying vec4 v_fragmentColor;

    varying vec2 v_texCoord;

    #endif

    void main()

    {

        gl_Position = CC_PMatrix * a_position;

        v_fragmentColor = a_color;

        v_texCoord = a_texCoord;

    }

    );

    可见唯一区别就是顶点坐标a_position乘的矩阵不同,ccPositionTextureColor_vert中乘的是CC_MVPMatrix,ccPositionTextureColor_noMVP_vert中乘的是CC_PMatrix。

    于是就明白了:

    在2.x的立即模式中,传入shader的顶点坐标为局部坐标,所以需要在shader中乘以MVP矩阵来求屏幕坐标。

    在3.x的command模式中,传入shader的顶点坐标已预先转化成了世界坐标(即已乘过MV矩阵),所以在shader中就只乘P矩阵即可。

    于是引出两个问题:

    问题1,为什么command模式下,sprite要预先转化成世界坐标?

      因为如果所有顶点都转化到了相同的空间(比如世界空间),那么它们的变换矩阵M就同一了(可以作为一个uniform变量),于是这些顶点可以一次性提交,从而减少了draw call数量。这也正是command机制的目的所在。不过需要注意的是,3.x中也并非所有的节点都使用了noMVP的形式,因为并不是所有渲染对象都如sprite这样顶点数很少适合展平做batch -- 假设有几个MV矩阵不同的顶点数很多的mesh,如果也像处理sprite一样进行展平一次性提交,虽然draw call数量下来了,可是cpu做顶点变换的开销却会大幅增加,其结果很可能反而不如多用几个draw call,但把顶点变换转移到gpu(shader)中去做效率高。所以在对一个节点更换shader的时候,要注意看一下其默认shader是有MVP还是noMVP。

    问题2,sprite预先转化成世界坐标的代码在哪?

      在void Renderer::visitRenderQueue(const RenderQueue& queue)中的if ( RenderCommand::Type::QUAD_COMMAND == commandType )分支中有一句fillQuads(cmd)。fillQuads(...)的实现如下:

    void Renderer::fillQuads(const QuadCommand *cmd)

    {

        memcpy(_quadVerts + _numberQuads * 4, cmd->getQuads(), sizeof(V3F_C4B_T2F_Quad) * cmd->getQuadCount());

        

        const Mat4& modelView = cmd->getModelView();

        

        for(ssize_t i=0; i< cmd->getQuadCount() * 4; ++i)

        {

            V3F_C4B_T2F *q = &_quadVerts[i + _numberQuads * 4];

            Vec3 *vec1 = (Vec3*)&q->vertices;

            modelView.transformPoint(vec1);

        }

        

        _numberQuads += cmd->getQuadCount();

    }

    其中的for循环就是在对quads顶点数组中的每个坐标乘以MV矩阵。

  • 相关阅读:
    jsp第六周作业
    jsp第三周作业
    JSP第二次作业
    软件测试课堂练习(3.4)
    10.21
    10.7
    9.30
    9.23
    第四次作业
    第七周作业
  • 原文地址:https://www.cnblogs.com/wantnon/p/4190341.html
Copyright © 2020-2023  润新知