Skip to content
本页目录

可视化

代码仓库

kaimo313/visual-learning-demo: 跟月影大大学习可视化的笔记 (github.com)

1.开篇词-图形学

可视化架构:

image-20230522110224858

可视化学习路径

image-20230522110329878

2.预习 web前端与可视化的区别

image-20230522114651517

可视化领域的库

专业呈现各种类型图表的图表库

  • echarts
  • chart.js

专业处理地图、地理位置的可视化地理库

  • mapbox
  • leaflet
  • deck.gl
  • cesiumjs

专业处理视觉呈现的渲染库

  • 2d
    • spritejs
  • 3d
    • threejs
    • babylonjs

处理数据的数据驱动框架

  • d3

专注于数据处理,把渲染交给底层的图形系统

3.浏览器中实现可视化的四种方式

  • HTML+CSS
  • SVG
  • Canvas2d
  • WebGL

image-20230522133205714

4.canvas指令式绘图系统

4.1. Canvas 元素和 2D 上下文

html
<body>
 <canvas width="512" height="512"></canvas>
</body>
css
canvas {
 width: 256px;
 height: 256px;
}

画布宽度决定了可视区的坐标范围,所以canvas将画布宽高分开的做法,能方便适配不同显示设备

image-20230522144542035

4.2 canvas坐标系

canvas左上角坐标值为(0,0),右下角坐标值为(512,512)

image-20230522144522949

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();

绘图步骤:

  1. 获取 Canvas 对象,通过 getContext(‘2d’) 得到 2D 上下文;

  2. 设置绘图状态,比如填充颜色 fillStyle,平移变换 translate 等等;

  3. 调用 beginPath 指令开始绘制图形;

  4. 调用绘图指令,比如 rect,表示绘制矩形;

  5. 调用 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。

image-20230522150318004

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 个部分,分别是输入设备、中央处理单元、图形处理单元、存储器、帧缓存和输出设备。

image-20230522163041845

常见的术语:

光栅(Raster):几乎所有的现代图形系统都是基于光栅来绘制图形的,光栅就是指构成图像的像素阵列。

像素(Pixel):一个像素对应图像上的一个点,它通常保存图像上的某个具体位置的颜色等信息。

帧缓存(Frame Buffer):在绘图过程中,像素信息被存放于帧缓存中,帧缓存是一块内存地址。

CPU(Central Processing Unit):中央处理单元,负责逻辑计算。

GPU(Graphics Processing Unit):图形处理单元,负责图形计算。

首先,数据经过 CPU 处理,成为具有特定结构的几何信息。然后,这些信息会被送到 GPU 中进行处理。在 GPU 中要经过两个步骤生成光栅信息。这些光栅信息会输出到帧缓存中,最后渲染到屏幕上。

WebGL绘图过程

  1. 创建 WebGL 上下文

  2. 创建 WebGL 程序(WebGL Program)

  3. 将数据存入缓冲区

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

  5. 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 从顶点着色器和图元提取像素点给片元着色器执行代码的过程,就是我们前面说的

生成光栅信息的过程,我们也叫它光栅化过程。

顶点决定形状,片元是处理像素,图元决定顶点到像素(光栅化)的具体方式

我们可以将图元设为线段,那么片元着色器就会处理顶点之间的线段上的像素点信息,这样画出来的图形就是空心的

如果我们把图元设为三角形,那么片元着色器就会处理三角形内部的所有像素点,这样画出来的图形就是实心的

image-20230522165609642

因为图元是 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 轴朝外。这是一个右手坐标系

image-20230522165758636

首先,我们要定义这个三角形的三个顶点。WebGL 使用的数据需要用类型数组定义,默认

格式是 Float32Array。Float32Array 是 JavaScript 的一种类型化数组(TypedArray),

JavaScript 通常用类型化数组来处理二进制缓冲区。

我们要在这个坐标系上显示一个顶点坐标分别是(-1, -1)、(1, -1)、(0, 1)的三角形

image-20230522165835611

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 画布上画出了一个红色三角形。

image-20230522170411376

image-20230522170452537

webgl的绘图流程图

image-20230522164920082