实现在小游戏 WebGL2 上渲染 SVG?svg-webgl-loader 正式开源!

628次阅读  |  发布于3年以前

缘起

大家平时可能接触过 SVG,但是可能对 WebGL 不是很了解,SVG 主要是一种矢量图格式,给定对应的数据就可以绘制点、线和图形等,基于 XML 标记语言编写,可以在很小的体积下就能得到高清晰度图形,且支持任意形式的放大缩小而不失真;而 WebGL 是 HTML5 之后浏览器支持的一套图形的 API。

而 SVG 要么通过 DOM 支持,要么通过 Canvas Path2D 支持,在只有 WebGL2 的 Context 下却因为缺少相应的 API,所以无法支持 SVG,但团队成员在平时研究 3D / Threejs 源码时,发现源码中有类似将 SVG 渲染到 WebGL2 Context 上的逻辑,很感兴趣,所以就花时间抽出一个库,提供方便的 API 实现 SVG 在 WebGL2 Context 上的渲染:「svg-webgl-loader」,目前项目已经开源,感兴趣的同学可以去到如下地址查看代码

Github:https://github.com/elab-opensource/svg-webgl-loader[1]

NPM:svg-webgl-loader[2]

如何使用?

安装

我们提供了 NPM 包安装和 CDN 导入两种使用方式,其中 NPM 包安装如下:

npm i svg-webgl-loader # yarn add svg-webgl-loader

CDN 链接如下:

<script src="https://unpkg.com/svg-webgl-loader/dist/js/index.umd.js"></script>

导入使用

如果是通过 NPM 包安装,那么在你项目中正常导入使用即可:

import svgWebglLoader from "svg-webgl-loader";

如果你是通过 CDN 的形式引入,那么则可以以如下形式使用:

<script src="https://unpkg.com/svg-webgl-loader/dist/js/index.umd.js"></script>

const svgWebglLoader = window.svgWebglLoader

使用示例

一个标准的使用方式则为传入对应 SVG 内容以及待渲染的 Canvas 节点,以及配置对应的参数:

import svgWebglLoader from "svg-webgl-loader";
import svgUrl from "./img/test.svg";

// 加载解析svg数据
const svgData = await svgWebglLoader(svgUrl);

// 绘制
svgData.draw({
  canvas,
  loc: {
    x: 0,
    y: 0,
    width: 300,
    height: 300,
  },
});

一个实际的例子可以参考 CodeOpen 链接:

https://codepen.io/yh418807968/pen/GREMPXw?editors=1011[3]

如果需要,可以通过以下形式修改背景颜色:

let gl = canvas.getContext('webgl');
gl.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);

API 相关

draw

使用:

const svgData = await svgWebglLoader(svgUrl);
svgData.draw(drawParams)

输入参数:

interface drawParams {
  canvas: HTMLCanvasElement; // 待绘制的画布canvas
  loc?: {
    // 绘制区域
    x: 0,
    y: 0,
    width?: 300, // 绘制宽度,默认为svg图片本身宽度
    height?: 300, // 绘制宽度,默认为svg图片本身宽度
  };
  config?: {
    needTrim?: true, // 是否需要去除svg边缘空白,默认false
    needFill?: true, // 是否需要填充,默认true
    needStroke?: true, // 是否需要描边, 默认true
  };
}

调用之后产出的结果为渲染了 SVG 内容的 Canvas 对象,如果传入了对应的 canvas 参数,则为此传入的 Canvas 对象,否则会是在内部新建的 Canvas 对象。

原理浅析

svg-webgl-loader 主要是参考 Threejs 中加载渲染 SVG 的方案[4]:通过解析路径、路径离散化、三角化,将 SVG 分解为众多三角形,从而使得其可以采用 WebGL Shader 渲染。

以如下 SVG 为例:

<svg width="400" height="400" viewPort="0 0 400 400" version="1.1"    

xmlns="http://www.w3.org/2000/svg">   

<circle

    style="stroke:yellow; fill:blue; stroke-width: 3;"

    cx="61"

    cy="48"

    r="30" />

<polygon transform="translate(0, 60)" points="60,20 100,40 100,80 60,100 20,80 20,40" style="stroke:yellow; fill:blue; stroke-width: 3;"/>    

</svg>

其中包含一个圆形和一个多边形,解析流程主要如下:

大致流程参考下图:

❝注意:图中三角化、离散化等数据不是准确数据,只是解释说明大致流程。

包分析

大小

包大小约 90 KB,主要分为 2 部分:axios 和 主体逻辑。

渲染耗时

挑选了一个简单几何形状和一个复杂图形,分别看看耗时情况:

路径复杂度 表现 解析/渲染耗时
简单形状 一个rect t 18ms
复杂图形 约240条path 150ms

可以看到解析/渲染时间大致在 20~150ms 间,视觉上基本没有延时或卡顿。不过,由于 GPU 的并行绘制方式,绘制时间其实很短,以上时间几乎都是数据解析时间(即在主线程上进行),因此在耗时上的后续优化方向主要是将一些循环计算等转移到 shader 中,由 GPU 统一并行处理。

耗时计算方式:

展望及未来规划

svg-webgl-loader 目前可以渲染绝大部分 SVG,你可以通过体验它了解 SVG 和 WebGL 的一部分原理,同时有助于你日后在使用它们或了解 Threejs 相关的原理。

svg-webgl-loader 的出生是因为兴趣使然,所以在功能上依然还有很多可扩展及优化的地方,具体列举如下。

可拓展功能

  1. 支持渲染合批
  2. 支持嵌入其他webgl环境
  3. 支持自定义曲线离散化功能
  4. 曲线自适应离散化方案
  5. 支持节点引用和复用
  6. 支持自定义图形大小和缩放比例
  7. 支持SVG动画
  8. 支持SVG IMG标签

现有功能优化

  1. 分离曲线表达、离散化、三角化功能
  2. 重构Parse方法,支持栈解析SVG文本
  3. 增加GPU计算坐标、颜色等信息
  4. 包大小及耗时优化

总结

svg-webgl-loader 中涉及到了大量 SVG 、Canvas 与 WebGL 相关的知识,探究它们的原理是困难而有趣的,如果你也有兴趣一起探索关于它们的奥妙,也可以联系小编拍砖哦~

同时,如果你对 svg-webgl-loader 感兴趣的话,别忘了去到对应的地址给我一个 Star 以鼓励我们改进的更好哦~

Reference

[1]https://github.com/elab-opensource/svg-webgl-loader:https://github.com/elab-opensource/svg-webgl-loader

[2]svg-webgl-loader:https://www.npmjs.com/package/svg-webgl-loader

[3]https://codepen.io/yh418807968/pen/GREMPXw?editors=1011:https://codepen.io/yh418807968/pen/GREMPXw?editors=1011

[4]方案:https://github.com/mrdoob/three.js/blob/dev/examples/webgl_loader_svg.html

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8