《Cocos2d-x高级开发教程》学习笔记 OpenGL部分

来源:互联网 发布:vue数据交互 编辑:程序博客网 时间:2024/05/16 01:54

10.1 OpenGL简介

一、状态机

OpenGL是一个基于状态的绘图模型,我们把这种模型称为状态机。

为了正确地绘制图形,我们需要把OpenGL设置到合适的状态,然后调用绘图指令

OpenGL把所有的参数作为状态来保存,如果没有设置新的参数,则会一直采用当前的状态来绘图

我们可以把绘图设备人为地分为两个部分:"服务器端",负责具体的绘制渲染;"客户端",负责向服务器端发送绘图指令

GL_APICALL voidGL_APIENTRY glEnable (GLenum cap); //开启一个状态

GL_APICALL voidGL_APIENTRY glDisable (GLenum cap); //禁止一个状态

二、坐标系

OpenGL使用右手三维坐标系,初始化时,屏幕向右的方向为X方向,屏幕向上的方向为Y方向,由屏幕指向我们的方向为Z方向。

在不对OpenGL做任何设置的时候,初始的坐标系称作世界坐标系。

世界坐标系以屏幕中心为原点(0,0,0)

绘图坐标系,初始化时与世界坐标系重叠,当用glTranslatef(),glScalef(), glRotatef()对当前绘图坐标系进行平移、伸缩、旋转变换之后, 世界坐标系和当前绘图坐标系不再重合。改变以后,再用glVertex3f()等绘图函数绘图时,都是在当前绘图坐标系进行绘图,所有的函数参数也都是相 对当前绘图坐标系来讲的。

三、渲染流水线

过程:显示列表、求值器、顶点装配、像素操作、纹理装配、光栅化和片断操作等;

OpenGL ES 1.0版本采用固定渲染管线;OpenGL从2.0版本开始引入了可编程着色器(shader)。可编程着色器作为原有渲染管线中一些部分的代替品,不仅可以实现原有的渲染功能,还可以自由实现开发者自定义的渲染效果。可编程着色器主要包含顶点着色器和片段着色器,其中前者负责对顶点进行几何变换以及光照计算,后者负责处理光栅化得到的像素以及纹理。

10.1.2 绘图 (三角形带(triangle stripe )的概念,参考计算机图形学)

1.声明顶点坐标、纹理坐标、颜色三个静态数组,用来构建数据。

2.初始化纹理,CCTextureCache类的addImage方法载入纹理,然后用纹理的属性初始化四个顶点的纹理坐标,再用CCTexture2D保存下来。

3.绘制图片:绑定纹理、设置顶点数组和绘图。

具体参考Fishjoy的例子,例子中绘制的图片会被背景掩盖,解决方法是拉大窗口(程序设置)

10.1.3 矩阵与变换

当我们设置好OpenGL的坐标系,并传入顶点数据后,OpenGL就会通过一系列计算把顶点映射到世界坐标系之中,再把世界坐标系中的点投影到平面上。OpenGL维护了一个当前绘图矩阵,用于表示当前的绘图坐标系。这个矩阵被初始化为单位矩阵,此时绘图坐标系与世界坐标系相同,当我们不断地在绘图矩阵后乘上新的矩阵时,会相应地改变绘图坐标系

cocos2d-x 2.0以后的版本 已经采用OpenGL ES 2.0,放弃了固定的渲染流水线,取而代之的是自定义的各种着色器,引入第三方库Kamzmatah来进行矩阵计算,具体函数参考书中。

 

 

10.2 Cocos2d-x 绘图原理

10.2.1 精灵的绘制

绘制精灵的代码位于引擎源码的"sprite_nodes\CCSprite.cpp"中,分为5个部分:1. 设置OpenGL状态;2. 设置颜色混合模式;3.绑定纹理;4.绘图;5.调试相关处理。

10.2.2渲染树的绘制

Cocos2d-x游戏的层次:导演类CCDirector直接控制渲染树的根节点--场景(CCScene),场景包含多个层(CCLayer),层中包含多个精灵(CCSprite)。每一个上述的游戏元素都在渲染树中表示为节点(CCNode),游戏元素的归属关系就转换为了节点间的归属关系,进而形成树结构。visit()方法绘制一棵渲染树,每个节点需要绘制子节点。

引擎会根据不同的平台设法使系统不断地调用这个方法,从而完成游戏主循环。(mainloop)

visit方法分为4部分:1.进行先行的处理,矩阵圧栈;2.通过transform方法进行一些变换,draw方法不关心纹理绘制的位置,绘制到当前坐标系的原点,所以绘制前需要进行调整。3.递归绘制节点以其子节点;4.恢复,矩阵退栈。

10.2.3 坐标变换

transform,一堆坐标变换的方法。Cocos2d-x使用的一个开源几何计算库Kazmath

10.3TexturePacker与优化

10.3.1绘图瓶颈

因素:1.纹理过小;2.纹理切换次数过多;3.纹理过大;

10.3.2 碎图压缩与精灵框帧

运用TexturePacker将碎图合成一张纹理,并生成一个.plist文件,通过CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("all.plist");加载,即存放在精灵框帧的缓存内。

cannon =CCSprite::createWithSpriteFrameName(cannonPath); //从精灵框帧完成初始化

TexturePacker下载地址:http://www.codeandweb.com/texturepacker/download

10.3.3批量渲染

CCSpriteBatchNode可以一次批量提交所有子节点的绘图请求

10.3.4 色彩深度优化

通过修改颜色深度,降低游戏质量,以达到舒畅运行或者包大小的要求。

11 OpenGL绘图技巧

11.1 自定义绘图

由"CCDrawingPrimitives.h"和对应的cpp文件提供,包括了点、线、多边形、圆形和贝塞尔曲线等最基本的几何图形的绘制,还包括了一些基本的设置,如设置点的大小、绘制的颜色等。

形如ccDrawLine的绘图函数,每次都需要调用OpenGL渲染,效率低,只适用于少量绘制的情况。

11.2 遮罩层

又称为剪刀效果:开启遮罩效果后,一切的绘制提交都是正常渲染的,但最终只有屏幕上的指定区域会被绘制。

 glEnable(GL_SCISSOR_TEST); //启动遮罩效果

glScissor(rect.origin.x,rect.origin.y, rect.size.width, rect.size.height); //设置遮罩效果

glDisable(GL_SCISSOR_TEST);//关闭遮罩效果

例子:表盘的效果,让0到9的数字顺序摆放,连续移动,只显示一个区域,数字依次通过;

11.3 数据交流

CCImage类,加载到内存的纹理图片,纹理以每个像素的颜色值保存在内存之中。

主要提供两个方面的功能:一方面是文件的加载与保存,另一方面是内存缓冲区的读写。

CCTexture2D类:该类所包含的纹理大小必须是2的幂次,只存在于显存中的纹理;

 

文件读写相关的方法:

boolinitWithImageFile(const char* strPath, EImageFormat imageType = kFmtPng);

boolinitWithImageFileThreadSafe(const char* fullpath, EImageFormat imageType =kFmtPng);

bool saveToFile(constchar* pszFilePath, bool bIsToRGB = true);

getData和getDataLen这两个方法提供了获取当前纹理的缓冲区的功能,而initWithImageData方法提供了使用像素数据初始化图片的功能

 

例子:实现截图功能的方法 glReadPixelsinitWithImageData

使用glReadPixels方法将当前绘图区的像素都读取到了一个内存缓冲区内,然后用这个缓冲区来初始化CCImage并返回

11.4 可编程管线

通过着色器定义每一个顶点或者像素的着色方式,产生更丰富的效果。

1.顶点着色器(vertex shader)

2.段色着色器(fragment shader)

这两个着色器不能单独使用,必须成对出现,这是因为顶点着色器会首先确定每一个显示到屏幕上的顶点的属性,然后这些顶点组成的区域被化分成一系列像素,这些像素的每一个都会调用一次段着色器,最后这些经过处理的像素显示在屏幕上,二者是协同工作的。

 

11.4.2 CCGLPragram

获取着色器程序接口 : const GLuintgetProgram(); 返回当前着色器程序的表示符。

导入着色器程序函数: boolinitWithVertexShaderByteArray(const GLchar* vShaderByteArray,const GLchar*fShaderByteArray);

                                  bool initWithVertexShaderFilename(const char*vShaderFilename, const char* fShaderFilename);

第一个参数均指定了顶点着色器,后一个参数则指定了像素着色器;

 

11.4.3 变量传递

着色器存在两种输入数据,分别被标识为arrtibute和uniform

1.attribute变量是程序直接传递给顶点着色器的,段着色器不能访问,被限制为向量或者标量等简单数据结构;

2.uniform变量是全局性的,支持复杂的数据结构,可以通过uniformblock自定义复杂的数据结构;

以上两种变量的传递都要经过获取位置和设置两步

intglGetUniformLocation(GLuint program, const GLchar* name);//参数是当前绘图程序及需要获取的uniform变量名

对uniform变量的设置存在着一系列以"glUniform"为前缀的函数:例如:voidglUniform1i(GLint location, GLint x);

OpenGL的函数末尾总是紧接着类似“1i”和 “3f”这样的后缀

考虑到内存和显卡间数据交换的开销,引擎在CCGLProgram中进一步封装了一层缓冲机制,记录下每次传递的值,只有在传值不一的时候才真正设置到显卡数据中:

voidCCGLProgram::setUniformLocationWith1i(unsigned int location, GLint i1);

绑定一个attribute变量名到特定的标识符上,这样我们就可以直接通过名称来访问变量了:

voidaddAttribute(const char* attributeName, GLuint index);

根据同一个变量在不同调用间是否一致,可以分为一次性传值与数组传值。这两种传值方式是互斥的,要通过一组函数来切换:

voidglEnableVertexAttribArray(GLuint index);

voidglDisableVertexAttribArray(GLuint index);

没有开启数组传值的attribute变量使用"glVertexAttrib"系列函数传值

开启数组传值的attribute变量则通过以下的接口函数传值:

voidglVertexAttribPointer(GLuint indx, //变量标识符

GLint size, //变量的维数

GLenum type, //组成变量的基本类型

GLboolean normalized,//是否归一化,一般为false

GLsizei stride, //每次取值间隔

const GLvoid* ptr);//数组指针

 

11.5 水纹效果

11.5.2 ShaderNode类

11.5.3uniform变量准备

11.5.4 绘制

11.5.5 添加到场景

 

11.6 CCGrid3D

CCActionGrid3D特殊的动作类,也提供了一个实现水纹效果的波浪效果动作

 

0 0