使用
WebGL
绘图,必须要和内存、GPU
打交道,真正控制图形输出的每一个细节。
一、WebGL 最基本的概念
二、WebGL 绘制图形
- 创建
WebGL
上下文; - 创建
WebGL
程序; - 将数据存入缓冲区;
- 将缓冲区数据读取到
GPU
; GPU
执行WebGL
程序,输出结果。
1. 创建 WebGL 上下文
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
复制代码
2. 创建 WebGL 程序
创建一个
WebGLProgram
对象,给GPU
最终运行着色器的程序。
- 编写着色器
着色器是用
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); } `; 复制代码
WebGL
是以顶点和图元来描述图形几何信息的。-
顶点:几何图形的顶点。
顶点着色器(
Vertex Shader
):负责处理图形的顶点信息。
可以改变顶点的信息,如:点的坐标、法线方向、材质等,从而该百年绘制出来的图形的形状或者大小等。 -
图元:
WebGL
可直接处理的图形单元(其他非图元的图形最终必须要转换为图元才可以被WebGL
处理)。由WebGL
的绘图模式决定,有点、线、三角形等。片元着色器(
Fragment Shader
):负责处理图形的像素信息。
对指定图元中的像素点着色(处理光栅化后的像素信息)。光栅化过程:
WebGL
从顶点着色器和图元提取像素点给片元着色器执行代码的过程。
-
- 创建
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上下文对象上。 const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); 复制代码
- 启用
WebGLProgram
对象gl.useProgram(program); 复制代码
3. 将数据存入缓冲区
WebGL
的坐标系是一个右手坐标系。
WebGL
使用的数据需要用类型数组定义。
- 使用类型数组定义顶点(默认为
Float32Array
类型化数组)const points = new Float32Array([ -1, -1, 0, 1, 1, -1, ]); 复制代码
- 将定义好的数据写入WebGL缓存区
a, 创建一个缓存对象;
b, 绑定为当前操作对象;
c, 把当前的数据写入缓存对象。const bufferId = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, bufferId); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); 复制代码
4. 将缓冲区数据读取到 GPU
const vPosition = gl.getAttribLocation(program, 'position');
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
复制代码
5. 执行着色器程序完成绘制
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
复制代码
GPU
并行计算
不论图形中有多少个像素点,着色器程序在 GPU
中就会被同时执行多少次。
三、顶点着色器的作用
- 通过
gl_Position
设置顶点;// 将原有三角形的周长缩小为原来的一半 const vertex = ` attribute vec2 position; void main() { gl_PointSize = 1.0; gl_Position = vec4(position * 0.5, 1.0, 1.0); } `; 复制代码
- 通过定义
varying
变量,向片元着色器传递数据。// 将数据通过varying变量传给片元着色器 const vertex = ` attribute vec2 position; varying vec3 color; void main() { gl_PointSize = 1.0; color = vec3(0.5 + position *0.5, 0.0); gl_Position = vec4(position * 0.5, 1.0, 1.0); } `; 复制代码
四、WebGL 绘图步骤简图
五、其他
仅供交流学习。demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGL版三角形</title>
</head>
<body>
<canvas></canvas>
<script>
// 1. 创建WebGL上下文
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
// 2. 创建WebGL程序:一个WebGLProgram对象,给GPU最终运行着色器的程序。
// 顶点着色器:负责处理图形的顶点信息。
const vertex = `
attribute vec2 position;
varying vec3 color;
void main() {
gl_PointSize = 1.0;
// 线性插值:让像素点的颜色均匀渐变。
color = vec3(0.5 + position * 0.5, 0.0);
gl_Position = vec4(position * 0.5, 1.0, 1.0);
}
`;
// 创建shader对象
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.compileShader(vertexShader);
// 片元着色器:负责处理图形的像素信息。
const fragment = `
precision mediump float;
varying vec3 color;
void main() {
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
gl_FragColor = vec4(color, 1.0);
}
`;
// 创建shader对象
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragment);
gl.compileShader(fragmentShader);
// 创建WebGLProgram对象:添加shader对象,然后链接到WebGL上下文对象上。
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// 启用WebGLProgram对象
gl.useProgram(program);
// 3. 将数据存入缓冲区
// 使用类型数组定义顶点(默认为Float32Array类型化数组)
const points = new Float32Array([
-1, -1,
0, 1,
1, -1,
]);
// 将定义好的数据写入WebGL缓存区:创建一个缓存对象 》绑定为当前操作对象 》把当前的数据写入缓存对象。
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
// 4. 将缓冲区数据读取到GPU:获取顶点着色器中的position变量地址 》给变量设置长度和类型 》激活变量。
const vPosition = gl.getAttribLocation(program, 'position');
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
// 5. 执行着色器程序完成绘制
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
</script>
</body>
</html>
复制代码