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代码比较慢)

 


    下次提高班预告:实现光栅算法中的画线算法。

 

posted @ 2012-04-26 22:45  豆豆狗  阅读(7302)  评论(15编辑  收藏  举报