• Overview
  • 创建 Sprite
    • Shader 与 program
    • 更新 shader
    • 更新 uniform
    • 更新 texture
    • add triangle command

    Overview

    本范例主要用于演示 Sprite 在 V4 中的绘制流程,以帮助开发者熟悉和理解 V4 API 使用。

    创建 Sprite

    Sprite 创建方式与 V3 一样,并未改动:

    1. auto visibleSize = Director::getInstance()->getVisibleSize();
    2. auto origin = Director::getInstance()->getVisibleOrigin();
    3. auto mySprite = Sprite::create("mysprite.png");
    4. sprite->setPosition(Vec2(visibleSize / 2) + origin);
    5. this->addChild(sprite);

    接下来进入到 Sprite 内部,看看与 V3 相比,发生了哪些变化。

    Shader 与 program

    在 V3,通过 setGLProgramState 为 Sprite 指定 shader。

    1. setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, texture));

    V4 移除了 GLProgramState,删除了在 app 中 直接使用 OpenGL ES API 的代码,OpenGL ES API 的使用只出现在 renderer/backend/opengl 文件中。

    V4 在 RenderCommand 类中定义了一个 protected 成员 backend::PipelineDescriptor,用于保存 backend::ProgramState, backend::BlendDescriptor 及 backend::VertexLayout。

    因此在初始化阶段,需要完成以下三个步骤:

    • 创建 backend::ProgramState

    通过 ProgramState(const std::string& vertexShader, const std::string& fragmentShader) 创建 backend::ProgramState 对象,通过 backend::ProgramState 对象存储 uniform 和 texture,通过创建 backend::Buffer 存储顶点数据。

    1. auto& pipelineDescriptor = _trianglesCommand.getPipelineDescriptor();
    2. _programState = new (std::nothrow) backend::ProgramState(positionTextureColor_vert, positionTextureColor_frag);
    3. pipelineDescriptor.programState = _programState;
    4. _mvpMatrixLocation = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix");
    5. _textureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture");
    6. _alphaTextureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture1");
    • 创建 backend::VertexLayout
    1. //set vertexLayout according to V3F_C4B_T2F structure
    2. auto& vertexLayout = _trianglesCommand.getPipelineDescriptor().vertexLayout;
    3. const auto& attributeInfo = _programState->getProgram()->getActiveAttributes();
    4. auto iter = attributeInfo.find("a_position");
    5. if(iter != attributeInfo.end())
    6. {
    7. vertexLayout.setAttribute("a_position",
    8. iter->second.location,
    9. backend::VertexFormat::FLOAT3,
    10. 0,
    11. false);
    12. }
    13. iter = attributeInfo.find("a_texCoord");
    14. if(iter != attributeInfo.end())
    15. {
    16. vertexLayout.setAttribute("a_texCoord",
    17. iter->second.location,
    18. backend::VertexFormat::FLOAT2,
    19. offsetof(V3F_C4B_T2F, texCoords),
    20. false);
    21. }
    22. iter = attributeInfo.find("a_color");
    23. if(iter != attributeInfo.end())
    24. {
    25. vertexLayout.setAttribute("a_color",
    26. iter->second.location,
    27. backend::VertexFormat::UBYTE4,
    28. offsetof(V3F_C4B_T2F, colors),
    29. true);
    30. }
    31. vertexLayout.setLayout(sizeof(V3F_C4B_T2F), backend::VertexStepMode::VERTEX);
    • 创建 backend::BlendDescriptor
    1. backend::BlendDescriptor& blendDescriptor = _trianglesCommand.getPipelineDescriptor().blendDescriptor;
    2. blendDescriptor.blendEnabled = true;
    3. if (_blendFunc == BlendFunc::ALPHA_NON_PREMULTIPLIED)
    4. {
    5. blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::SRC_ALPHA;
    6. blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
    7. blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::SRC_ALPHA;
    8. blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
    9. }
    10. else
    11. {
    12. blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::ONE;
    13. blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
    14. blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::ONE;
    15. blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
    16. }

    更新 shader

    通过 Node::setProgramState(backend::ProgramState* programState),可以动态替换 shader。例如,当需要开启 alpha test 时,由于OpenGL ES 2.0 不支持 glAlphaFunc,此时需要将原来的 fragment shader 替换成 positionTextureColorAlphaTest_frag。

    1. auto programState = new (std::nothrow) backend::ProgramState(positionTextureColor_vert, positionTextureColorAlphaTest_frag);
    2. _sprite->setProgramState(programState);
    3. auto alphaLocation = programState->getUniformLocation("u_alpha_value");
    4. programState->setUniform(alphaLocation, &alphaThreshold, sizeof(alphaThreshold));
    5. CC_SAFE_RELEASE_NULL(programState);

    更新 uniform

    1. const auto& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    2. auto programState = _trianglesCommand.getPipelineDescriptor().programState;
    3. if (programState && _mvpMatrixLocation)
    4. programState->setUniform(_mvpMatrixLocation, projectionMat.m, sizeof(projectionMat.m));

    更新 texture

    1. if (_texture == nullptr || _texture->getBackendTexture() == nullptr)
    2. return;
    3. auto programState = _trianglesCommand.getPipelineDescriptor().programState;
    4. programState->setTexture(_textureLocation, 0, _texture->getBackendTexture());
    5. auto alphaTexture = _texture->getAlphaTexture();
    6. if(alphaTexture && alphaTexture->getBackendTexture())
    7. {
    8. programState->setTexture(_alphaTextureLocation, 1, alphaTexture->getBackendTexture());
    9. }

    add triangle command

    1. _trianglesCommand.init(_globalZOrder,
    2. _texture,
    3. _blendFunc,
    4. _polyInfo.triangles,
    5. transform,
    6. flags);
    7. renderer->addCommand(&_trianglesCommand);

    为了减少 draw call 次数,在 Renderer 中会对 TriangleCommand 进行 batch。因此在 Sprite 中并未创建 backend::Buffer,而是放到了 Renderer 中。范例2 将演示当使用 CustomCommand 时如何创建 backend::Buffer 以及如何拷贝顶点数据。