Cocos2D-X shader(二) OpenGL渲染管线

Cocos2D-X shader(二) OpenGL渲染管线,第1张

概述OpenGL渲染管线    Cocos2D-X底层是基于OpenGL的,所以在变现我们自己的Shader之前,得了解一下渲染管线的知识。 如上图所示,OpenGL的渲染管线主要包括:     1、准备顶点数据(通过VBO--顶点缓冲区对象,VAO--顶点数组对象和Vertex attribute来传递数据给OpenGL)     2、顶点处理(这里主要由Vertex Shader来完成,从上图中可 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代码 请先阅读系列第一篇文章:http://blog.csdn.net/operhero1990/article/details/50215635 头文件代码并没有修改,只是对init()和onDraw()函数做出了修改,代码如下:
<span >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 >	</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,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,//第一个点的坐标                            size.wIDth,//第二个点的坐标                           size.wIDth / 2,size.height};  //第三个点的坐标    //指定每一个顶点的颜色,颜色值是RGBA格式的,取值范围是0-1    float color[] = { 0,1,//第一个点的颜色,绿色                        1,//第二个点的颜色,红色                         0,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,vertercIEs);    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_color,4,color);    //绘制三角形,所谓的draw call就是指这个函数调用    glDrawArrays(GL_TRIANGLES,3);    //通知cocos2d-x 的renderer,让它在合适的时候调用这些OpenGL命令    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,3);    //如果出错了,可以使用这个函数来获取出错信息    CHECK_GL_ERROR_DEBUG();}</span>

int()中******分隔线下就是修改的代码,运行后效果如图:

fragmentShader中
float gray = dot(v_fragmentcolor.rgb,v_fragmentcolor.a); \n \

的作用就是使图片颜色置灰。 使用VBO缓存数据 顶点数组(Vertex Array)被保存在客户端内存,当执行glDrawArrays或 glDrawElements时,才把它们从客户端内存copy到图形内存。这样占用了大量的内存带宽,Vertex Buffer Objects允许OpengGL ES2.0应用在高性能的图形内存中分配并cache顶点数据,然后从此图形内存中执行render,这样避免了每次画一个原语都要重发送数据。具体的实现过程参考子龙山人的文章。这里先给出完整代码(只修改了onDraw函数):
#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,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 instancebool 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,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,//第一个点的坐标 //                           size.wIDth,//第二个点的坐标 //                          size.wIDth / 2,size.height};  //第三个点的坐标 //   //指定每一个顶点的颜色,颜色值是RGBA格式的,取值范围是0-1 //   float color[] = { 0,//第一个点的颜色,绿色 //                       1,红色 //                        0,1};  //第三个点的颜色, 蓝色 //   //激活名字为position和color的vertex attribute //   GL::enabLevertexAttribs(GL::VERTEX_ATTRIB_FLAG_position | GL::VERTEX_ATTRIB_FLAG_color); //   //分别给position和color指定数据源 //   glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_position,vertercIEs); //   glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_color,color); //   //绘制三角形,所谓的draw call就是指这个函数调用 //   glDrawArrays(GL_TRIANGLES,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,size.wIDth,size.wIDth / 2,size.height};	float color[] = { 0,1};	glBufferData(GL_ARRAY_BUFFER,sizeof(vertercIEs),vertercIEs,GL_STATIC_DRAW);	gluint positionLocation = glGetAttribLocation(glProgram->getProgram(),"a_position");	glEnabLevertexAttribarray(positionLocation);	glVertexAttribPointer(positionLocation,(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,(GLvoID*)0);	glDrawArrays(GL_TRIANGLES,3);	glBindVertexArray(0);	CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,3);	CHECK_GL_ERROR_DEBUG();}
onDraw()函数中,注释部分是没有使用VBO的实现方法。 使用VBO时,通过glGenBuffers用于创建一个VBO,glBindBuffer用于绑定VBO与制定缓存类型,glBufferData用于写入缓存数据。 数据缓存后,还需要绑定到Shader上。通过glGetAttribLocation可以获取Shader中attribute的位置,通过glEnabLevertexAttribarray打开响应的功能,通过glVertexAttribPointer给相应的attribute变量绑定缓存数据。 最后,绘制结果和上一段代码一致。 当然,这段代码还可以继续优化,因为没一帧的绘制都会调用onDraw函数。可以重载draw()函数,帮缓存数据的 *** 作放入其中,就减少了重复的 *** 作。具体可以参考子龙山人那篇文章。 总结

以上是内存溢出为你收集整理的Cocos2D-X shader(二) OpenGL渲染管线全部内容,希望文章能够帮你解决Cocos2D-X shader(二) OpenGL渲染管线所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1075523.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存