可视化学习:WebGL的基础使用
引言
继续复习可视化的学习。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的基础使用的更多相关文章
- CSGrandeur的WebGL学习——WebGL教程
在线查看:http://csgrandeur.gitbooks.io/webgl-learn/content/ 离线mobi:http://files.cnblogs.com/files/CSGran ...
- 这几天开始,先学习一些 java 基础吧,学的有点累
这几天开始,先学习一些 java 基础吧,学的有点累
- Emacs学习心得之 基础配置
作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Emacs学习心得之 基础配置 1.前言2.基础配置 一.前言 本篇博文记录了Emacs的一 ...
- Emacs学习心得之 基础操作
作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Emacs学习心得之 基础操作 1.前言与学习计划2.Emacs基础操作 一. 前言与学习计 ...
- Tensorflow学习笔记3:TensorBoard可视化学习
TensorBoard简介 Tensorflow发布包中提供了TensorBoard,用于展示Tensorflow任务在计算过程中的Graph.定量指标图以及附加数据.大致的效果如下所示, Tenso ...
- 如何学习FPGA?FPGA学习必备的基础知识
如何学习FPGA?FPGA学习必备的基础知识 时间:2013-08-12 来源:eepw 作者: 关键字:FPGA 基础知识 FPGA已成为现今的技术热点之一,无论学生还是工程师都希望 ...
- java与.net比较学习系列(2) 基础语言要素
这一篇从最基础的开始对比总结,说起基础语言要素,故名思义,就是学习语言的基础,主要内容包括标识符,关键字和注释.我想从以下几点进行总结,有区别的地方有都使用红色粗体字进行了总结. 1,标识符 2,关键 ...
- MyBatis:学习笔记(1)——基础知识
MyBatis:学习笔记(1)--基础知识 引入MyBatis JDBC编程的问题及解决设想 ☐ 数据库连接使用时创建,不使用时就释放,频繁开启和关闭,造成数据库资源浪费,影响数据库性能. ☐ 使用数 ...
- bootstrap学习笔记之基础导航条 http://www.imooc.com/code/3111
基础导航条 在Bootstrap框中,导航条和导航从外观上差别不是太多,但在实际使用中导航条要比导航复杂得多.我们先来看导航条中最基础的一个--基础导航条. 使用方法: 在制作一个基础导航条时,主要分 ...
- AJAX学习前奏----JS基础加强
AJAX学习前奏----JS基础加强 知识概要: 1.js类&属性&方法的定义 2.静态属性与方法 3.构造方法 4.原型的使用 5.Object对象直接加属性和方法 6.JSO ...
随机推荐
- 一键部署 Umami 统计个人网站访问数据
谈到网站统计,大家第一时间想到的肯定是 Google Analytics.然而,我们都知道 Google Analytics 会收集所有用户的信息,对数据没有任何控制和隐私保护. Google Ana ...
- 一文了解Gin对Cookie的支持
1. 引言 本文将从Web应用程序处理请求时需要用户信息,同时HTTP又是无状态协议这个矛盾点出发.从该问题出发,简单描述了解决该问题的Token 机制,进而引出Cookie的实现方案. 基于此我们将 ...
- 零代码,使用 Dify 和 Laf 两分钟接入企业微信 AI 机器人
Dify 允许创建 AI 应用,并提供二次开发的能力.这里我将演示创建一个法律问答助手的 AI 应用,称作"知法".在本篇教程中,我将指导你为"知法"接入企业微 ...
- 商品详情接口设计:使用API调用获取淘宝商品数据的完整方案
在如今的电商时代,获取商品的详细信息是实现商业化应用的基础.本文将详细介绍如何通过API调用来获取淘宝商品数据,并提供一个完整的商品详情接口设计方案,包括代码示例.开发人员可以根据此方案快速实现商 ...
- 多层前馈神经网络及BP算法
一.多层前馈神经网络 首先说下多层前馈神经网络,BP算法,BP神经网络之间的关系.多层前馈[multilayer feed-forward]神经网络由一个输入层.一个或多个隐藏层和一个输出层组成,后向 ...
- QA|selenium在send_keys时报错dict object has no attribute ''|UI自动化测试
Q:selenium在send_keys时报错dict object has no attribute 'send_keys',如下图 增加了print(type(e1))发现确实是字典类型,怪了,按 ...
- 一文解锁vue3中hooks的使用姿势
vue3 中的 hooks 是什么? 简单来说如果你的函数中用到了诸如 ref,reactive,onMounted 等 vue 提供的 api 的话,那么它就是一个 hooks 函数,如果没用到它就 ...
- 【爬虫实战】用python爬豆瓣电影《热烈》短评
目录 一.爬虫对象-豆瓣电影短评 二.爬取结果 三.爬虫代码讲解 三.演示视频 四.获取完整源码 一.爬虫对象-豆瓣电影短评 您好!我是@马哥python说,一名10年程序猿. 今天分享一期爬虫案例, ...
- 「joisc2016 - D3T2」回転寿司
题意大概是这样,「每次操作选出区间中的一个 LIS(strictly),满足其开端是极靠近左端点且大于 \(A\) 的位置,答案即这个 LIS 的末尾,做一个轮换后弹出序列末端」. 首先做几个观察. ...
- 利用SPI实现全自动化——LCD屏与RGB灯
如果你开启了广告屏蔽,请将博客园加入白名单,帮助博客园渡过难关,谢谢! 前言 在21年做物理实验和23年客串电赛之后,我带着STM32重回电子DIY界.这次的项目是一个电池供电的补光灯,由于用途更偏向 ...