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;

 

\n#ifdef GL_ES\n

varying lowp vec4 v_fragmentColor;

varying mediump vec2 v_texCoord;

\n#else\n

varying vec4 v_fragmentColor;

varying vec2 v_texCoord;

\n#endif\n

 

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;

 

\n#ifdef GL_ES\n

varying lowp vec4 v_fragmentColor;

varying mediump vec2 v_texCoord;

\n#else\n

varying vec4 v_fragmentColor;

varying vec2 v_texCoord;

\n#endif\n

 

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矩陣。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM