第10章 高级绘图(II)—— Mesh, Canvas, SpriteBatch
本章将继续介绍一些love的高级绘图功能。Mesh绘图,Canvas绘图缓存,以及SpriteBatch绘制组,以及一些数据知识。
高级绘图(II)
Mesh 网格
Mesh实际上是显卡绘制图片的基础,所有的图片都是通过用mesh组成的多边形,从图片的各个像素映射到渲染对象上的。Mesh由存储一个一个顶点位置,顶点对应的材质的位置,以及渲染颜色组成。Mesh中的顶点组成的多边形的基础单位是最简单的面,即三角形,任何复杂的图形都可以用三角形来拼接起来。而Mesh的绘制模式,则指定了这些顶点如何拼接三角形。
Mesh的主要作用是绘制任意形状,任意变形的材质,以及颜色的渐变等。
这里仅介绍标准型(非用户自定义属性mesh)Mesh的相关用法,而自定义mesh必须配合shader使用,这里暂不讲解。
mesh的创建
|
|
vertices 顶点
这个参数是一个表,包含以下列格式组成的顶点组。
其中x,y指的是mesh系统中顶点的位置(绝对位置)。u,v指的是这个顶点对应的材质的位置(它们是相对位置,取值0,1之间),r,g,b,a指的是该顶点对应的绘制颜色。
mode 构型模式
这个参数接受一个字符串,来指定顶点通过什么形式来构型的,默认为“fan”
fan 扇形,以第一个点为中心,然后其他点分别与中心点相连来构成三角形。一般用作画一些中心放射的图形。
strip 三角形链,由诸如1,2,3;3,2,4;3,4,5这种每连续3个三个顶点组成。一般用作画带状的图形。
triangles 三角形表,每三个顶点组成一个独立表,没有公用关系。它可以画任意图形,只不过如果用在前两种的话,效率稍低。因为顶点更多,有重复的。
points 孤点表,这里暂不学习。
你也可以单独的指定 setVertexMap来单独指定某个顶点的加入顺序。
usage 使用方式
这个参数也接受一个字符串,来确定顶点的读取行为,默认为“动态”
dynamic 普通的动态加载
static 顶点位置不再改变
stream 流式加载,顶点位置每帧都要变化。
解释完参数,让我们举一个形象的例子来说明mesh是如何工作的。
- 首先准备一个素材图片或者纯白的图片。
- 我们选取这个素材图片上的三个点来构成一个三角形。
- 准备一个可以伸缩的薄膜,裁剪成三角形同样的形状和大小,并把这个三角形内的素材复制下来。然后按照需求在三个顶点进行染色。色彩将按其距离远近进行扩散。
- 现在,我们这个薄膜就是一个只有一个三角形的mesh。然后我们按照我们的需求,通过拉伸这三个顶点来控制这个三角形的形状。
- 然后我们把这个薄膜作为一个图层放到我们的屏幕上,这就是mesh的绘制的。
- 如果是一组顶点,那么可以想象 把一大块薄膜蒙在图片上,然后各个顶点用图钉固定住,裁剪掉没有用的部分,我们就得到了一个带图钉的图片,图片复制到薄膜上。然后我们通过移动图钉的位置,带动薄膜改变形状,从而形成mesh新的状态。
设置材质
|
|
如果你不设置材质,就相当于用纯白填充一个矩形。
动态Mesh的更新
|
|
上面前两种方法是针对某一个顶点的,必须指定顶点的序号。第三种可以直接整体修改。
Mesh的绘制
mesh是一个drawable对象,所以可以直接像绘制。跟普通的图片是一样的,可以拉伸,旋转等。
Canvas 绘图缓存
canvas有很多中叫法,比如内存dc,画布,帧缓存,虚拟绘制对象等等。它就是一块内存区域,在设置后,把drawcall的结果并非发送到屏幕上,而是发送到这块内存里。
canvas的作用当然就是存储了,单单存储还不够,它的核心在于“累积”。
因为之前我们讲到love绘图就跟ps一样,但是唯一的区别在于,ps是静态的积累的,而love是动态的,每一帧都要用背景颜色把画面重新填充一次。而canvas就给了我们一个积累的平台。除非手动清理,不然canvas的结果将一直被保留。另外,它作为虚拟绘制对象,是不受love.draw回调限制的,也就是对canvas的绘制可以写在任意位置。
比如有一个图片,它是动态拼接而成的,如果我们不希望重复的进行拼接工作,而直接使用拼接结果,那么我们就可以先把它绘制在canvas上,然后每次仅仅绘制这个canvas就可以了。
canvas的创建很简单,只需要提供一个宽和高就行了,默认是屏幕的大小。
canvas也是一个开关设置,与其他一样,开启时加参数,关闭是无参数调用。注意,仅仅在canvas上绘制不会体现到屏幕上。
还有一种回调方式来绘制canvas,如下:
一些摄像机库也用同样的方式来绑定绘制函数。
SpriteBatch 绘制组
SpriteBatch成为精灵组、绘制组,是一种提高渲染效率的方法。它通过合并很多重复的对于同一个对象的绘制来达到减少drawcall的目的。注意,它的合并是针对某一个图片而言的,也就是不同的图片是不能合并在一个图片组里。但是由于一些素材采用的是精灵清单的形式,这种合并就十分强大而有效率,尤其对于大量的重复出现的图片。不过相比而言它的使用比较复杂,有一些库能够帮到你。试试autobatch。
创建
它要求指定一个素材,和最大组数。默认为1000. 用法与上面mesh是一样的,可以选择动态,静态和流式三种。
每当要添加绘制的时候,代码如下
参数就是每个精灵单独绘制的参数了,实际上它是一个Matrix矩阵。返回值是id,用来单独控制某一个精灵组中的精灵。
当然,每次绘制玩了,你需要清空这个组,不然是要积累下去的。
精灵组的绘制同任何drawable一样。一般而言,精灵组直接绘制到0,0就行了。
数据对象
相信每一个用love的同学最开始对love.image.newImageData()这类函数比较疑惑。因为他们只是数据对象而非绘图对象。他们用来做什么呢?
在回答这个问题之前,我们首先要看看love中类似的“数据对象”,以data为基类的对象有哪些:
CompressedData 压缩数据
CompressedImageData 压缩图像数据
FileData 文件数据
GlyphData 字体数据
ImageData 图片数据
SoundData 声音数据
他们分别在love.filesystem,love.font,love.sound,love.image下。对于一般新用户来讲,很容易把他们误认为是可以直接拿来放到游戏中使用了,然而并不是这样。他们可以说是文件缓存,或者是文件与游戏可用对象之间的桥梁。一般而言,任何直接创建的可用游戏资源比如声音,图片,字体等,都是先从文件读入到数据,再从数据转到可用资源这个过程,只是中间过程省略了,没有展现到用户(类似的比如spine在读取后,会生成data对象)。
那么他们的作用是什么呢? 首先可以进行预读,比如重复播放的声音(一个场景下某个声音同时播放),需要建立多个播放对象,如果每次都从文件读取,那么效率就降低了,而从缓存读取,速度就快很多。另外,我们可以修改数据,比如imageData对象,我们可以像改任何文件一样,修改它某个像素的颜色,或者进行遍历。比如原来的图像的人物衣服是带红色配饰的,我们可以通过把所有像素红色像素改为蓝色,从而生成蓝色的人物。另外,还可以截取音效等等。还有一个功能是将数据以文件导出。最后这个功能赋予你可以用love做图形编辑软件,声音编辑软件等等(当然,专业软件不会用这种的,不过自己使用足够了。)
同时,一般而言,数据都可以作为参数来创建游戏使用对象。也就是他们是可以相互转化的。
编程时间
设计阶段
- 使用mesh画一个色带,送红,绿,蓝的渐变。
- 使用mesh画圆,并绑定在love的logo图片上。
- 使用canvas做一个绘图板,鼠标拖动时,在图片上画出轨迹。
- 使用imageData的mapPixel来制作一个圆形图案,离圆心距离越近,颜色越黑。
分析: 第一个目标很容易,是无素材绑定的mesh,只需要6个顶点(因为有3段渐变,色带上下各一个顶点),用strip模式来组成三角形。然后把颜色值放进去就行了,uv用0,0即可(因为没有贴图)。第二个目标,跟第一个一样,只是要学会如何使用uv绑定贴图。因为没有颜色渐变,所以使用白色255,255,255,uv因为他们是比例值,所以要计算每个点在图片的位置比例,加入即可。这里logo的半径是30,素材大小是64。使用“fan”模式加入顶点。第三个目标是canvas的基本用法,只要每帧向画布上画画笔的位置即可。但是,千万别忘了,你用鼠标的轨迹,实际上是非连续的,还记得之前说的么,所以简单的画点是不行的,需要把上一帧的位置和当前帧的位置连线才行。第四个目标稍微复杂一些,因为可能大家还不太习惯这种逐像素遍历的做法和数学思想。这里关键的是用距离函数。
代码阶段
渐变色带
这里唯一注意的是,由于mesh和任何绘图模式一样可以任意拉伸,而且是无损的,所以一般我们把mesh都写成单位长度1,方便我们设置宽和高。
绑定贴图mesh
这里要说的有两点,一个是圆的本质,我们一般画圆的时候,第四个参数那个取值一般为默认,而现在我们知道了,是那个30。画圆实际不是真实的圆,而是像上面方法一样生成多边形。实际上,真实的画圆在每一次circle函数时都要进行上面的计算,因此用mesh画圆的效率要比直接circle要高(当然更占内存,功是一样的,区别在于力和力距^^)。另一个需要注意的,是”fan”的第一个顶点是圆心,千万别忘了。而且最后的一个顶点一定要回归到第二个顶点,这样才是完整的图形,而不是一个带缺口的圆。
绘图画板
在建立画板时,取全屏的默认值即可。而Update中,我们跟按钮的操作是一样的,在按下是,要把ox,oy的值设定了,否者将绘制出最后一次鼠标释放的位置的连线,这不是我们想要的。 另外,canvas绘制就很简单了,只有在有笔触时绘制,因为canvas是积累的,所以不用担心没有绘制就不被保存了。
图片数据与mapPixel
dist方法很简单啦,就是两点间距离公式。我们来分析下map方法,如果距离大于100,就不绘制,否则灰度值从距离100,100为0的位置起从255 - 55之间渐变。我们知道,圆就是距离到圆心小于半径的点的集合。所以,上面的方法,实际上是绘制了一个圆。这个集合的思想一定要能够贯彻理解。因为后面我们的shader的主要的思想就是它。
最后imagedata并不能用于绘制,必须转化为image才可以。
作业
- 做一个颜色渐变的圆。自己查询HSV与RGB公式,色调沿着圆周分布。
- 上面使用logo绑定的mesh,这里要求可以通过拖动来修改顶点来达到变形的目的。提示:做一个handle类,与mesh的顶点进行绑定。当handle移动时,更改对应id的mesh顶点的位置(xy改变,uv不变)。
- 增加一些新的功能到我们的绘图板上,模仿一下win自带的。利用button来改变笔触。想使用油漆桶?需要知道多边形的边缘,可以利用递归来计算边缘,相关算法请自行百度。
- 用mapPixel画一个矩形。
本章代码
|
|