利用WebGL绘制最简单的几何图形
一、WebGL简介
WebGL是一种用于在网页上渲染交互式3D和2D图形的JavaScript API。它基于OpenGL ES 2.0,提供了一种在浏览器中使用硬件加速图形的方式。
二、图形系统绘图流程
图形系统的通用绘图流程会包括六个部分:
- 输入设备
- 中央处理单元:首先,数据经过 CPU 处理,成为具有特定结构的几何信息。
- 图形处理单元:然后,这些信息会被送到 GPU 中进行处理。
- 存储器
- 帧缓存:光栅信息会输出到帧缓存中
- 输出设备
- 光栅(Raster):几乎所有的现代图形系统都是基于光栅来绘制图形的,光栅就是指构成图像的像素阵列。
- 像素(Pixel):一个像素对应图像上的一个点,它通常保存图像上的某个具体位置的颜色等信息。
- 帧缓存(Frame Buffer):在绘图过程中,像素信息被存放于帧缓存中,帧缓存是一块内存地址。
- CPU(Central Processing Unit):中央处理单元,负责逻辑计算。
- GPU(Graphics Processing Unit):图形处理单元,负责图形计算。
三、渲染管线
渲染管线(Rendering Pipeline)是指计算机图形学中用于生成图像的一系列阶段和处理步骤。它描述了从输入几何数据到最终呈现图像的整个过程。渲染管线通常包括以下几个主要阶段:
- 应用阶段(Application Stage):在这个阶段,程序接收输入数据,例如顶点数据、纹理信息等。这些数据通常由应用程序提供,例如游戏引擎或者图形应用。
- 几何处理阶段(Geometry Processing Stage):在这个阶段,输入的几何数据(例如顶点、线段、三角形等)经过一系列变换(如模型变换、视图变换、投影变换等)和剪裁(Clipping),最终形成裁剪后的几何图形。
- 光栅化阶段(Rasterization Stage):在这个阶段,几何图形被转换为像素的集合,即光栅化。光栅化的过程包括确定每个像素的位置和颜色,以及应用纹理映射等操作。
- 片段处理阶段(Fragment Processing Stage):在这个阶段,对光栅化后的像素进行处理,计算最终的颜色值。这包括应用光照模型、纹理采样、深度测试、融合操作等。
- 输出阶段(Output Stage):最终生成的像素数据被发送到图形硬件的输出缓冲区,呈现在屏幕上。
四、WebGL绘图流程
1、创建获取WebGL上下文
const gl = document.querySelector("canvas").getContext("webgl");
2、创建WebGL程序(WebGL Program)
-
编写着色器
//顶点着色器 const vertex = ` attribute vec2 position; void main(){ gl_PointSize = 1.0; gl_Position = vec4(position,0.0,1.0); } `; // 片元着色器 const fragment = ` precision mediump float; void main(){ gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `;
-
创建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对象
//创建WebGLPrgame对象 const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader);
-
连接并启用WebGLProgram对象
gl.linkProgram(program); gl.useProgram(program); //启用webglProgram对象
3、将数据存入缓冲区
-
创建一个缓存对象
const triangle = new Float32Array([-1, -1, 0, 1, 1, -1]); const bufferId = gl.createBuffer();
-
绑定为当前操作对象
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
-
当前数据写入缓存对象
gl.bufferData(gl.ARRAY_BUFFER, triangle, gl.STATIC_DRAW);
4、将缓冲区数据读取到GPU
//获取顶点着色器的position的变量位置下标
const vPosition = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);//从bindBuffer绑定的缓冲区中读取数据
gl.enableVertexAttribArray(vPosition);//激活这个变量
5、GPU执行WebGL程序,输出结果
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, triangle.length / 2);
五、完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<canvas width="500" height="500"></canvas>
</body>
<script type="module">
const gl = document.querySelector("canvas").getContext("webgl");
//顶点着色器
const vertex = `
attribute vec2 position;
void main(){
gl_PointSize = 1.0;
gl_Position = vec4(position,0.0,1.0);
}
`;
// 片元着色器
const fragment = `
precision mediump float;
void main(){
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
`;
//创建顶点着色器
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);
//创建WebGLPrgame对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program); //启用webglProgram对象
//创建一个三角形坐标数据,并存入缓冲区
const triangle = new Float32Array([-1, -1, 0, 1, 1, -1]);
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, triangle, gl.STATIC_DRAW);
//获取顶点着色器的position的变量位置下标
const vPosition = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);//从bindBuffer绑定的缓冲区中读取数据
gl.enableVertexAttribArray(vPosition);//激活这个变量
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, triangle.length / 2);
</script>
</html>
六、顶点着色器向片元着色器传值
// 顶点着色器
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);
}`;
// 片元着色器
const fragment = `
precision mediump float;
varying vec3 color;
void main(){
gl_FragColor = vec4(color,1.0);
}`;