一、前言
gl.drawArrays 是WebGL中最核心的绘图方法之一。它就像一个“绘图指挥官”,告诉GPU如何解释顶点缓冲区中的数据,并将其绘制成点、线或三角形。理解它的每种绘图模式及其适用场景,是驾驭WebGL、实现从简单粒子到复杂3D模型的基础。本文将带您深入掌握这个命令,并通过完整的代码示例演示每种模式的实际用法。

二、方法签名与核心参数
gl.drawArrays(mode, first, count) 方法接受三个参数,决定了绘图的方式和范围。
| mode | GLenum | 指定绘制图元的类型。WebGL根据这个模式将顶点数据组装成不同的图形。 |
| first | GLint | 指定从顶点数组的哪个索引开始读取数据(通常从0开始)。 |
| count | GLsizei | 指定要渲染的顶点数量。 |
三、核心参数详解:绘制模式(mode)
mode 参数是 gl.drawArrays 的灵魂,它决定了图形的组装方式。
| gl.POINTS | 0x0000 | 绘制独立的点 | 每个顶点作为一个单独的点。 | 粒子系统:绘制星空、雪花、大量子弹或标记点。 |
| gl.LINES | 0x0001 | 绘制独立的线段 | 顶点对 (v0, v1),(v2, v3),… 构成线段。若顶点数为奇数,最后一个被忽略。 | 线框网格:绘制模型的边缘。网络拓扑图:连接不连续的点。 |
| gl.LINE_STRIP | 0x0003 | 绘制一条连续折线 | 线段由 (v0, v1),(v1, v2),(v2, v3),… 构成。 | 函数图像:绘制正弦波、股票走势图。路径动画:显示物体移动轨迹。 |
| gl.LINE_LOOP | 0x0002 | 绘制一条闭合折线 | 与 LINE_STRIP 类似,但会在最后一个顶点和第一个顶点之间自动添加一条线段。 | 绘制多边形轮廓:如空心矩形、多边形边框。 |
| gl.TRIANGLES | 0x0004 | 绘制独立的三角形 | 顶点三元组 (v0, v1, v2),(v3, v4, v5),… 构成三角形。 | 绝大多数3D模型:任何实体表面都可分解为三角形。 |
| gl.TRIANGLE_STRIP | 0x0005 | 绘制一个三角形带 | 前三个顶点构成第一个三角形,之后每新增一个顶点与上两个顶点构成新三角形。 | 高效构建连续表面:如地形、带状物体(道路、丝带)。优势:顶点利用率高。 |
| gl.TRIANGLE_FAN | 0x0006 | 绘制一个三角形扇 | 前三个顶点构成第一个三角形,之后每新增一个顶点与第一个顶点和上一个顶点构成新三角形。 | 绘制锥体或扇形:如金字塔侧面、圆形或饼图填充。 |
类型如下图

四、完整代码示例
以下示例均使用相同的顶点数据(六个顶点位置,外加颜色属性),以便直观对比不同模式下的绘制结果。每个示例都是完整可运行的HTML文件。
准备工作:基础HTML模板与通用着色器
所有示例共用一个基础模板,只需替换 <script> 标签内的核心绘制逻辑即可。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGL示例</title>
<style>
#webgl {
border: 2px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
<!– 使用普通方式引入,不要用 module –>
<script src="main.js"></script>
</body>
</html>
</body>
</html>
公共代码
// ================== 封装工具:创建着色器程序 ==================
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function createProgram(gl, vsSource, fsSource) {
const vs = createShader(gl, gl.VERTEX_SHADER, vsSource);
const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program));
return null;
}
return program;
}
// ===== 通用的顶点属性设置 (位置2分量 + 颜色3分量, 步长20字节) =====
function setupPositionAndColor(gl, program, bufferData) {
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(bufferData), gl.STATIC_DRAW);
const posLoc = gl.getAttribLocation(program, 'a_position');
const colorLoc = gl.getAttribLocation(program, 'a_color');
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 20, 0);
if (colorLoc !== -1) {
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, 20, 8);
}
return { posLoc, colorLoc };
}
1. gl.POINTS:独立点
-
核心代码替换:
javascript
const vs = `
attribute vec2 a_position;
attribute vec3 a_color;
varying vec3 v_color;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
gl_PointSize = 22.0;
v_color = a_color;
}`;const fs = `
precision mediump float;
varying vec3 v_color;
void main() {
vec2 coord = gl_PointCoord – vec2(0.5);
float d = length(coord);
if (d > 0.5) discard;
float alpha = 1.0 – smoothstep(0.2, 0.5, d);
gl_FragColor = vec4(v_color, alpha);
}
`;
const program = createProgram(gl, vs, fs);
gl.useProgram(program);const vertices = [
// 位置 (x,y) , 颜色 (r,g,b)
0.0, 0.65, 1.0, 0.3, 0.3, // 上红
0.55, 0.1, 0.3, 1.0, 0.3, // 右绿
0.3, -0.55, 0.3, 0.3, 1.0, // 右下蓝
-0.3, -0.55, 1.0, 1.0, 0.2, // 下黄
-0.55, 0.1, 1.0, 0.4, 1.0 // 左紫
];
setupPositionAndColor(gl, program, vertices);
gl.drawArrays(gl.POINTS, 0, 5); -
应用场景:非常适合粒子系统,如在游戏中绘制星星、火花或标记点。
-
运行效果: 独立圆点。

2. gl.LINES:独立线段
-
核心代码替换:
javascript
const vs = `
attribute vec2 a_position;
attribute vec3 a_color;
varying vec3 v_color;
void main() {
gl_Position = vec4(a_position,0.0,1.0); v_color = a_color;
} `;const fs = `
precision mediump float;
varying vec3 v_color;
void main() {
gl_FragColor = vec4(v_color,1.0);
}`;const program = createProgram(gl, vs, fs);
gl.useProgram(program);
const vertices = [
// 横线红
-0.7, 0.0, 1.0, 0.2, 0.2,
0.7, 0.0, 1.0, 0.2, 0.2,
// 竖线绿
0.0, -0.7, 0.2, 1.0, 0.2,
0.0, 0.7, 0.2, 1.0, 0.2,
// 斜线蓝 (\\)
-0.6, -0.6, 0.2, 0.4, 1.0,
0.6, 0.6, 0.2, 0.4, 1.0,
// 斜线黄 (/)
-0.6, 0.6, 1.0, 1.0, 0.2,
0.6, -0.6, 1.0, 1.0, 0.2
];
setupPositionAndColor(gl, program, vertices);
gl.drawArrays(gl.LINES, 0, 8); -
应用场景:绘制网格线、DNA双螺旋结构的横档、或任何不需要连接的线条。

3. gl.LINE_STRIP:连续折线
-
核心代码替换:
javascript
const vs = `
attribute vec2 a_position;
void main() { gl_Position = vec4(a_position,0.0,1.0);
}`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(0.3,0.9,0.4,1.0);
}`;const program = createProgram(gl, vs, fs);
gl.useProgram(program);const points = [];
for (let i = 0; i <= 20; i++) {
const t = i / 20;
const x = -0.8 + t * 1.6;
const y = Math.sin(x * 6.5) * 0.45;
points.push(x, y);
}const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW);const posLoc = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(posLoc);gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINE_STRIP, 0, 21);
-
应用场景:完美适配数据可视化中的折线图、绘制心电波形或路径轨迹。

4. gl.LINE_LOOP:闭合折线
-
核心代码替换:
javascript
const vs = `
attribute vec2 a_position;
attribute float a_bright;
varying float v_bright;
void main() {
gl_Position = vec4(a_position,0.0,1.0); v_bright = a_bright;
}
`;
const fs = `
precision mediump float;
varying float v_bright;
void main() {
gl_FragColor = vec4(0.4, 0.7, 1.0, 1.0) * (0.6 + 0.5 * v_bright);
}
`;
const program = createProgram(gl, vs, fs);
gl.useProgram(program);const data = [];
for (let i = 0; i < 6; i++) {
const angle = (i / 6) * Math.PI * 2;
const x = Math.cos(angle) * 0.7;
const y = Math.sin(angle) * 0.7;
data.push(x, y, i / 6); // 亮度因子
}
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);const posLoc = gl.getAttribLocation(program, 'a_position');
const brightLoc = gl.getAttribLocation(program, 'a_bright');
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 12, 0);
gl.enableVertexAttribArray(brightLoc);
gl.vertexAttribPointer(brightLoc, 1, gl.FLOAT, false, 12, 8);gl.drawArrays(gl.LINE_LOOP, 0, 6);
-
应用场景:绘制多边形边框、区域轮廓或循环路径示意图。
-

5. gl.TRIANGLES:独立三角形
-
核心代码替换:
javascript
const vs = `
attribute vec2 a_position;
attribute vec3 a_color;
varying vec3 v_color;
void main() {
gl_Position = vec4(a_position,0.0,1.0);
v_color = a_color;
}`;const fs = `
precision mediump float;
varying vec3 v_color;
void main() {
gl_FragColor = vec4(v_color,1.0);
}`;const program = createProgram(gl, vs, fs);
gl.useProgram(program);const vertices = [
// tri1 (红,绿,蓝)
-0.5, -0.5, 1.0, 0.2, 0.2,
0.5, -0.5, 0.2, 1.0, 0.2,
-0.5, 0.5, 0.2, 0.2, 1.0,
// tri2 (青,紫,黄)
0.5, -0.5, 0.2, 1.0, 1.0,
0.5, 0.5, 1.0, 0.3, 1.0,
-0.5, 0.5, 1.0, 1.0, 0.2
];
setupPositionAndColor(gl, program, vertices);
gl.drawArrays(gl.TRIANGLES, 0, 6); -
应用场景:最基础的3D建模方式,用于构建任何复杂模型的表面。

6. gl.TRIANGLE_STRIP:矩形
-
核心代码替换:
javascript
const vs = `
attribute vec2 a_position;
attribute vec3 a_color;
varying vec3 v_color;
void main() { gl_Position = vec4(a_position,0.0,1.0); v_color = a_color; }
`;
const fs = `
precision mediump float;
varying vec3 v_color;
void main() {
gl_FragColor = vec4(v_color,1.0);
}`;const program = createProgram(gl, vs, fs);
gl.useProgram(program);const vertices = [
-0.6, -0.6, 1.0, 0.3, 0.3, // 左下红
-0.6, 0.6, 0.3, 1.0, 0.3, // 左上绿
0.6, -0.6, 0.3, 0.3, 1.0, // 右下蓝
0.6, 0.6, 1.0, 1.0, 0.3 // 右上黄
];
setupPositionAndColor(gl, program, vertices);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); -
应用场景:高效构建地形、路面、管道等连续表面,能显著减少顶点数据传输。

7. gl.TRIANGLE_FAN:三角形扇
-
核心代码替换:
javascript
const vs = `
attribute vec2 a_position;
attribute float a_factor;
varying float v_factor;
void main() {
gl_Position = vec4(a_position,0.0,1.0);
v_factor = a_factor; }
`;
const fs = `
precision mediump float;
varying float v_factor;
void main() {
float r = 0.6 + 0.5 * sin(v_factor * 6.28);
float g = 0.5 + 0.5 * cos(v_factor * 5.0);
float b = 0.7 + 0.3 * sin(v_factor * 7.0 + 2.0);
gl_FragColor = vec4(r, g, b, 1.0);
} `;
const program = createProgram(gl, vs, fs);
gl.useProgram(program);const data = [0, 0, 0]; // 中心
for (let i = 0; i <= 8; i++) {
const angle = (i / 8) * Math.PI * 2;
const x = Math.cos(angle) * 0.7;
const y = Math.sin(angle) * 0.7;
data.push(x, y, i / 8);
}
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);const posLoc = gl.getAttribLocation(program, 'a_position');
const factorLoc = gl.getAttribLocation(program, 'a_factor');
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 12, 0);
gl.enableVertexAttribArray(factorLoc);
gl.vertexAttribPointer(factorLoc, 1, gl.FLOAT, false, 12, 8);gl.drawArrays(gl.TRIANGLE_FAN, 0, 10);
-
应用场景:绘制锥体侧面、圆形填充、或任何从一个中心点向外辐射的图形,如饼图。

五、性能考量与最佳实践
顶点数量 (count) 的选择:count 并非越大越好。它受GPU处理能力和显存带宽限制。对于粒子系统(POINTS),轻松支持数万点;对于复杂模型,需要结合视锥剔除等技术优化。
first 参数的妙用:可以将多个对象的顶点数据存储在同一个缓冲区中,通过不同的 first 和 count 值分批绘制,减少缓冲区切换的开销。
与 gl.drawElements 的选择:drawArrays 简单直接,适合顶点不重复的几何体(如粒子)。对于顶点复用率高的模型(如立方体,8个顶点但要绘制12个三角形),使用索引绘制 (gl.drawElements) 更节省内存。
六、总结
gl.drawArrays 的七种绘制模式,为图形表达提供了极大的灵活性。从最简单的点,到复杂的三角形扇,选择合适的模式不仅能正确呈现图形,更是优化性能的关键。希望通过本文的参数详解和完整的可运行示例,能帮助您在未来的WebGL项目中,更自信、更高效地使用这个核心命令。
感谢阅读! 喜欢本文请不要吝啬你的 一键三连:点赞、收藏、留言,让更多小伙伴看到这份干货!





