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 以及如何拷贝顶点数据。