HTML5 Canvas 提高班(一) —— 光栅图形学(1)中点画圆算法
本系列的随笔在于给大家提供一些难度较深的canvas应用场景,借用数学或物理模型实现效果或性能媲美桌面应用的案例;并且此系将尽可能使用最简明的js代码展示效果。
推荐使用:chrome、ie9浏览器进行阅读,同时我还在做一个基于canvas的矢量渲染器的类库,希望大家关注。
话不多说,我们现在开始第一次随笔的内容。
光栅图形学(1)中点画圆算法
我们平时在使用canvas绘制图形时,通常会调用context的各种API,如设置样式的strokeline、fillcolor等;再如绘制图形的context.arc,context.fillRect等。
如果我们现在有一个场景,需要绘制1W个以上的图形,并且要求其刷新频率达到12fps以上,也就是说我们必须要在1秒内完成10W次canvasAPI的调用,想想这有多么可怕,大家可以在机子的机器上尝试一下。。得卡到强制关闭浏览器。
下面我们引入了光栅图形学中的中点画圆算法。
1.获取context元素的像素数组:
var cxt= canvas.getContext("2d"); cxt.clearRect(0, 0, width, height); var data = cxt.getImageData(0, 0,width, height); imageData = data.data;
现在imageData变量便引用了当前canvas元素的所有像素数组。
imageData的数据结构是一维数组,每四个元素表示一个像素的所有属性,依次表示:r、g、b、alpha 的值,其范围均是是(0——255)。
有了这样的理论逻辑我们可以通过canvas上的任意一点(x,y)计算出imageData中表示次像素的第一个元素的索引值。
计算方式如下代码:
function getStartIndex(x, y) { return y * width * 4 + x * 4; }
有了上面的理论知识,大家应该知道我们下面要做什么了吧?对就是利用中点画圆算法对每一个像素进行颜色值(rgb)的修改。
2.光栅学——中点画圆
首先我们通过圆的对称性将其分为8个部分。
现在我们假设,这个圆的中点位于(0,0)的位置。
设d是点p(x,y)到圆心的距离:则我们得到这样的圆方程FX(X,Y) = d 。
这里我们按照Bresenham算法,推出:
(此部分可能需要有图形学学习经验的同学,再以后的随笔中可能会介绍Bresenham算法)。
如果dM<0,表示下一点M在圆内,得
如果dM>0,表示下一点M在圆外,得
。
有了上面的理论知识,我们就可以轻松的写出绘制圆的算法了:
// 中点画圆法 function circle(x, y, r, color) { var tx = 0, ty = r, d = 1 - r; while(tx <= ty){ // 利用圆的八分对称性画点 putpixel(x + tx, y + ty, color); putpixel(x + tx, y - ty, color); putpixel(x - tx, y + ty, color); putpixel(x - tx, y - ty, color); putpixel(x + ty, y + tx, color); putpixel(x + ty, y - tx, color); putpixel(x - ty, y + tx, color); putpixel(x - ty, y - tx, color); if(d < 0){ d += 2 * tx + 3; }else{ d += 2 * (tx - ty) + 5; ty--; } tx++; } }
putpixel函数用于将每一个像素的颜色值填入对应的数组当中,这里我们的颜色只表示灰度值,因为还没有对彩色做任何处理。
function putpixel(x, y, color){ var index = getStartIndex(x, y); for(var i = 0; i< 4; i++) { if(i == 3) { imageData[index + i] = 255; } else{ // var co = parseInt(Math.random() * 255) imageData[index + i] = color; } } }
至此我们就已经把一个圆的像素填入我们的像素数组当中,最后将像素对象放回原来的canvas当中,就实现了圆的光栅画法:
cxt.putImageData(data, 0, 0);
3.案例
下面这个案例以1w个圆的边界碰撞检测运动为例,说明了我们实现的光栅图形性能完全可以胜任各种bt场景。(首要推荐chrome18其他浏览器可能解析js代码比较慢)
下次提高班预告:实现光栅算法中的画线算法。