Overview
本范例主要用于演示 Sprite 在 V4 中的绘制流程,以帮助开发者熟悉和理解 V4 API 使用。
创建 Sprite
Sprite 创建方式与 V3 一样,并未改动:
auto visibleSize = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();
auto mySprite = Sprite::create("mysprite.png");
sprite->setPosition(Vec2(visibleSize / 2) + origin);
this->addChild(sprite);
接下来进入到 Sprite 内部,看看与 V3 相比,发生了哪些变化。
Shader 与 program
在 V3,通过 setGLProgramState
为 Sprite 指定 shader。
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
存储顶点数据。auto& pipelineDescriptor = _trianglesCommand.getPipelineDescriptor(); _programState = new (std::nothrow) backend::ProgramState(positionTextureColor_vert, positionTextureColor_frag); pipelineDescriptor.programState = _programState; _mvpMatrixLocation = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix"); _textureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture"); _alphaTextureLocation = pipelineDescriptor.programState->getUniformLocation("u_texture1");
创建 backend::VertexLayout
//set vertexLayout according to V3F_C4B_T2F structure auto& vertexLayout = _trianglesCommand.getPipelineDescriptor().vertexLayout; const auto& attributeInfo = _programState->getProgram()->getActiveAttributes(); auto iter = attributeInfo.find("a_position"); if(iter != attributeInfo.end()) { vertexLayout.setAttribute("a_position", iter->second.location, backend::VertexFormat::FLOAT3, 0, false); } iter = attributeInfo.find("a_texCoord"); if(iter != attributeInfo.end()) { vertexLayout.setAttribute("a_texCoord", iter->second.location, backend::VertexFormat::FLOAT2, offsetof(V3F_C4B_T2F, texCoords), false); } iter = attributeInfo.find("a_color"); if(iter != attributeInfo.end()) { vertexLayout.setAttribute("a_color", iter->second.location, backend::VertexFormat::UBYTE4, offsetof(V3F_C4B_T2F, colors), true); } vertexLayout.setLayout(sizeof(V3F_C4B_T2F), backend::VertexStepMode::VERTEX);
创建 backend::BlendDescriptor
backend::BlendDescriptor& blendDescriptor = _trianglesCommand.getPipelineDescriptor().blendDescriptor; blendDescriptor.blendEnabled = true; if (_blendFunc == BlendFunc::ALPHA_NON_PREMULTIPLIED) { blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::SRC_ALPHA; blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::SRC_ALPHA; blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; } else { blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::ONE; blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::ONE; blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; }
更新 shader
通过 Node::setProgramState(backend::ProgramState* programState)
,可以动态替换 shader。例如,当需要开启 alpha test 时,由于OpenGL ES 2.0 不支持 glAlphaFunc
,此时需要将原来的 fragment shader 替换成 positionTextureColorAlphaTest_frag。
auto programState = new (std::nothrow) backend::ProgramState(positionTextureColor_vert, positionTextureColorAlphaTest_frag);
_sprite->setProgramState(programState);
auto alphaLocation = programState->getUniformLocation("u_alpha_value");
programState->setUniform(alphaLocation, &alphaThreshold, sizeof(alphaThreshold));
CC_SAFE_RELEASE_NULL(programState);
更新 uniform
const auto& projectionMat = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
auto programState = _trianglesCommand.getPipelineDescriptor().programState;
if (programState && _mvpMatrixLocation)
programState->setUniform(_mvpMatrixLocation, projectionMat.m, sizeof(projectionMat.m));
更新 texture
if (_texture == nullptr || _texture->getBackendTexture() == nullptr)
return;
auto programState = _trianglesCommand.getPipelineDescriptor().programState;
programState->setTexture(_textureLocation, 0, _texture->getBackendTexture());
auto alphaTexture = _texture->getAlphaTexture();
if(alphaTexture && alphaTexture->getBackendTexture())
{
programState->setTexture(_alphaTextureLocation, 1, alphaTexture->getBackendTexture());
}
add triangle command
_trianglesCommand.init(_globalZOrder,
_texture,
_blendFunc,
_polyInfo.triangles,
transform,
flags);
renderer->addCommand(&_trianglesCommand);
为了减少 draw call 次数,在 Renderer 中会对 TriangleCommand 进行 batch。因此在 Sprite 中并未创建 backend::Buffer,而是放到了 Renderer 中。范例2 将演示当使用 CustomCommand 时如何创建 backend::Buffer 以及如何拷贝顶点数据。