Skip to content

WebGL - 绘制模式

WebGL 提供了几种解释顶点数据以形成不同形状的方式,这些方式称为绘制模式(drawing modes)或基本类型(primitive types)。这些模式被指定为 gl.drawArrays() 和 gl.drawElements() 函数的第一个参数。

回顾绘制命令的语法:

void gl.drawArrays(mode, first, count);
void gl.drawElements(mode, count, type, offset);

mode 参数决定了如何连接顶点(由 count 和 first/offset 指定,可能还有索引)来形成几何图元(geometric primitives)。

序号模式常量描述顶点顺序示例
1gl.POINTS为每个指定的顶点绘制一个点。点的大小可以通过顶点着色器中的 gl_PointSize 控制。
2gl.LINES绘制一系列独立的线段。顶点 (0, 1)、(2, 3)、(4, 5) 等,每对形成一条独立的线段。需要偶数个顶点(count 必须是 2 的倍数)。
3gl.LINE_STRIP绘制一系列相连的线段。顶点 (0, 1)、(1, 2)、(2, 3) 等,形成一条连续的折线。
4gl.LINE_LOOP类似于 gl.LINE_STRIP,但会额外绘制一条线段连接最后一个顶点回第一个顶点,形成一个闭合循环。
5gl.TRIANGLES绘制一系列独立的三角形。顶点 (0, 1, 2)、(3, 4, 5) 等,每三个形成一个独立的三角形。需要 count 是 3 的倍数。
6gl.TRIANGLE_STRIP绘制一系列相连的三角形。在第一个三角形 (0, 1, 2) 之后,随后的顶点 n 形成三角形 (n-1, n-2, n)、(n-1, n, n+1) 等,交替缠绕顺序(winding order)。对于以条带形式表示的网格(meshes)渲染非常高效。Text Description: Creates triangles (v0,v1,v2), (v2,v1,v3), (v2,v3,v4), (v4,v3,v5)…
7gl.TRIANGLE_FAN绘制一系列共享第一个顶点(顶点 0)的相连三角形。三角形的形成方式是 (0, 1, 2)、(0, 2, 3)、(0, 3, 4) 等。适用于绘制圆形、圆锥或扇形等形状。Text Description: Creates triangles (v0,v1,v2), (v0,v2,v3), (v0,v3,v4)…

此示例使用 gl.LINES 绘制三条独立的水平线段。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebGL Drawing Modes - Lines</title>
<style>
body { margin: 0; overflow: hidden; background-color: #fff; }
canvas { display: block; border: 1px solid black; }
</style>
</head>
<body>
<canvas id="webgl-canvas" width="400" height="400"></canvas>
<script>
// --- 着色器源代码(简单直通)---
const vsSource = `
attribute vec3 a_position;
void main() {
gl_Position = vec4(a_position, 1.0);
// 使线即使很细也能可见
gl_PointSize = 1.0; // 如果线很细,某些驱动程序可能需要此设置
}
`;
const fsSource = `
precision mediump float;
void main() {
gl_FragColor = vec4(0.0, 0.0, 0.8, 1.0); // 蓝色
}
`;
// --- 工具函数:createShader, createProgram (假定已定义) ---
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(`Shader compilation error (${type === gl.VERTEX_SHADER ? 'Vertex' : 'Fragment'}):`, gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function createProgram(gl, vsSource, fsSource) { /* ... */
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
if (!vertexShader || !fragmentShader) return null;
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program linking error:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
return program;
}
// --- 主 WebGL 代码 ---
const canvas = document.getElementById('webgl-canvas');
const gl = canvas.getContext('webgl');
if (!gl) { alert('WebGL not supported.'); throw new Error('WebGL not supported'); }
const shaderProgram = createProgram(gl, vsSource, fsSource);
if (!shaderProgram) throw new Error('Failed program creation.');
// --- 几何体(3 条独立线段的 6 个顶点)---
const lineVertices = new Float32Array([
// 第 1 条线
-0.7, 0.6, 0.0, // 起点
0.7, 0.6, 0.0, // 终点
// 第 2 条线
-0.7, 0.0, 0.0,
0.7, 0.0, 0.0,
// 第 3 条线
-0.7, -0.6, 0.0,
0.7, -0.6, 0.0
]);
// --- 缓冲区 ---
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, lineVertices, gl.STATIC_DRAW);
// --- 属性设置 ---
const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'a_position');
if (positionAttribLocation < 0) console.error("Attr 'a_position' not found");
gl.enableVertexAttribArray(positionAttribLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, false, 0, 0);
// --- 绘制 ---
gl.useProgram(shaderProgram);
gl.clearColor(1.0, 1.0, 1.0, 1.0); // 白色背景
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// 使用 LINES 模式绘制
gl.drawArrays(
gl.LINES, // 模式:绘制独立线段
0, // 起始索引
6 // 顶点数量 (3 条线段 * 每条线段 2 个顶点)
);
</script>
</body>
</html>

预期输出:

代码将在白色背景上垂直堆叠渲染出三条独立的水平蓝色线段。

可视化其他模式(使用相同的顶点)

Section titled “可视化其他模式(使用相同的顶点)”

如果您使用上述示例中相同的六个顶点,并将 gl.LINES 更改为 gl.drawArrays 调用中的其他模式,您将看到不同的结果:

  • gl.LINE_STRIP:一条连接所有六个点的 Z 字形线:(-0.7, 0.6) -> (0.7, 0.6) -> (-0.7, 0.0) -> (0.7, 0.0) -> (-0.7, -0.6) -> (0.7, -0.6)。
  • gl.LINE_LOOP:与 gl.LINE_STRIP 相同,但会额外绘制一条线连接最后一个点 (0.7, -0.6) 回第一个点 (-0.7, 0.6),形成一个闭合的复杂形状。
  • gl.TRIANGLES:两个独立的三角形。第一个使用顶点 0、1、2 ((-0.7, 0.6), (0.7, 0.6), (-0.7, 0.0))。第二个使用顶点 3、4、5 ((0.7, 0.0), (-0.7, -0.6), (0.7, -0.6))。
  • gl.TRIANGLE_STRIP:一系列相连的三角形:(0,1,2)、(1,2,3)、(2,3,4)、(3,4,5)。这会形成一个复杂、可能重叠的形状。
  • gl.TRIANGLE_FAN:一组从顶点 0 发散的三角形:(0,1,2)、(0,2,3)、(0,3,4)、(0,4,5)。这也会形成一个以第一个顶点为中心的复杂形状。

选择正确的绘制模式取决于您想要渲染的几何体以及您的顶点/索引数据的组织方式。