Appearance
可视化
代码仓库
kaimo313/visual-learning-demo: 跟月影大大学习可视化的笔记 (github.com)
1.开篇词-图形学
可视化架构:
可视化学习路径
2.预习 web前端与可视化的区别
可视化领域的库
专业呈现各种类型图表的图表库;
- echarts
- chart.js
专业处理地图、地理位置的可视化地理库;
- mapbox
- leaflet
- deck.gl
- cesiumjs
专业处理视觉呈现的渲染库;
- 2d
- spritejs
- 3d
- threejs
- babylonjs
处理数据的数据驱动框架。
- d3
专注于数据处理,把渲染交给底层的图形系统
3.浏览器中实现可视化的四种方式
- HTML+CSS
- SVG
- Canvas2d
- WebGL
4.canvas指令式绘图系统
4.1. Canvas 元素和 2D 上下文
html
<body>
<canvas width="512" height="512"></canvas>
</body>
css
canvas {
width: 256px;
height: 256px;
}
画布宽度决定了可视区的坐标范围,所以canvas将画布宽高分开的做法,能方便适配不同显示设备
4.2 canvas坐标系
canvas左上角坐标值为(0,0),右下角坐标值为(512,512)
4.3利用canvas绘制几何图形
4.3.1获取canvas上下文
js
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
4.3.2用canvas上下文绘制图像
javascript
const rectSize = [100, 100];
context.fillStyle = 'red';
context.beginPath();
context.rect(0.5 * canvas.width, 0.5 * canvas.height, ...rectSize);
context.fill();
绘图步骤:
获取 Canvas 对象,通过 getContext(‘2d’) 得到 2D 上下文;
设置绘图状态,比如填充颜色 fillStyle,平移变换 translate 等等;
调用 beginPath 指令开始绘制图形;
调用绘图指令,比如 rect,表示绘制矩形;
调用 fill 指令,将绘制内容真正输出到画布上。
5.svg声明式图形系统
利用svg绘制几何图形
html
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="orange" />
</svg>
svg 元素下的 circle 元素表示这是一个绘制在 SVG 图像中的圆形,属性 cx 和 cy 是坐标,表示圆心的位置在图像的 x=100、y=50 处。属性 r 表示半径,r=40 表示圆的半径为40。
js代码去控制svg
javascript
function draw(parent, node, {fillStyle = 'rgba(0, 0, 0, 0.2)', textColor = 'white'} = {}){
const {x, y, r} = node;
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circ
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', r);
circle.setAttribute('fill', fillStyle);
parent.appendChild(circle);
...
}
参数默认值解构的写法
上述的代码实际上等于function draw(ctx, node, fillStyle = 'rgba(0, 0, 0, 0.2)', textColor = 'white' ) {}
因为同样调用: draw(context, el, {fillStyle: 'red', textColor: 'black'}) 比 draw(context, el, 'red', 'black') 可读性要好 假设你是代码维护者,前者你一看就知道后面两个颜色参数哪个是fillStyle,哪个是textColor;后者你不看函数定义,不知道red和black分别哪个是fillStyle,哪个是textColor。 代码是给人阅读的,所以设计API的时候需要考虑这样的细节。
svg与canvas的区别
5.1.写法不同
与canvas不同,SVG 首先通过创建标签来表示图形元素,circle 表示圆,g
表示分组,text 表示文字。接着,SVG 通过元素的 setAttribute 给图形元素赋属性值,这
个和操作 HTML 元素是一样的。
5.2用户交互实现上的不同
给 SVG 的根元素添加 mousemove 事件,添加代码的操作很简单,你可以
直接看代码
SVG 有一个非常大的优点,那就是可以让图形的用户交互非常简单
javascript
let activeTarget = null;
svgroot.addEventListener('mousemove', (evt) => {
let target = evt.target;
if(target.nodeName === 'text') target = target.parentNode;
if(activeTarget !== target) {
if(activeTarget) activeTarget.setAttribute('fill', 'rgba(0, 0, 0, 0.2)')
}
target.setAttribute('fill', 'rgba(0, 128, 0, 0.1)');
activeTarget = target;
});
6.WebGL:GPU与渲染管线
一个通用计算机图形系统主要包括 6 个部分,分别是输入设备、中央处理单元、图形处理单元、存储器、帧缓存和输出设备。
常见的术语:
光栅(Raster):几乎所有的现代图形系统都是基于光栅来绘制图形的,光栅就是指构成图像的像素阵列。
像素(Pixel):一个像素对应图像上的一个点,它通常保存图像上的某个具体位置的颜色等信息。
帧缓存(Frame Buffer):在绘图过程中,像素信息被存放于帧缓存中,帧缓存是一块内存地址。
CPU(Central Processing Unit):中央处理单元,负责逻辑计算。
GPU(Graphics Processing Unit):图形处理单元,负责图形计算。
首先,数据经过 CPU 处理,成为具有特定结构的几何信息。然后,这些信息会被送到 GPU 中进行处理。在 GPU 中要经过两个步骤生成光栅信息。这些光栅信息会输出到帧缓存中,最后渲染到屏幕上。
WebGL绘图过程
创建 WebGL 上下文
创建 WebGL 程序(WebGL Program)
将数据存入缓冲区
将缓冲区数据读取到 GPU
GPU 执行 WebGL 程序,输出结果
1.创建 WebGL 上下文
javascript
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
2.创建WebGL程序
首先,要创建这个 WebGL 程序,我们需要编写两个着色器(Shader)。着色器是用GLSL 这种编程语言编写的代码片段
const vertex = `
attribute vec2 position;
void main() {
gl_PointSize = 1.0;
gl_Position = vec4(position, 1.0, 1.0);
}
`;
const fragment = `
precision mediump float;
void main()
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
着色器(Shader)
那我们为什么要创建两个着色器呢?这就需要我们先来理解顶点和图元这两个基本概念了。
顶点着色器理解为处理顶点的 GPU 程序代码。它可以改变顶点的信息(如顶点的坐标、法线方向、材质等等),从而改变我们绘制出来的图形的形状或者
大小等等。
**片元着色器的作用,就是处理光栅化后的像素信息。**WebGL 从顶点着色器和图元提取像素点给片元着色器执行代码的过程,就是我们前面说的
生成光栅信息的过程,我们也叫它光栅化过程。
顶点决定形状,片元是处理像素,图元决定顶点到像素(光栅化)的具体方式
我们可以将图元设为线段,那么片元着色器就会处理顶点之间的线段上的像素点信息,这样画出来的图形就是空心的
如果我们把图元设为三角形,那么片元着色器就会处理三角形内部的所有像素点,这样画出来的图形就是实心的
因为图元是 WebGL 可以直接处理的图形单元,所以其他非图元的图形最终必须要转换为图元才可以被 WebGL 处理。
举个例子,如果我们要绘制实心的四边形,我们就需要将四边形拆分成两个三角形,再交给 WebGL 分别绘制出来。
将glsl的代码转换成js中的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);
我们创建 WebGLProgram 对象,并将这两个 shader 关联到这个 WebGL 程序上
javascript
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
GPU 就会执行我们通过 WebGLProgram 设定的 两个 shader 程序了。
javascript
gl.useProgram(program);
3.将数据存入缓冲区域
webgl的坐标系是一个三维空间坐标系,坐标原点是(0,0,0)。其中,x 轴朝右,y 轴朝上,z 轴朝外。这是一个右手坐标系
首先,我们要定义这个三角形的三个顶点。WebGL 使用的数据需要用类型数组定义,默认
格式是 Float32Array。Float32Array 是 JavaScript 的一种类型化数组(TypedArray),
JavaScript 通常用类型化数组来处理二进制缓冲区。
我们要在这个坐标系上显示一个顶点坐标分别是(-1, -1)、(1, -1)、(0, 1)的三角形
javascript
const points = new Float32Array([
-1, -1,
0, 1,
1, -1,
]);
接着,我们要将定义好的数据写入 WebGL 的缓冲区
javascript
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
4.将缓冲区数据读取到GPU
顶点着色器代码
javascript
attribute vec2 position;
void main() {
gl_PointSize = 1.0;
gl_Position = vec4(position, 1.0, 1.0);
}
接下来我们将 buffer 的数据绑定给顶点着色器的 position 变量
javascript
const vPosition = gl.getAttribLocation(program, 'position');获取顶点着色器中的posi
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);给变量设置长度和类型
gl.enableVertexAttribArray(vPosition);激活这个变量
经过这样的处理,在顶点着色器中,我们定义的 points 类型数组中对应的值,就能通过变量 position 读到了。
5.执行着色器程序完成绘制
javascript
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
这样,我们就在 Canvas 画布上画出了一个红色三角形。