书中的示例部分实际操作一下,对于内容的理解还是十分有帮助的。
第四章 4.9 示例:自定义RenderCommand
阅读的时候关于顶点数组、着色器程序部分是十分困惑的。这很正常,因为这是后面第七章的内容。在180页开始,会有很详细的介绍。此外,本例中的主要的两个类 ShowPlygonLayer 和 TrangleCommand 有部分方法的实现书中没有展示。可能是作者觉得太简单,没必要。但自己实现的时候还是要去想一想的。
完整代码如下:
TriangleCommand.h
class TriangleCommand : public CustomCommand
{
public:
TriangleCommand();
void init(int globalOrder, GLProgram* shader, Vec3* vertices, Color4F* colors, GLuint* indices, int indexCount, const Mat4& mv);
void onDraw();
private:
Color4F* _squareColors;
Vec3* _noMVPVertices;
GLuint* _indices;
int _vertexCount;
Mat4 _mv;
GLProgram* _shader;
};
TriangleCommand.cpp
void TriangleCommand::init(int globalOrder, GLProgram* shader, Vec3* vertices, Color4F* colors, GLuint* indices, int indexCount, const Mat4& mv)
{
_globalOrder = globalOrder;
_shader = shader;
_noMVPVertices = vertices;
_squareColors = colors;
_indices = indices;
_vertexCount = indexCount;
_mv = mv;
// 书中只描述没有代码的地方
func = CC_CALLBACK_0(TriangleCommand::onDraw, this);
}
void TriangleCommand::onDraw()
{
// 书中的useMaterial方法 就直接用这两行代替了
_shader->use();
_shader->setUniformsForBuiltins(_mv);
GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR );
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0, _noMVPVertices);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, _squareColors);
glDrawElements(GL_TRIANGLES, _vertexCount, GL_UNSIGNED_INT, _indices);
}
ShowPolygonLayer.h
class ShowPolygonLayer : public Node
{
public:
CREATE_FUNC(ShowPolygonLayer);
void setRectangle(Rect& rect);
private:
virtual bool init() override;
virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
GLuint _indices[24];
Color4F _squareColors[8];
TriangleCommand _customCommand;
Vec3 _noMVPVertices[8];
Vec2 _squareVertices[8];
};
ShowPolygonLayer.cpp
bool ShowPolygonLayer::init()
{
// 书中没有代码的地方 固定的顶点索引 就是后面indices的值
// 就是三角形会用到的顶点的索引值
_indices[0] = 0;
_indices[1] = 1;
_indices[2] = 2;
_indices[3] = 1;
_indices[4] = 2;
_indices[5] = 3;
_indices[6] = 1;
_indices[7] = 3;
_indices[8] = 4;
_indices[9] = 3;
_indices[10] = 4;
_indices[11] = 5;
_indices[12] = 4;
_indices[13] = 5;
_indices[14] = 6;
_indices[15] = 4;
_indices[16] = 6;
_indices[17] = 7;
_indices[18] = 2;
_indices[19] = 6;
_indices[20] = 7;
_indices[21] = 0;
_indices[22] = 2;
_indices[23] = 7;
// 书中没有代码的地方 按需求应该统一是带透明度的黑色
// 这里为了看下效果用了彩色
_squareColors[0] = Color4F::RED;
_squareColors[1] = Color4F::ORANGE;
_squareColors[2] = Color4F::YELLOW;
_squareColors[3] = Color4F::GREEN;
_squareColors[4] = Color4F::GRAY;
_squareColors[5] = Color4F::BLUE;
_squareColors[6] = Color4F::GREEN;
_squareColors[7] = Color4F::WHITE;
// 书中没有代码的地方 设置着色器程序 这里是参照LayerColor的方法 setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_COLOR_NO_MVP));
return true;
}
void ShowPolygonLayer::setRectangle(Rect& rect)
{
// 书中用的是getVisibleSize() 运行后会有一小部分没有渲染 改成WinSize就是全屏幕了
// 可能和分辨率策略有关 还没有详细看这里
Size size = Director::getInstance()->getWinSize(); //Director::getInstance()->getVisibleSize();
_squareVertices[0] = Vec2(0, 0);
_squareVertices[1] = Vec2(size.width, 0);
_squareVertices[2] = Vec2(rect.origin.x, rect.origin.y);
_squareVertices[3] = Vec2(rect.origin.x + rect.size.width, rect.origin.y);
_squareVertices[4] = Vec2(size.width, size.height);
_squareVertices[5] = Vec2(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height);
_squareVertices[6] = Vec2(rect.origin.x, rect.origin.y + rect.size.height);
_squareVertices[7] = Vec2(0, size.height);
}
void ShowPolygonLayer::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
for (int i = 0; i < 8; ++i)
{
Vec4 pos;
pos.x = _squareVertices[i].x;
pos.y = _squareVertices[i].y;
pos.z = getPositionZ();
pos.w = 1;
_modelViewTransform.transformVector(&pos);
_noMVPVertices[i] = Vec3(pos.x, pos.y, pos.z) / pos.w;
}
_customCommand.init(_globalZOrder, getGLProgram(), _noMVPVertices, _squareColors, _indices, 24, transform);
renderer->addCommand(&_customCommand);
}
最后在HelloWorld::init()添加使用
bool HelloWorld::init()
{
... ... // 略掉的原有内容
ShowPolygonLayer* pLayer = ShowPolygonLayer::create();
Rect rect(200, 100, 100, 100);
pLayer->setRectangle(rect);
addChild(pLayer);
return true;
}
几个卡住的地方:
1、着色器程序的设置。着色器程序是以参数传给自定义的渲染类的,但前提是得先创建了。不然渲染类实际使用的时候是空的。着色器程序的创建是用的LayerColor同样的方法。
2、LayerColor和本例很像,但它没有额外创建一个RenderCommand,直接把onDraw写到了LayerColor类里。然后使用CustomrRender,将onDraw方法作为CustomrRender的回调函数。但本例想做一个可复用的RenderCommand,所以这里有变化。
void LayerColor::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
_customCommand.init(_globalZOrder, transform, flags);
_customCommand.func = CC_CALLBACK_0(LayerColor::onDraw, this, transform, flags);
renderer->addCommand(&_customCommand);
for(int i = 0; i < 4; ++i)
{
Vec4 pos;
pos.x = _squareVertices[i].x; pos.y = _squareVertices[i].y; pos.z = _positionZ;
pos.w = 1;
_modelViewTransform.transformVector(&pos);
_noMVPVertices[i] = Vec3(pos.x,pos.y,pos.z)/pos.w;
}
}
最开始我使用的方法跟这里很像,但是运行一段时间会崩溃,看堆栈信息像是出现了死循环。后来把设置func回调函数的方法放到RenderCommand的init函数里执行,就不会有这个问题了。
_customCommand.init(_globalZOrder, getGLProgram(), _noMVPVertices, _squareColors, _indices, 24, transform);
// ❌ 错误 会导致死循环
_customCommand.func = CC_CALLBACK_0(TriangleCommand::onDraw, _customCommand);
renderer->addCommand(&_customCommand);
代码运行效果: