2、cocos2d-x学习笔记——渲染概览

96
鱼鸟fly
2016.05.05 13:47* 字数 1713

渲染是一个游戏的重要组成部分,给与玩家最直接的体验。渲染做的好,游戏不仅要华丽,还要运行流畅。接下来,我们来看看cocos2d-x的渲染方式。这里只解析渲染流程,不解析优化。

大家都知道,cocos2d-x底层渲染是用的OpenGL ES 2.0,所以在了解Node的渲染之前要先了解一下OpenGL ES的渲染流程。

在游戏中的点,要先将点的坐标转化为世界坐标,再将世界坐标转化为眼睛坐标(观察体上面的坐标),然后将眼睛坐标转化到规范化观察体上面的坐标,最后将规范化观察体上面坐标转化为裁剪坐标。

首先要介绍一下观察体,也就是需要显示的物体的范围所形成的一个立方体。主要有两种观察体,一种是正投影观察体,还有一种是棱台观察体。正投影观察体是一个正方体,里面的点都是平行投影到裁剪坐标系。棱台观察体,模仿人眼观察物体,只是截取后面的棱台。棱台观察体可以经过矩阵变换为正投影观察体。观察体是有边界的。

OpenGL ES的常用规范化观察体坐标系的x、y、z的取值范围为-1到1,通过矩阵变换将观察体上面的点变换到规范化观察体上面,最后投影到裁剪坐标上面。

顶点着色器的输出坐标时标准化标准化观察体上面的坐标。上面的变换只是顶点着色器的简单应用,它还可以用于执行其他的自定义计算(如照明、顶点蒙皮等等)。接下来,对顶点做色器输出的顶点进行图元装配,将定点着色器的输出顶点按设定的图元类型进行绘制。图元类型有点、直线和三角形。然后对超出部分进行裁剪、透视分割,最后进行视口变换。

接着进行光栅化,取得所有图元,并为所有图元生成对应的片段即点。然后将所有点提交给片段着色器,经过片段着色器的处理后输出所有片段的颜色值,最后就是片段操作,有裁剪区域测试,模板深度测试,深度缓冲区测试,多重采样,混合,抖动。

以上简单的介绍了一下OpenGL ES 的渲染流程(如果你想了解的更清楚可以购买《OpenGL ES 2.0 编程指南》这本书),cocos2d-x是在Open GL ES 的基础上进行包装的,肯定也少不了上述流程。那么,现在,我们一起来看看它是如何实现渲染的。

在渲染开始之前,要做好渲染准备,首先创建GLView,也就是用于渲染的窗口,除了苹果要在CAEAGLLayer的基础上面创建外,其他的可以通过EGL创建窗口。创建完之后,将GLView交给Director管理,来看看setOpenGLView函数这个函数做了什么。它输出了GPU的支持信息;在setGLDefaultValues函数里面开启像素颜色混合(像素颜色混合:有使透明像素有透明效果等效果,关闭后透明像素没有透明效果),关闭了深度测试(深度测试:使前面的物体对后面的图元有遮挡效果。关闭后,后绘制的图元先显示),接着在setProjection函数里面设置了viewprot(视口的位置,大小)的值,还设置了眼睛到规范化观察体的转换矩阵,并将其压入MATRIX_STACK_PROJECTION栈中,设置了点坐标到眼睛坐标的转化矩阵,并将其压入MATRIX_STACK_MODELVIEW栈中;接着在initGLView函数里面设置了VBO(vertex buffer object,顶点缓冲区对象,在图形内存中缓存定点数据,减少CPU和GPU之间复制的数据量,获得更好的性能)VAO(vectex array object顶点数组对象,仅在mac或者iphone上调用,保存顶点缓冲区所有绑定和启用的客户顶点状态);最后初始化了FBO(frame buffer object,帧缓冲区对象,缓存一组颜色、深度、模板、纹理和渲染目标,用于性能优化)。

准备做好之后,便开始渲染。渲染放在主循环里面,以便于每一帧都对对象进行渲染。我们来看看Director的mainLoop函数,与渲染有关的是drawScene函数,我们只看与渲染有关的代码。首先,renderer调用了clear函数清理了上一帧的颜色缓冲区和深度缓冲区,然后调用clearAllFBOs函数清理了上一帧所有的帧缓冲区对象的缓冲数据,接着调用clearDrawStats函数将批渲染和顶点个数清0,然后当前场景调用render函数开始渲染当前帧,也就是当前场景的渲染,最后将渲染的图像推送到屏幕上面。

我来看看Scene的render函数,这个函数绘制整个游戏场景。在scene的构造函数里面,创建了一个默认的camera,并把camera加入了scene中,而在camera的OnEnter函数里面,将相机加入了scene的_cameras成员函数中。这样,在遍历scene中相机的时候,就有一个默认的相机。接下来看看遍历相机时的操作,首先,相机调用apply函数启用FBO和viewport,接着清理了相机的背景深度,然后调用了visit遍历了每个节点,并根据传过来的父节点转化矩阵(这个矩阵存储物体坐标系到世界坐标系的变换矩阵、缩放矩阵和翻转矩阵)将每个节点转换矩阵更新,还调用draw函数将需要渲染的图元(RenderCommand类)加入渲染队列,接着调用renderer的render函数渲染图元,将需要批量渲染的图元批量渲染,这样可以减少顶点提交次数,达到渲染优化的效果,并在渲染完成后将渲染图元删除。

随笔