OpenGL渲染管线
Cocos2D-X底层是基于OpenGL的,所以在变现我们自己的Shader之前,得了解一下渲染管线的知识。
如上图所示,OpenGL的渲染管线主要包括:
1、准备顶点数据(通过VBO--顶点缓冲区对象,VAO--顶点数组对象和Vertex attribute来传递数据给OpenGL)
2、顶点处理(这里主要由Vertex Shader来完成,从上图中可以看出,它还包括可选的Tessellation和Geometry shader阶段)
3、顶点后处理(主要包括Clipping,顶点坐标归一化和viewport变换)
4、Primitive组装(比如3点组装成一个3角形)
5、光栅化成一个个像素
6、使用Fragment shader来处理这些像素
7、采样处理(主要包括Scissor Test, Depth Test, Blending, Stencil Test等)
具体过程如下图:
使用OpenGL ES 编写Shader文件
Shader包含两个文件:***.vert和***.frag文件。在工程../cocos2d/cocos/renderer/里,包含了一批cocos自带的shader文件。
***.vert文件是作用于每一个顶点。渲染节点中包含多少个顶点,vert文件就执行多少次。
***.frag文件是定义最终显示在屏幕上每个像素点的颜色。
Shader代码可以以.vert和.frag文件的方式存储,也可以直接以字符串的方式存储在代码中。下面我们分别介绍着两种实现方式:
用独立文件存储Shader代码
这种方式自己没有实现,直接贴出网上的方法链接。(期待后续!)
直接在程序中创建Shader代码
<span style="color:#333333;">bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//this->setGLProgram(GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_COLOR));
<span style="white-space:pre"> </span>//***************************************************
GLchar* myVertextShader =
"attribute vec4 a_position; \n \
attribute vec4 a_color; \n \
varying vec4 v_fragmentColor; \n \
void main() \n \
{ \n \
gl_Position = CC_MVPMatrix * a_position; \n \
v_fragmentColor = a_color; \n \
}";
GLchar* myFragmentShader =
"varying vec4 v_fragmentColor; \n \
void main() \n \
{ \n \
float gray = dot(v_fragmentColor.rgb, vec3(0.299, 0.587, 0.114)); \n \
gl_FragColor = vec4(gray, gray, gray, v_fragmentColor.a); \n \
}";
auto program = new GLProgram;
program->initWithVertexShaderByteArray(myVertextShader, myFragmentShader);
program->link();
program->updateUniforms();
this->setShaderProgram(program);
program->release();
return true;
}
void HelloWorld::visit(Renderer* renderer, const Mat4& parentTransform, uint32_t parentFlags)
{
Layer::visit(renderer, parentTransform, parentFlags);
_command.init(_globalZOrder);
_command.func = CC_CALLBACK_0(HelloWorld::onDraw, this);
Director::getInstance()->getRenderer()->addCommand(&_command);
}
void HelloWorld::onDraw()
{
//获得当前HelloWorld的shader
auto glProgram = getGLProgram();
//使用此shader
glProgram->use();
//设置该shader的一些内置uniform,主要是MVP,即model-view-project矩阵
glProgram->setUniformsForBuiltins();
auto size = Director::getInstance()->getWinSize();
//指定将要绘制的三角形的三个顶点,分别位到屏幕左下角,右下角和正中间的顶端
float vertercies[] = { 0,0, //第一个点的坐标
size.width, 0, //第二个点的坐标
size.width / 2, size.height}; //第三个点的坐标
//指定每一个顶点的颜色,颜色值是RGBA格式的,取值范围是0-1
float color[] = { 0, 1,0, 1, //第一个点的颜色,绿色
1,0,0, 1, //第二个点的颜色, 红色
0, 0, 1, 1}; //第三个点的颜色, 蓝色
//激活名字为position和color的vertex attribute
GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR);
//分别给position和color指定数据源
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertercies);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, color);
//绘制三角形,所谓的draw call就是指这个函数调用
glDrawArrays(GL_TRIANGLES, 0, 3);
//通知cocos2d-x 的renderer,让它在合适的时候调用这些OpenGL命令
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 3);
//如果出错了,可以使用这个函数来获取出错信息
CHECK_GL_ERROR_DEBUG();
}</span>
int()中******分隔线下就是修改的代码,运行后效果如图:
float gray = dot(v_fragmentColor.rgb, vec3(0.299, 0.587, 0.114)); \n \
gl_FragColor = vec4(gray, gray, gray, v_fragmentColor.a); \n \
的作用就是使图片颜色置灰。
使用VBO缓存数据
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
//重载visit函数
virtual void visit(Renderer* renderer, const Mat4& parentTransform, uint32_t parentFlags) override;
//自定义渲染函数
void onDraw();
private:
//渲染命令封装类
CustomCommand _command;
};
#endif // __HELLOWORLD_SCENE_H__
#include "HelloWorldScene.h"
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
// add "HelloWorld" splash screen"
// auto sprite = Sprite::create("1005.png");
//auto _img = new Image();
//_img->initWithImageFile("1.tga");
//Texture2D* texture = initTextureWithImage(_img, 200);
//auto sprite2 = Sprite::createWithTexture(texture);
// // position the sprite on the center of the screen
// sprite2->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
//sprite->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y));
// // add the sprite as a child to this layer
//this->addChild(sprite, 0);
// this->addChild(sprite2, 0);
//this->setGLProgram(GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_COLOR));
GLchar* myVertextShader =
"attribute vec4 a_position; \n \
attribute vec4 a_color; \n \
varying vec4 v_fragmentColor; \n \
void main() \n \
{ \n \
gl_Position = CC_MVPMatrix * a_position; \n \
v_fragmentColor = a_color; \n \
}";
GLchar* myFragmentShader =
"varying vec4 v_fragmentColor; \n \
void main() \n \
{ \n \
float gray = dot(v_fragmentColor.rgb, vec3(0.299, 0.587, 0.114)); \n \
gl_FragColor = vec4(gray, gray, gray, v_fragmentColor.a); \n \
}";
auto program = new GLProgram;
program->initWithVertexShaderByteArray(myVertextShader, myFragmentShader);
program->link();
program->updateUniforms();
this->setShaderProgram(program);
program->release();
return true;
}
void HelloWorld::visit(Renderer* renderer, const Mat4& parentTransform, uint32_t parentFlags)
{
Layer::visit(renderer, parentTransform, parentFlags);
_command.init(_globalZOrder);
_command.func = CC_CALLBACK_0(HelloWorld::onDraw, this);
Director::getInstance()->getRenderer()->addCommand(&_command);
}
void HelloWorld::onDraw()
{
// //获得当前HelloWorld的shader
// auto glProgram = getGLProgram();
// //使用此shader
// glProgram->use();
// //设置该shader的一些内置uniform,主要是MVP,即model-view-project矩阵
// glProgram->setUniformsForBuiltins();
// auto size = Director::getInstance()->getWinSize();
// //指定将要绘制的三角形的三个顶点,分别位到屏幕左下角,右下角和正中间的顶端
// float vertercies[] = { 0,0, //第一个点的坐标
// size.width, 0, //第二个点的坐标
// size.width / 2, size.height}; //第三个点的坐标
// //指定每一个顶点的颜色,颜色值是RGBA格式的,取值范围是0-1
// float color[] = { 0, 1,0, 1, //第一个点的颜色,绿色
// 1,0,0, 1, //第二个点的颜色, 红色
// 0, 0, 1, 1}; //第三个点的颜色, 蓝色
// //激活名字为position和color的vertex attribute
// GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR);
// //分别给position和color指定数据源
// glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertercies);
// glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, color);
// //绘制三角形,所谓的draw call就是指这个函数调用
// glDrawArrays(GL_TRIANGLES, 0, 3);
// //通知cocos2d-x 的renderer,让它在合适的时候调用这些OpenGL命令
// CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 3);
// //如果出错了,可以使用这个函数来获取出错信息
// CHECK_GL_ERROR_DEBUG();
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
auto glProgram = getGLProgram();
glProgram->use();
glProgram->setUniformsForBuiltins();
auto size = Director::getInstance()->getWinSize();
float vertercies[] = { 0,0,
size.width, 0,
size.width / 2, size.height};
float color[] = { 0, 1,0, 1,
1,0,0, 1,
0, 0, 1, 1};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertercies), vertercies, GL_STATIC_DRAW);
GLuint positionLocation = glGetAttribLocation(glProgram->getProgram(), "a_position");
glEnableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
GLuint colorVBO;
glGenBuffers(1, &colorVBO);
glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);
GLuint colorLocation = glGetAttribLocation(glProgram->getProgram(), "a_color");
glEnableVertexAttribArray(colorLocation);
glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 3);
CHECK_GL_ERROR_DEBUG();
}