WebGL绘制变幻光斑

首先提示一下:如果你无法在本地搭建一个web服务环境,请暂时不要测试本例。由于WebGL安全方面的限制,本例中用到一张帖图仅能通过相对路径在本域调用,file协议是无法看到下图效果的,仅能看到一片移动的方形。

效果图:(图1)

image

 

类似效果使用Canvas2d绘图也可以实现。下面这个地址就是一个2D实现的例子。(图2)

http://muse-js-lib.googlecode.com/files/DrawCircleLight.html

image

 

但是经过实际运行,发现2D绘图相当耗费资源。上面这个图仅仅绘制了15个圆形,大量的运算耗费在beginPath/closePath等等这些命令中。而图1在运行时,绘制100个对象,cpu占用仅在20%上下浮动。我的机器配置如下:

 

image

显卡:NVIDIA 7600GS

 

2D的绘图就不说了。WebGL绘图之所以能够效率高,我想原因也不必多说。由于需要在某个应用中绘制一个变幻背景,为了不影响主体动画的表现,对动态背景绘制的效率要求自然比较苛刻。在对2D绘图感觉失望后,继而在WebGL中实现了这个效果,感觉比较理想。

在绘制中,引用了两个js库。一个是J3DIMath.js,该库是苹果公司于2009年撰写的用于计算顶点阵列的工具类;另一个是iWebGL_beta_4.0.js,是本人正在制作WebGL绘制的库。看过本人前面文章的童鞋请注意以下:iWebGL库已经完全重写,与前面几篇文章中所引用的库完全不兼容,当前最新版本是beta4.0。

相关的类有两个。首先是Light3D类:

function Light3D(webgl, texture, baseMat, w, h){
    this.tex = texture;
    this.baseMat = baseMat;
    this.webgl = webgl;
    this.w = w;
    this.h = h;
    
    this.init();
}
Light3D.prototype = {
    draw : function(element, texture){
        this.matrix.translate(this.sign * .01, 0, 0);
        element.matrix('u_mvpm', this.matrix);
        element.bindTexture(texture);    
        element.uniformFloat('u_color', 1, 1, 1
            , Math.sin(this.angle / 180 * Math.PI) * this.a* .3);
        element.draw();
        
        this.angle += this.step;
        if(this.angle >=180){
            this.angle = 0;            
            this.init();
        }
    },
    init : function(){
        this.step = 10 * Math.random();
        this.matrix = new J3DIMatrix4(this.baseMat);
        this.matrix.translate(this.randSign() * (Math.random() * this.w * .5)
            , this.randSign() * (Math.random() * this.h * .5)
            , 0);
        var scale = Math.random() * this.randSign() + .1;
        this.matrix.scale(1 + scale, 1 + scale, 1);
        this.sign = this.randSign();
        this.a = Math.random() * .7;
        this.xPos = Math.random();
        this.matrix.translate(this.xPos, 0, 0);
        this.angle = 0;
    },
    randSign : function(){
        return parseInt(Math.random() * 10) % 2 == 0 ? 1 : -1;
    }
}

该类是绘制一个对象,并在一个周期内作横向和透明度的变化。init方法负责在一个限制区域(w,y)内随机生成一个位置和最终透明度值,通过运算angel属性的sin值来设置过程内的alpha变化值。同时angel属性也是生命周期的控制参数。运行时的实际生命周期长度由angle的增量step属性来控制。step也是随机的。angle属性在超过180时所有属性重置,并在新的位置开始新的绘制。

 

另一个类是Lights3D。这是一个创建一组Light3D对象的类,并在draw方法中对其进行循环绘制。只是为了方便,很简单:

function Lights3D(count, webgl, texture, baseMat, w, h){
    count = Math.max(1, count || 1);
    this.lights = [];
    for(var i = 0; i < count; i++){
        this.lights.push(new Light3D(webgl, texture, baseMat, w, h));
    }
}
Lights3D.prototype = {
    draw : function(element, texture){
        for(var i = 0; i < this.lights.length; i++){
            this.lights[i].draw(element, texture);
        }
    }
}

 

最后封装了一个闭包,将所有相关对象——包括shader代码——都封到一起,确保调用方便和移植方便。

/*
* showLights
* @canvas canvas元素id或者canvas对象本身
* @count 光斑个数
* @textureUrl 贴图地址
* @r, @b, @b, @a 色度RGBA
*/
function showLights(canvas, count, textureUrl, r, g, b, a){
    var webgl = new iWebGL(canvas);
    var vsCode = 'uniform mat4 u_mvpm;attribute vec4 a_texc;attribute vec4 a_pos;varying vec2 v_texc;'
        + 'void main(){ gl_Position=u_mvpm*a_pos;v_texc=a_texc.st; }';
    var fsCode = 'precision mediump float;uniform sampler2D samp2d;uniform vec4 u_color;varying vec2 v_texc;'
        + 'void main(){'
        + '    vec4 textureColor = texture2D(samp2d, vec2(v_texc.s,v_texc.t));'
        + '    gl_FragColor = textureColor * vec4(u_color);'
        + '}';
    
    var vs = $gl.createShader(webgl.gl, vsCode, 'VERTEX_SHADER');
    var fs = $gl.createShader(webgl.gl, fsCode, 'FRAGMENT_SHADER');
    vs.params = [];
    $gl.getParams(vsCode, vs.params);
    webgl.initShaders(vs, fs);
    
    r = r || 0;
    g = g || 0;
    b = b || 0;
    a = a || 1;
    count = Math.max(1, count || 5);
    
    webgl.clear([r, g, b, a], 10000);
    webgl.perspective(40, 1, 10000, 2);
    webgl.lookat(0, 0, 5, 0, 0, 0, 0, 1, 0);
    
    var baseMat = new J3DIMatrix4();
    baseMat.scale(.5, .5, 1);
    
    var texture = webgl.texture(textureUrl);
    
    var ls = new Lights3D(count, webgl, texture, baseMat, 20, 10);
    
    var sphere = $gl.createPlane(webgl);
    sphere.vertex('a_pos');
    sphere.texcoord('a_texc');
 
    setInterval(function(){
        webgl.fresh();
        ls.draw(sphere, texture);
    }, 50)
}

 

附帖图:

lightMask

额……上图的一刹那我突然想到,在图2的例子中,是不是在2D中使用drawImage之类的方法,能提高2d实现的效率呢?

调用:

showLights('glcanvas', 100, 'resources/light2.png', .5, .5, 1, 1);

下面是所有相关文件。请在下载后更改iWebGL-build-17.html 中两个js类库引用路径和帖图路径。

 

J3DIMath.js   35.6 KB

iWebGL_beta_4.0.js   23.1 KB

iWebGL-build-17.html   3.5 KB

lightMask.png   8.7 KB

DrawCircleLight.html   3.0 KB

posted @ 2011-12-13 15:01  MKing's Kindom  阅读(1711)  评论(2编辑  收藏  举报