🧱 WebGL2 2D 渲染引擎技术文档
基于模块化设计 +
gl-matrix+ WebGL2 的高性能 2D 图形渲染系统支持:点、线段、矩形、圆形、多边形、圆弧 绘制,支持缩放、平移、自由绘图交互
📋 模块职责总览
| 模块名 | 核心职责 | 依赖模块 | 生命周期 |
|---|---|---|---|
CanvasCore(核心入口) |
1. 获取 WebGL 上下文 2. 管理画布清空(clear) 3. 驱动渲染循环(requestAnimationFrame) 4. 协调 View 与 Renderer 执行 | View, Renderer2D |
初始化 1 次 + 每帧执行 |
View(视口与矩阵) |
1. 设置视口尺寸 2. 管理视图矩阵(viewMatrix) 3. 管理正交投影矩阵(projectionMatrix) 4. 支持平移、缩放变换 | 无(依赖 canvas 尺寸) | 初始化 1 次 + resize 时更新 + 每帧复用 |
ShaderProgram(着色器管理) |
1. 编译顶点/片段着色器 2. 链接 WebGLProgram 3. 缓存 attribute/uniform 位置 4. 提供 ProgramInfo 接口 |
无(仅依赖 gl) | 初始化 1 次(每个着色器程序) |
GLBuffer(VBO/EBO 封装) |
1. 创建 VBO(顶点数据)或 EBO(索引数据) 2. 上传数据到 GPU 3. 绑定/解绑缓冲区 | 无(依赖 gl) | 每个 RenderGeometry 对应 1~2 个,初始化 1 次 |
VertexArray(VAO 封装) |
1. 创建和绑定 VAO 2. 配置顶点属性布局(attribute → VBO) 3. 自动启用/禁用 attribute | GLBuffer |
每个 RenderGeometry 对应 1 个,初始化 1 次 |
RenderGeometry(图形数据生成) |
1. 根据类型生成顶点数据(position) 2. 生成索引数据(indices) 3. 支持点、线、矩形、圆、多边形、圆弧等 | 无(纯数据生成类) | 每次绘制新图形时创建 1 个 |
Event(交互事件) |
1. 监听鼠标事件(点击、拖拽、滚轮) 2. 实现缩放(zoom)、平移(pan) 3. 在 isdrawing 模式下驱动 RenderGeometry 生成 |
View, RenderGeometry |
初始化 1 次 + 交互时动态触发 |
Renderer2D(2D 渲染执行) |
1. 激活着色器程序 2. 遍历 RenderGeometry 列表 3. 创建/复用 VAO + VBO 4. 设置 uniforms(矩阵、颜色) 5. 调用 drawElements 绘制 |
ShaderProgram, VertexArray, GLBuffer, RenderGeometry |
每帧执行 |
⏳ 三大执行阶段详解
1. 初始化阶段(仅一次)
js
// main.js 中执行顺序
const view = new View(); // → 初始化视口与投影矩阵
const shaderProgram = new ShaderProgram(gl); // → 编译着色器,生成 ProgramInfo
const renderer2D = new Renderer2D(gl, shaderProgram); // → 准备渲染器
const canvasCore = new CanvasCore('canvas'); // → 获取 gl 上下文
canvasCore.init(view, renderer2D); // → 启动渲染循环
const event = new Event(canvas, view, renderer2D, geometryList); // → 绑定交互
✅ 关键行为:
- WebGL 上下文建立
- 着色器编译与链接
- 默认视图矩阵初始化(正交投影)
- 事件监听器注册(鼠标、滚轮)
2. 渲染循环阶段(每帧执行)
js
// CanvasCore.renderLoop → renderFrame → Renderer2D.render
canvasCore.renderFrame = () => {
resizeCanvasToDisplaySize(canvas); // 自动适配高清屏
view.setViewport(canvas.width, canvas.height); // 更新视口
canvasCore.clear(); // 清空画布
renderer2D.render(view); // 执行绘制
};
✅ 每帧流程:
| 步骤 | 模块 | 说明 |
|---|---|---|
| 1 | CanvasCore |
检测 canvas 尺寸变化,自动调整 viewport |
| 2 | View |
若尺寸变化,重新计算 projectionMatrix |
| 3 | Renderer2D |
遍历所有 RenderGeometry,绘制每个图形 |
| 4 | VertexArray + GLBuffer |
复用已创建的 VAO/VBO,避免重复上传数据 |
| 5 | ShaderProgram |
设置 uniform:u_viewMatrix, u_projectionMatrix, u_color |
🔁 使用
requestAnimationFrame循环驱动,确保流畅动画
3. 交互阶段(事件驱动)
js
// Event 模块监听
canvas.addEventListener('mousedown', ...) // 开始绘图
canvas.addEventListener('mousemove', ...) // 动态生成图形预览
canvas.addEventListener('mouseup', ...) // 结束并提交图形
canvas.addEventListener('wheel', ...) // 缩放
canvas.addEventListener('contextmenu', ...) // 右键平移(模拟)
✅ 交互行为:
| 事件 | 行为 |
|---|---|
mousedown + drawingType set |
开启 isdrawing = true,记录起点 |
mousemove |
实时计算图形参数,临时生成 RenderGeometry 并加入列表 |
mouseup |
结束绘制,提交图形,isdrawing = false |
wheel |
以鼠标为中心缩放,更新 View.zoom() |
mousemove + 右键按下 |
触发 View.pan() 实现平移 |
💡 所有交互最终通过修改
View矩阵 或 向geometryList添加新RenderGeometry来影响下一帧渲染
🧩 关键模块代码说明与设计亮点
✅ View:使用 gl-matrix 管理变换
js
import { mat4 } from 'gl-matrix';
this.projectionMatrix = mat4.create();
this.viewMatrix = mat4.create();
// 正交投影:适合 2D 场景
mat4.ortho(this.projectionMatrix, -w, w, -h, h, -1, 1);
// 支持以某点为中心缩放(如鼠标位置)
mat4.translate(this.viewMatrix, this.viewMatrix, [centerX, centerY, 0]);
mat4.scale(this.viewMatrix, this.viewMatrix, [this.scale, this.scale, 1]);
mat4.translate(this.viewMatrix, this.viewMatrix, [-centerX, -centerY, 0]);
📌 优势: 精确控制缩放中心,避免"缩放偏离鼠标"问题
✅ RenderGeometry:支持多种图形生成
| 类型 | 数据生成方式 |
|---|---|
point |
[x, y] |
line |
[x0,y0, x1,y1] + indices [0,1] |
rect |
四个角点 + 闭合索引 [0,1,2,3,0] |
circle |
极坐标生成 n 个点,连接成 LINE_STRIP |
arc |
在 startAngle ~ endAngle 范围内采样 |
polygon |
用户传入 points 数组,自动生成索引 |
📌 设计亮点: 解耦"数据生成"与"渲染",便于扩展(如添加贝塞尔曲线)
✅ Renderer2D:VAO/VBO 缓存优化
js
this.buffers = new WeakMap(); // geometry → GLBuffer
this.vaos = new WeakMap(); // geometry → VertexArray
📌 性能优化:
- 首次绘制时创建 VAO/VBO
- 后续复用,避免每帧重新上传顶点数据
- 使用
WeakMap自动随RenderGeometry被回收
✅ Event:支持多种绘图模式切换
js
window.startRect = () => event.setDrawingMode('rect');
window.startLine = () => event.setDrawingMode('line');
📌 用户可通过按钮切换绘图模式,临时生成预览图形,提升交互体验
✅ CanvasCore:自动适配高清屏(Retina)
js
function resizeCanvasToDisplaySize(canvas) {
const dpr = window.devicePixelRatio || 1;
const displayWidth = canvas.clientWidth * dpr;
const displayHeight = canvas.clientHeight * dpr;
if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
canvas.width = displayWidth;
canvas.height = displayHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
}
return true;
}
📌 关键: 不依赖 CSS 尺寸,确保清晰渲染
📐 架构关系图(文字版)
scss
[Event] ←→ [View] (缩放/平移)
↓
[Renderer2D] ←→ [ShaderProgram] (着色器)
↓
[RenderGeometry] → [GLBuffer] → [VertexArray]
↓
[CanvasCore] → requestAnimationFrame → 渲染循环
↓
WebGL Context
所有模块通过
gl上下文连接,数据流清晰,职责分明
✅ 总结:系统优势
| 特性 | 实现方式 |
|---|---|
| 🚀 高性能 | VAO/VBO 缓存、避免重复上传 |
| 🖱️ 高交互性 | 支持绘图、缩放、平移、预览 |
| 🧱 模块化 | 各模块独立,易于维护与扩展 |
| 📐 精确变换 | gl-matrix 支持专业矩阵运算 |
| 🖥️ 响应式 | 自动适配 canvas 尺寸与 DPI |
| 🔌 可扩展 | 易添加新图形类型、新着色器、新交互 |
🛠️ 后续可扩展方向
- 图形编辑:点击选中图形并拖拽调整
- 图层系统:支持 zIndex、可见性控制
- 纹理支持:为矩形添加图片贴图
- 批量渲染 :使用
gl.drawArraysInstanced绘制大量相同图形 - 撤销/重做 :维护
geometryList历史栈 - 导出 SVG / JSON:序列化图形数据