引言

继续复习可视化的学习。WebGL和其他Web端的图形系统存在很大的不同,是OpenGL ES规范在浏览器的实现,它最大的不同就在于它更接近底层,可以由开发者直接操作GPU来实现绘图,性能很好,可以充分利用GPU并行计算的能力,并且WebGL还支持3D物体的渲染;WebGL最大的缺点应该就在于它的使用比较复杂,不易掌握,不同于一般的Web API使用,想要掌握好WebGL,还需要了解与WebGL相关的GLSL语言。

着色器

想要在WebGL中绘图,离不开着色器的使用,着色器是什么呢,我觉得可以简单理解为,着色器定义了如何去处理画布上的坐标点。在WebGL中有两类着色器,一类是顶点着色器,一类是片元着色器(或者也可以说片段着色器)。

顶点着色器可以认为是,声明了需要处理的坐标点;片元着色器就是定义了将这个待处理的坐标点渲染为什么颜色。当然这只是我目前学下来的一个理解,一种感觉,不一定准确。

这两类着色器都是由GPU调用的。

GPU会根据着色器程序,以及传入的数据,对批量的坐标点并行进行处理,最后渲染为一个图形。WebGL最大的特点就在于对批量的坐标点应用同一个着色器程序。所以通常来说,要绘制的坐标点和绘制的颜色,尤其是坐标点,一般不会直接写在GLSL代码中,而是由GPU从缓存中读取相关信息;所以在GPU读取之前,我们需要通过代码将数据写入缓存。

基础使用

接下来我们通过绘制一个简单的三角形来体会WebGL的使用,来了解如何使用WebGL来绘图。

首先我们在页面上准备一个canvas画布。

<canvas width="512" height="512"></canvas>
canvas {
width: 512px;
height: 512px;
border: 1px solid #eee;
}

接下来我们就开始编写JavaScript代码。

1. 获取WebGL上下文

首先是最基本的,获取WebGL上下文。

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

2. 编写着色器程序

然后是最关键的,编写着色器程序。

  • 编写着色器程序的第一步,是编写GLSL代码,来定义两个着色器。

定义着色器有两种方式,可以直接通过一段字符串定义,也可以通过使用自定义type属性的script元素把GLSL代码包含在网页中;以下我们通过字符串来定义。

const vertex = `
attribute vec2 position; void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`;

vertex变量定义的是顶点着色器的GLSL代码。

attribute表明变量是专门用于接收顶点数据的;

vec2是变量类型,表示变量是一个包含两个元素的数组,两个元素分别代表x和y坐标;

position不用说,就是变量的标识。

在运行着色器程序时,会对每一个待处理的顶点执行main函数,将position通过vec4创建一个包含4个元素的数组,把2D坐标转换为3D坐标,并赋值给gl_Position。gl_Position是内建变量,是四维向量,为什么3D坐标要用四维向量表示呢?这是因为顶点有可能会需要做一些坐标变换的操作。

gl_Position就是最终要渲染的点的坐标。

const fragment = `
precision mediump float; void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

fragment变量定义的是片元着色器的GLSL代码。

precision mediump float;这是对精度的描述,不添加会报错;

gl_FragColor也是内建变量,是四维向量,代表待渲染的点的颜色信息。vec4中的四个元素分别对应RGB三个颜色通道的色阶和alpha通道,与CSS中的RGBA不同的点在于,这里的RGB的取值在0.0到1.0之间。

  • 接下来我们就来创建着色器程序

先是分别定义两个GLSL代码对应的shader对象,并把GLSL代码传递给shader对象,然后编译这两个shader对象,这两个着色器。

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragment);
gl.compileShader(fragmentShader);

然后是创建Program对象并关联这两个shader对象,将两个shader对象链接到着色器程序。

const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

最后使WebGL上下文使用这个program程序。

gl.useProgram(program);

至此,GPU就可以通过这个着色器程序来使用两个着色器。

3. 将顶点数据存入缓冲区

现在就可以使用着色器程序来绘制我们的三角形了。

对于三角形,我们知道用三个顶点就可以确定一个三角形。

在定义三角形的顶点之前,我们需要先了解WebGL中的坐标系。和Canvas2D不同,在默认情况下,WebGL的坐标系原点(0, 0)在画布的中心,并且画布的左下角是(-1, -1),右上角是(1, 1),也就是说x和y坐标的取值范围都是-1到1。

接着我们还需要了解,WebGL中顶点信息是保存在TypedArray中的,TypedArray翻译过来可以叫做定型数组或者类型数组,我们知道在JavaScript中,普通数组中的元素并没有限制,我们可以通过push方法,插入任意类型的值,但是在定型数组中就不能这么做了,定型数组可以简单认为就是数组中所有元素的类型是被指定了的。

JavaScript通过定型数组向GPU传输数据,某种程度上也是防止GPU接收到意外类型的数据吧,并且这样GPU也不用花费额外的时间去进行数据类型的判断和转换,性能效率更高。

好了,了解完这些之后,我们就来定义三角形的三个顶点

const points = new Float32Array([
-1, -1,
0, 1,
1, -1,
]);

points变量引用了一个Float32Array类型的数组,Float32Array就是定型数组的其中一种,代表数组中的元素都是32位的浮点数。

可以看到,我们在这个数组中放了6个元素,每两个元素代表一个点的x和y坐标。

然后我们就将这些点写入WebGL的缓冲区。我的理解是,这三个点其实并不是独立的点,WebGL会将这三个点在后续用于划定要处理的范围;后续还会通过绘图模式来进一步确定要处理的点。

定义好顶点后,我们就将这些顶点数据写入缓冲区提供给WebGL使用

首先在使用前我们需要先创建buffer对象,也就是缓冲区对象。

const bufferId = gl.createBuffer();

然后将这个缓冲区指定为WebGL的操作对象,gl.ARRAY_BUFFER代表这个缓冲区存储的是顶点数据。

gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);

最后将数据写入缓冲区,以供GPU读取。

gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

gl.STATIC_DRAW代表数据加载一次,可以在多次绘制中使用。

4. 将缓冲区数据读取到GPU

完成了数据的写入后,GPU就可以从缓冲区读取数据,所以我们需要告诉GPU去哪里读取数据

因为上面缓冲区中存储的是顶点数据,所以这些数据是在顶点着色器中使用;又因为在顶点着色器的GLSL代码中,我们指定了变量position用于接收顶点数据,所以我们需要先获取position变量的地址

const vPosition = gl.getAttribLocation(program, 'position');

接着创建一个指针,指向刚刚绑定到gl.ARRAY_BUFFER的缓冲区,并保存到vPosition中。

gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);

2表示顶点数据是2个为一组读取,这是因为在顶点着色器的GLSL代码中,我们是通过vec2类型接收的;

gl.FLOAT代表读取的数据类型;

最后2个0,代表的是从缓冲区中读取数据时的偏移量,这个例子中数据都是连续写入的,所以不用管,都设置为0就可以。

最后是激活这个变量,这样在顶点着色器中就能通过变量position读取到points定型数组中对应的值了。

gl.enableVertexAttribArray(vPosition);

5. 执行着色器程序完成绘制

着色器程序和数据都准备好了之后,GPU就可以调用着色器程序并完成图形的绘制了。

首先,我们先调用gl.clear将当前画布的内容清除,就类似于Canvas2D中的clearReact。

gl.clear(gl.COLOR_BUFFER_BIT);

我们可以直接使用gl.COLOR_BUFFER_BIT,也可以自己指定颜色。

然后我们就可以开始绘制了。

gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);

drawArrays的第一个参数是绘图模式,代表绘图时所使用的图元,图元我理解就是图形的单元,就一个图形是由若干个单元组成的,比如这个代码中的gl.TRIANGLES就代表这个图形由三角形组成,它就是一个三角形,那么WebGL所要渲染的点就是整个三角形区域内的点。

如果我们设置为gl.LINE_LOOP,就代表这个图形由封闭的线段组成,会形成一个封闭图形,它默认最后一个点和第一个点连接在一起,那么WebGL所要渲染的点就是顶点所形成的封闭线段上的点。

如果我们设置为gl.LINES,就代表这个图形由一个个线段组成,那么WebGL所要渲染的点就是每一条线段上的点。

drawArrays的第二个参数是缓冲区的起始偏移量,这里我们从0开始读取。

最后一个参数是顶点的个数,由于points中每两个元素一组作为一个顶点坐标,所以数组长度除以2就是顶点的个数。

至此,就完成了三角形的绘制。

可以看到,这个三角形是红色的,这是因为我们在片元着色器中的定义,就是gl_FragColor,我们写死了vec4(1.0, 0.0, 0.0, 1.0);,这就相当于我们在CSS中写的RGBA(255, 0, 0, 1);在WebGL中,RGB色阶通道的取值也和透明度一样,在0.0到1.0之间。我们可以通过修改gl_FragColor来修改三角形的颜色。

由于我们在代码中把gl_FragColor写死了,所以在对所有点并行执行片元着色器时,渲染了同样的颜色,所以三角形整体是红色的。

可视化学习:WebGL的基础使用的更多相关文章

  1. CSGrandeur的WebGL学习——WebGL教程

    在线查看:http://csgrandeur.gitbooks.io/webgl-learn/content/ 离线mobi:http://files.cnblogs.com/files/CSGran ...

  2. 这几天开始,先学习一些 java 基础吧,学的有点累

    这几天开始,先学习一些 java 基础吧,学的有点累

  3. Emacs学习心得之 基础配置

    作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Emacs学习心得之 基础配置 1.前言2.基础配置 一.前言 本篇博文记录了Emacs的一 ...

  4. Emacs学习心得之 基础操作

    作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Emacs学习心得之 基础操作 1.前言与学习计划2.Emacs基础操作 一. 前言与学习计 ...

  5. Tensorflow学习笔记3:TensorBoard可视化学习

    TensorBoard简介 Tensorflow发布包中提供了TensorBoard,用于展示Tensorflow任务在计算过程中的Graph.定量指标图以及附加数据.大致的效果如下所示, Tenso ...

  6. 如何学习FPGA?FPGA学习必备的基础知识

    如何学习FPGA?FPGA学习必备的基础知识 时间:2013-08-12 来源:eepw 作者: 关键字:FPGA   基础知识       FPGA已成为现今的技术热点之一,无论学生还是工程师都希望 ...

  7. java与.net比较学习系列(2) 基础语言要素

    这一篇从最基础的开始对比总结,说起基础语言要素,故名思义,就是学习语言的基础,主要内容包括标识符,关键字和注释.我想从以下几点进行总结,有区别的地方有都使用红色粗体字进行了总结. 1,标识符 2,关键 ...

  8. MyBatis:学习笔记(1)——基础知识

    MyBatis:学习笔记(1)--基础知识 引入MyBatis JDBC编程的问题及解决设想 ☐ 数据库连接使用时创建,不使用时就释放,频繁开启和关闭,造成数据库资源浪费,影响数据库性能. ☐ 使用数 ...

  9. bootstrap学习笔记之基础导航条 http://www.imooc.com/code/3111

    基础导航条 在Bootstrap框中,导航条和导航从外观上差别不是太多,但在实际使用中导航条要比导航复杂得多.我们先来看导航条中最基础的一个--基础导航条. 使用方法: 在制作一个基础导航条时,主要分 ...

  10. AJAX学习前奏----JS基础加强

     AJAX学习前奏----JS基础加强 知识概要: 1.js类&属性&方法的定义 2.静态属性与方法 3.构造方法 4.原型的使用 5.Object对象直接加属性和方法 6.JSO ...

随机推荐

  1. Golang之旅——内存管理

    转载放在最前 一文带你了解,虚拟内存.内存分页.分段.段页式内存管理[Golang三关-典藏版]一站式Golang内存洗髓经 | Go 技术论坛 刘丹冰Aceld感谢以上文章作者,收获满满 存储器管理 ...

  2. Unity TextMeshPro 添加中文字体遇见的问题以及解决方案

    前言 按标准官方教程为 Unity TextMeshPro 添加中文字体时出现了各种奇奇怪怪的问题,于是有了这篇随笔. 中文字体解决方案 以下步骤适用于 TextMeshPro 3.0.6. 字符数量 ...

  3. Jmeter读取结果文件报错Error loading results file解决方法

    最近在项目性能测试过程中,遇到jmeter读取jtl文件出错的问题,如下图所示: 方法一:修改配置文件 将要读取结果文件的组件Configure界面配置都勾选上,默认情况下有些选项没勾选会出错. 第一 ...

  4. 高德Android高性能高稳定性代码覆盖率技术实践

    ​前言 代码覆盖率(Code coverage)是软件测试中的一种度量方式,用于反映代码被测试的比例和程度. 在软件迭代过程中,除了应该关注测试过程中的代码覆盖率,用户使用过程中的代码覆盖率也是一个非 ...

  5. 京东获得JD商品详情 API 返回值说明

    ​ item_get-获得JD商品详情  API测试 onebound.jd.item_get 公共参数 名称 类型 必须 描述 key String 是 调用key(必须以GET方式拼接在URL中) ...

  6. 接口未配置在app.json文件中

    微信小程序发布 提示 接口未配置在app.json文件中 狗血 昨天更新 就在app.json中添加  解决问题 "requiredPrivateInfos":[ "ge ...

  7. 浅聊一下SpringMVC的核心组件以及通过源码了解其执行流程

    浅聊一下SpringMVC的核心组件以及通过源码了解其执行流程 MVC作为WEB项目开发的核心环节,正如三个单词的分解那样,Controller(控制器)将View(视图.用户客户端)与Model(j ...

  8. OGG-Postgres实时同步到Kafka

    (一)数据同步信息 名称 源端 名称 目标端 数据库类型 Postgresql 12.4 组件类型 Kafka IP地址 20.2.127.23 Broker地址 20.2.125.52:9092, ...

  9. XL-Formula流式统计运算方式配置说明

    1.简介 XL-Formula是一种用于描述流式统计运算方式的配置标准,它代表着一种通用型流式统计系统的实现方法,更深层次它代表着一种以通用型流式统计技术为切入点,低成本实现企业数据化运营的理念.该配 ...

  10. 【NET 7.0、OpenGL ES】使用Silk.NET渲染MMD,并实时进行物理模拟。

    有关mmd播放器,网上也有许多非常漂亮的实现,如 pmxeditor.saba.blender_mmd_tools等等.. 首先我想先介绍下我参考实现的仓库: sselecirPyM/Coocoo3D ...