WebGL 百万线段高性能 Demo(批量绘制,无循环绘制)

WebGL 百万线段高性能 Demo(批量绘制,无循环绘制)

核心优化关键点(百万级必备)

  1. 单缓冲区存放所有线段顶点 ,一次上传 GPU,不频繁bufferData
  2. 使用gl.LINES,每 2 个顶点一条线段,100 万条 = 200 万个顶点
  3. 着色器极简逻辑,CPU 只生成一次数据,渲染仅一次drawArrays
  4. 关闭不必要状态,降低管线开销
  5. 浮点数组一次性分配内存,减少 GC 卡顿

完整可运行代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>WebGL 100万条线段批量渲染</title>
  <style>
    canvas { border: 1px solid #333; background: #000; }
  </style>
</head>
<body>
<canvas id="webglCanvas" width="1200" height="700"></canvas>
<script>
const canvas = document.getElementById('webglCanvas');
const gl = canvas.getContext('webgl', {
  antialias: false, // 关闭抗锯齿提升性能
  preserveDrawingBuffer: false
});
if (!gl) alert("浏览器不支持WebGL");

// ===================== 1. 着色器 =====================
const vertexShaderSource = `
attribute vec2 aPos;
void main() {
  gl_Position = vec4(aPos, 0.0, 1.0);
}
`;

const fragmentShaderSource = `
precision mediump float;
uniform vec3 uLineColor;
void main() {
  gl_FragColor = vec4(uLineColor, 1.0);
}
`;

// 编译着色器工具
function createShader(type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  return shader;
}
function createProgram(vs, fs) {
  const program = gl.createProgram();
  gl.attachShader(program, vs);
  gl.attachShader(program, fs);
  gl.linkProgram(program);
  gl.useProgram(program);
  return program;
}

const vs = createShader(gl.VERTEX_SHADER, vertexShaderSource);
const fs = createShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
const program = createProgram(vs, fs);

// 获取属性/Uniform位置
const aPosLoc = gl.getAttribLocation(program, 'aPos');
const uColorLoc = gl.getUniformLocation(program, 'uLineColor');

// ===================== 2. 生成100万条线段顶点数据 =====================
const LINE_COUNT = 1000000; // 100万条线
const VERTEX_COUNT = LINE_COUNT * 2; // 每条线2个顶点
const vertexData = new Float32Array(VERTEX_COUNT * 2); // 每个顶点 x,y

// 随机生成 [-1,1] 范围内线段(模拟随机分布线条)
let offset = 0;
for (let i = 0; i < LINE_COUNT; i++) {
  // 起点
  const x1 = Math.random() * 2 - 1;
  const y1 = Math.random() * 2 - 1;
  // 终点
  const x2 = Math.random() * 2 - 1;
  const y2 = Math.random() * 2 - 1;

  vertexData[offset++] = x1;
  vertexData[offset++] = y1;
  vertexData[offset++] = x2;
  vertexData[offset++] = y2;
}

// ===================== 3. 创建缓冲区,一次性上传GPU =====================
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
// STATIC_DRAW:数据几乎不修改,GPU优化显存
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW);

// 绑定顶点属性
gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosLoc);

// ===================== 4. 渲染配置 =====================
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);

// 设置线条白色
gl.uniform3f(uColorLoc, 1, 1, 1);

// 关键:一次调用绘制全部100万条线
console.time("drawAllLines");
gl.drawArrays(gl.LINES, 0, VERTEX_COUNT);
console.timeEnd("drawAllLines");
</script>
</body>
</html>

关键流程拆解(百万线渲染标准流程)

1. 上下文创建优化

关闭抗锯齿antialias:false,百万线段开启 AA 会严重掉帧。

2. 数据存储逻辑

  • 100 万条线段 = 2000000 个顶点
  • 每个顶点vec2(x,y) = 2 个浮点数
  • 总浮点数量:2000000 * 2 = 4000000
  • Float32Array一次性预分配内存,避免循环 push 造成频繁扩容卡顿

3. 缓冲区上传策略

只用一次gl.bufferData把全部顶点送入 GPU,绝对不要循环多次上传数据,这是大批量渲染最大性能瓶颈。

4. 绘制调用

复制代码
gl.drawArrays(gl.LINES, 0, VERTEX_COUNT);

gl.LINES规则:顶点 0&1 一条线、2&3 一条线...... 完美匹配我们的数据结构。 整批只执行一次绘制指令,CPU-GPU 通信开销极低。

性能说明

  1. 显存占用粗略计算 单个 Float32 占 4 字节:4000000 × 4 = 16MB,现代显卡毫无压力。
  2. 浏览器限制: 部分低端笔记本 / 集成显卡渲染百万线条会帧率下降,属于显卡光栅化性能限制,代码本身无性能浪费。
  3. 进阶提速方案(如果需要更高帧率)
  • 使用索引缓冲ELEMENT_ARRAY_BUFFER,重复顶点复用(适合网格线)
  • 分批次多 Buffer 分段渲染(动态更新部分线条时用)
  • 使用 WebGL2 + 实例化渲染instanced批量生成重复线条
  • 降低画布分辨率,缩小width/height减少像素填充

常见问题

  1. 页面加载卡住几秒:生成 400 万浮点数需要少量 CPU 计算,属于正常现象;可把生成数据逻辑放 Web Worker 避免阻塞页面。
  2. 线条看不见:WebGL 坐标范围[-1,1],代码已全部随机在该区间内;背景黑色、线条白色对比明显。
  3. 线条粗细统一:原生 WebGLgl.LINES不支持自定义线宽(多数浏览器忽略lineWidth),如需粗线条要用三角带模拟线段。
相关推荐
想做后端的前端4 天前
WebGL实现FPS游戏
vue.js·游戏·webgl
ZENERGY-众壹5 天前
延时 5 秒怎么调优:光伏 SCADA 实时画面与 WebGL 拓扑集成的性能复盘
性能优化·webgl·scada·光伏运维·api集成
用户83134859306986 天前
Vue+Three.js实现PCB电路板3D交互:元器件点击高亮、部件显隐、模型自动旋转
vue.js·webgl·three.js
牧艺7 天前
cos-design BubbleField:用 Canvas 做一个「会呼吸」的深海气泡场
前端·webgl·canvas
用户83134859306988 天前
Cesium 实现行政区内部遮罩
vue.js·webgl·cesium
用户831348593069811 天前
Cesium 实现行政区反向遮罩镂空效果(自定义暗色蒙层+矢量/影像底图切换)
vue.js·webgl·cesium
孪生前端11 天前
Three.js 做医疗数字孪生:从空场景到可漫游楼层(附最小骨架)
webgl
点量云实时渲染-小芹12 天前
UE/Unity/Webgl模型在信创服务器上实时渲染推流的可行性分析
unity·webgl·云渲染信创·国产实时渲染·ue信创云推流
孪生前端12 天前
如何用配置驱动一套 Three.js 数字孪生模板
webgl
用户831348593069816 天前
Vue+Threejs实现人物奔跑场景
vue.js·webgl·three.js