WebGL实战

前文已经大概讲解了一些基础知识,涵盖了WebGL创建,着色器,着色器编程以及缓存区等知识,一些简单的点面效果即可以基于此来完成,接下来让我们绘制一下如下的效果:

WebGL波浪效果

让我们先准备一下前期的WebGL内容,首先让我们来创建一个可以接受参数的着色器,(如果不清楚具体要怎么创建可以参考上一章教程内容哦,<a target="_blank" href="/yanglei/2018/04/14/yanglei11.html">点击跳转</a>)
接下来,我们需要创建多个点的坐标,所有点在同一行时,只有X坐标是变化的,y是相同的。

但因为WebGL的坐标系与实际页面中的坐标系是不同的,如下图,普通canvas坐标系与正常的浏览器像素值相同,但WebGL中的坐标系是以整个WebGL中心点为(0.0,0.0),而且坐标的精确度为小数点后一位。坐标系对比如下图所示:

WebGL坐标系与普通canvas坐标系

然后你知道了这个依旧会绘制出超级模糊的图像,那是因为整个WebGL的尺寸是与canvas宽度与高度相关连的,并且canvas的宽度与高度如果用css来设置的话,会被默认成100×100,也就意味着,你绘制出来的图形是把100×100的图形拉伸到当前canvas的尺寸中。所以正确的设置canvas的方式应该如下:

//错误
<canvas id="glcanvas" style="width: 700px; height: 500px;">
//正确的方式
<canvas id="glcanvas" width="700" height="500">

首先我们需要先将对应的真实坐标转换成WebGL坐标,因为canvas宽度的一半对应为WebGL的1.0尺寸。所以将真实像素除以宽度的一半就可以得到对应的WebGL尺寸,高度与宽度的处理方式一致。

function webglX (num) {
    return num/(width/2);
};
function webglY (num) {
    return num/(height/2);
}

有了点,那么我们就可以使用先前讲过的缓存区开始批量绘制点了。(如果不清楚具体如何使用缓存区绘制,也可以点击上一章哦,<a target="_blank" href="/yanglei/2018/04/14/yanglei11.html">点击跳转</a>)。

接下来我们要让他动起来,怎么动起来呢?思路是我们可以在不停的重绘,并且将点进行轻微位移即可达到效果,定时执行函数可以使用setIntervalsetTimeout。但此2个函数性能上有很大问题,并且也无法完美的匹配浏览器的刷新频率。

在这里我跟大家介绍一个新函数requestAnimationFrame

requestAnimationFrame是为了提高js动画性能而诞生的神器。下面我们简单来介绍一下这个函数为何被誉为神器。

  1. requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,也就是没s刷新60次。因与浏览器刷新频率一样所以不会出现看似卡顿的情况出现。
  2. 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量
  3. requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销

具体的内容以后有机会可以单独详细来介绍此函数功能。

接下来让我们来绘制一个变化的函数,变化值从0开始,requestAnimationFrame内容入参为循环执行的函数名。

var num = 0;
function render() {
    requestAnimationFrame(render);
    num = num - 1;
    var data = createPoints(num);
    setPoints(data, 1000);
}

接下来我们需要将之前绘制点的部分功能函数提取到单独的一个函数中,方便动画循环调用时使用。

function setPoints(data, num) {
    var vertexBuffer = gl.createBuffer();
    if(!vertexBuffer) {
        log('创建缓存区失败。');
        return -1;
    }
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    var a_position = gl.getAttribLocation(gl.program, 'a_p');
    gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_position);
    gl.clearColor(0.0,0.0,0.0,1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.POINTS, 0 , num);
}

接下来也就是最重要的,我们需要每次都变更对应点的坐标位置,因我们要使用波浪效果,所以采用正弦函数来实现,首先让所有的点都按照每次变更10度的浮动,并且因为是上下移动,我们只需要变更y轴的坐标即可。

var width = 700;
var height = 500;
function createPoints(gap) {
    var max = 10
    var arr = [];
    var n = 100;
    var m = 10;
    for(var i = 0; i < n; i++) {
        for(var j = 0; j < m; j++) {
            var x = webglX(-(width/2) + i*20);
            var y = webglY((height/2)  - j*20);
            var z = -1;
            var item = [x, y, z];
            arr = arr.concat(item);
        }
    }
    return new Float32Array(arr)
}

正弦函数Math.sin传入的值不是角度,而是弧度,我们需要通过下面的函数把角度转化为弧度,函数如下:(如果您不知道弧度与角度的关系,建议自行查询)

function numToDeg(num) {
    return Math.PI*num / 180;
};

我们为此函数中x与y的值进行一下操作。

1.为保证所有的点肯定全部覆盖屏幕,我们将每个点的间距调整按照整个canvas宽度超过500的尺寸来平均分,整个坐标系向做便宜200,也就是右边还存在300的富于空间,这样即使有错开位移也不会让屏幕露出空白。接下来,为了让上面的点波动比下面的点大,让我们来设置了一个最大倍数,并且将行数与此倍数关联,这样就会使动画上下浮动不一样了。

var deg = (j*20+gap);
var x = webglX(-(width/2)-200+i*((width+500)/n)+j*20);
var y = webglY(-(height/2)+Math.sin(numToDeg(deg))*(max+j*4) + j*20);

我们达到了横向


横向效果

接下来我们在角度便宜中添加上x轴的影响力。

var deg = (i-j*20+gap);
var x = webglX(-(width/2)-200+i*((width+500)/n)+j*20);
var y = webglY(-(height/2)+Math.sin(numToDeg(deg))*(max+j*4) + j*20);

让我们看看效果,波动开始有偏差了


侧横向效果

效果按照我们预期的方向前进了,接下来我们扩大x轴的影响,这个个人可以一点一点尝试,我们直接扩大到5倍

var deg = (i*5-j*20+gap);
var x = webglX(-(width/2)-200+i*((width+500)/n)+j*20);
var y = webglY(-(height/2)+Math.sin(numToDeg(deg))*(max+j*4) + j*20);
时差侧横向效果

看样子动画效果已经基本达到了效果,接下来让我们把上面的点变小,下面的点变大。产生前后的视觉差。
如何让渲染的点尺寸不同呢?没错我们也可以使用缓存区来操作。
对创建点的函数进行改造:

 function createPoints(gap) {
    //波动最大幅度 10px;
    var max = 10
    var n = 100;
    var m = 10;
    var arr = [];
    var size = [];
    for(var i = 0; i < n; i++) {
        for(var j = 0; j < m; j++) {
            var deg = (i*7-j*20+gap);
            var x = webglX(-(width/2)-200+i*((width+500)/n)+j*20);
            var y = webglY(-(height/2)+Math.sin(numToDeg(deg))*(max+j*4) + j*20);
            var z = -1;
            var item = [x, y, z];
            arr = arr.concat(item);
            size.push((4-j/4));
        }
    }
    return {
        positions: new Float32Array(arr),
        size: new Float32Array(size),
        num: m * n
    }
}

我们把点与对应的坐标匹配一下,并且把点的数量动态化,让我们在使用缓存区来把size动态批量化

function setSize(sizes, n) {
    var sizeBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, sizes, gl.STATIC_DRAW);
    var a_pointsize = gl.getAttribLocation(gl.program, 'size');
    gl.vertexAttribPointer(a_pointsize, 1, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_pointsize);
};

记得着色器上要声明一个接收的参数哦

attribute float size;

然后我们在render函数中可以直接使用其函数进行重绘了


WebGL波浪效果

我们已经绘制出来了最终效果,本着开源的原则,完整代码链接如下:

https://github.com/jdf2e/webgl-demo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 一:canvas简介 1.1什么是canvas? ①:canvas是HTML5提供的一种新标签 ②:HTML5 ...
    GreenHand1阅读 4,674评论 2 32
  • 一、canvas简介 1.1 什么是canvas?(了解) 是HTML5提供的一种新标签 Canvas是一个矩形区...
    Looog阅读 3,940评论 3 40
  • 【Android 自定义View之绘图】 基础图形的绘制 一、Paint与Canvas 绘图需要两个工具,笔和纸。...
    Rtia阅读 11,653评论 5 34
  • 一、canvas简介 1.1 什么是canvas?(了解) 是HTML5提供的一种新标签 Canvas是一个矩形区...
    J_L_L阅读 1,509评论 0 4
  • 什么是Ioc? 控制反转((Inversion of Control,英文缩写为IoC)把创建对象的权利交给框架,...
    LU7IN阅读 348评论 1 1