当前位置: 首页 > news >正文

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

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

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

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

完整可运行代码

<!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),如需粗线条要用三角带模拟线段。
http://www.jsqmd.com/news/1163185/

相关文章:

  • Godot Little Camera Preview路线图与未来功能展望
  • 如何使用lm-evaluation-harness评估Qwen3.5-397B-A17B-MoE-MXFP4:完整教程指南 [特殊字符]
  • 304/316材质安平不锈钢丝网核心性能、报价、交付周期对比指南
  • FP7125停产了?不必担心新物料FP7135替代方案:FP7125 与 FP7135 高压 LED 驱动芯片对比简析
  • 激光术后该敷普通面膜还是医用胶原修护敷料?从监管分类看创面护理的正确选择
  • 2026年7月最新嘉兴天梭官方售后热线及客户服务网点地址 - 天梭服务中心
  • PPTist终极指南:如何在5分钟内创建专业在线演示文稿
  • 银行全员营销怎么统计完成率?分行宣传任务追踪与自动化考核指南
  • Agent 控制新思路:BrainLoop 如何把模型推理变成可控的状态迁移
  • CTPersistance CRUD操作指南:创建、读取、更新与删除完整教程
  • 宝珀官方服务项目及价格查询|维修地址及客服电话权威信息公告(2026年7月最新) - 宝珀官方售后服务中心
  • GZ-50*25钢制柱式暖气片性能优势与工程应用
  • 南京擅长离婚财产分割的律师|高净值财产纠纷专业处理 - AZJ888
  • 三步构建本地AI自动交易系统:Qbot量化交易框架的完整实战指南
  • 线性注意力与全注意力交替:Qwen3.5-397B-A17B-MoE架构中的注意力机制设计
  • YOLO11目标检测算法中空标签图像(Empty Label Images)的处理策略,涵盖数据准备、损失函数设计、难负样本挖掘(Hard Negative Mining)及在安防监控、自动驾驶等场景
  • 告别画图烦恼:用代码生成专业图表的神器
  • 大模型Token深度解析:原理、计费逻辑与企业级成本优化实战
  • Audacity音频创作全流程:从零基础到专业作品的7天蜕变之旅
  • AI电话系统技术选型:语音识别精度、高并发调度与CRM集成评估
  • 终极Photoshop图层批量导出指南:高速提升工作效率的完整解决方案
  • Windows Hello VS 传统密码:身份认证安全层级全面对比分析
  • 2026年7月深圳热门的伺服回收厂商推荐,三菱PLC回收/欧姆龙光电开关回收/基恩士光电开关回收,伺服回收门店口碑推荐 - 品牌推荐师
  • 2026阜阳黄金回收白银回收铂金回收中检持证鉴定师铂金银饰高价回收门店联系方式推荐
  • Glue R包:R语言中最强大的字符串插值工具完全指南
  • 南京协议离婚财产分割律师 - AZJ888
  • 2026年7月最新深圳积家官方售后客服中心地址电话及服务网点分布 - 积家官方售后服务中心
  • 南京离婚公积金年金分割律师:以精细技术守护长效积累的公平归属 - AZJ888
  • CANN/ops-math Polar算子测试指南
  • Mermaid Live Editor:实时图表编辑的现代化架构演进