首页/ 学科题库/ webgl/ WebGL 绘制类型与参数化顶点生成
webgl 实战教程

WebGL 绘制类型与参数化顶点生成

2026-09-13 约 8 分钟 juanwangdev

WebGL 绘制类型与参数化顶点生成

一句话结论:绘制类型决定顶点如何被组装成图元,参数化生成决定顶点数据从哪来,两者配合才能用最少的数据画出想要的形状。

绘制类型与 drawArrays

是什么

绘制类型是 drawArrays 的第一个参数,用来告诉 GPU 如何把顶点序列组装成图元,常用取值有 POINTS、LINES、LINE_STRIP、LINE_LOOP、TRIANGLES、TRIANGLE_STRIP 与 TRIANGLE_FAN。

解决什么问题

它解决同一份顶点数据可以表达不同图形的问题。同样的 4 个点,用 POINTS 画成 4 个散点,用 LINE_LOOP 画成闭合线框,用 TRIANGLE_STRIP 则填充成矩形,无需改变顶点内容。

为什么这样做

GPU 的图元装配阶段是固定硬件,按模式顺序取顶点。把模式作为参数暴露出来,可以用极少的控制信息表达点、线、面等多种图元,避免为每种图形各写一套渲染路径。

怎么用

JavaScript
gl.drawArrays(gl.POINTS, 0, 4);          // 4 个独立点
gl.drawArrays(gl.LINES, 0, 4);           // 2 条独立线段
gl.drawArrays(gl.LINE_LOOP, 0, 4);       // 4 点首尾相连
gl.drawArrays(gl.TRIANGLES, 0, 6);       // 2 个独立三角形
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);  // 4 点组成条带(2 个三角形)
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);    // 扇形,常用于圆与多边形
模式顶点消耗典型用途
POINTS1 点 / 图元粒子、散点图
LINES2 点 / 线段辅助线、线框
LINE_STRIPn+1 点 / n 段折线、路径
TRIANGLES3 点 / 三角形通用网格
TRIANGLE_STRIPn+2 点 / n 个三角形条带、地形
TRIANGLE_FANn+2 点 / n 个三角形圆面、扇形

常见坑

  • 顶点数不是模式所需整数倍 → 原因:多余顶点被忽略或报错 → 处理:按模式对齐顶点数量
  • 用 TRIANGLE_STRIP 却期待独立三角形 → 原因:条带会共享边 → 处理:插入退化三角形或改用 TRIANGLES
  • 点太小看不见 → 原因:未设置 gl_PointSize → 处理:在顶点着色器中赋值 gl_PointSize
  • 线宽设置无效 → 原因:多数平台只支持 1 像素线宽 → 处理:用三角形模拟粗线

参数化图形顶点生成

是什么

参数化图形顶点生成,是用数学公式按参数(半径、分段数、行列数、宽高)在 CPU 侧计算出顶点位置、法线与 UV,再一次性写入缓冲的过程。

解决什么问题

它解决几何数据的来源问题。手工列出顶点只适用于极简图形,圆形、网格、地形这类规则几何必须用循环生成,参数化还让同一个函数通过调整参数产出不同精度的模型。

为什么这样做

规则几何具有严格的数学结构,用公式生成既准确又紧凑,还能与 LOD 策略配合:远处用低分段,近处用高分段,同一份代码服务不同性能档位,避免为每种精度维护一份模型文件。

怎么用

JavaScript
// 生成圆环顶点(TRIANGLE_STRIP):内外半径 + 分段数
function buildRing(innerR, outerR, segments) {
  const data = [];
  for (let i = 0; i <= segments; i++) {
    const a = (i / segments) * Math.PI * 2;
    const cos = Math.cos(a), sin = Math.sin(a);
    data.push(innerR * cos, innerR * sin, 0);
    data.push(outerR * cos, outerR * sin, 0);
  }
  return new Float32Array(data); // 共 (segments+1)*2 个顶点
}

// 生成网格顶点:行列数 + 间距
function buildGrid(cols, rows, step) {
  const data = [];
  for (let r = 0; r <= rows; r++) {
    for (let c = 0; c <= cols; c++) {
      data.push((c - cols / 2) * step, 0, (r - rows / 2) * step);
    }
  }
  return new Float32Array(data);
}

常见坑

  • 圆形段数过少导致边缘明显 → 原因:分段数不足 → 处理:按半径动态决定段数,常用 32 起
  • 生成的三角形朝向不一致 → 原因:缠绕顺序混乱 → 处理:统一按逆时针生成并配合面剔除
  • 每帧重新生成并上传 → 原因:形状固定却重复计算 → 处理:生成一次后缓存缓冲对象
  • 未计算法线与 UV → 原因:只关注位置 → 处理:生成时同步输出法线与纹理坐标

要点总结

  • 绘制类型决定组装方式,TRIANGLE_STRIP 与 FAN 能显著减少顶点数
  • 顶点数量必须与所选模式对齐,多余顶点会被忽略
  • 规则几何用循环按参数生成,分段数可按距离动态调整
  • 形状固定的几何生成一次即可,只在参数变化时重建
  • 生成时同步输出法线与 UV,避免后续补数据

常见问题

TRIANGLES 和 TRIANGLE_STRIP 有什么区别?

TRIANGLES 每三个顶点组成一个独立三角形,顶点不共享;TRIANGLE_STRIP 从第 3 个顶点起每个新顶点与前两个组成三角形,画 n 个三角形只需 n+2 个顶点,适合连续条带。

画一个矩形需要几个顶点?

用 TRIANGLES 需要 6 个顶点(两个三角形各 3 个);用 TRIANGLE_STRIP 只需 4 个;用索引缓冲则存 4 个顶点加 6 个索引,数据量最小且便于复用。

参数化生成圆形要多少个顶点?

由分段数决定。常见做法是取 32 到 64 段,段数越多边缘越平滑,计算方式是用三角函数按角度等分求出每个点的 x 与 y。

生成的顶点每帧都要重新上传吗?

不需要。形状固定的图形只生成并上传一次,之后每帧只绑定缓冲并绘制;只有形状随参数变化时才需要重新生成。

想在手机上练这篇的配套题目?
使用微信 卷王开发者小程序/App,首页顶部扫一扫识别下方二维码,即可解锁本讲对应真题与错题本。
了解产品 App
← 上一篇 WebGL 纹理加载时序与格式选型 下一篇 → WebGL 索引组网与面剔除