Webgl 基础以及canvasKit学习

发布时间 2023-10-17 08:49:44作者: 长安城下翩翩少年
一 基础概念 1. 关于canvas 、 webgl 、 skia 、canvasKit ctx.getContext('2d ' / 'webgl ' / 'webgl2'); 类型 2d 、webgl、webgl2 有什么不同? canvas2d 主要的性能问题就在于,绘制中间对象没法缓存,以及部分能力需要 CPU 计算这两点上。 而 canvaskit 在提供了类似 canvas 2d 的接口的同时,又让我们不用去操心 webGL 上的技术问题,可以说非常适合复杂的 2d 图形绘制场景了。canvasKit 还提供其他功能,比如布尔运算,文字排版(canvas 2d 中 fillText 只能绘制单行文本,不能直接换行,需要配合 计算文字长度的属性 measureText 来判断换行) 2. 关于skia 和 canvasKit 以及 wasm Skia 是一个开源 2D 图形库,它提供适用于各种硬件和软件平台的通用 API。 它作为 Google Chrome 和 ChromeOS、Android、Flutter 和许多其他产品的图形引擎。Skia 支持多语言调用, C++/C#/Java/Python/Rust/WASM 等。 程序化设计有浏览器端以及服务端的绘制需求,所以我们选择 canvaskit-wasm 这个 Skia 打包出来的供 JS 调用的 WebAssembly NPM 包,在 web 端以及 nodejs 端都能使用,这样就满足了多端的需求。 尽管使用了画布元素,CanvasKit 并没有调用 HTML 画布自己的绘制方法。它使用此画布元素获取 WebGL2 上下文并使用编译为 WebAssembly 的 C++ 代码执行大部分绘图工作,然后在每帧结束时向 GPU 发送命令 3. 其他概念 1. Surface 是什么? Surface本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原始缓冲区以及其他方面的内容,所以简单的说Surface是用来管理数据的(句柄)。 2. Canvas 中在style中设置 viewWidth,又在 width 中设置 realWidth 的意义何在? canvasKit api 1. CanvasKit.XYWHRect(x,y,width,height) 2. CanvasKit.Color(rgba) 3. CanvasKit.RRectXY(cc,rx,ry); // cc为矩形,rx,ry分别为圆角在x,y上的半径 async function init(){ // CanvasKitInit 加载 CanvasKit库的WebAssembly二进制文件,一旦CanvasKitInit函数成功地初始化了CanvasKit库,就可以使用它来创建2D和3D图形,并将它们呈现在HTML5画布元素上 // ck 是canvasKit 加载完成后实例化一个绘图对象 const ck = await CanvasKitInit({locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.19.0/bin/'+file}); // 创建一个与上面的 HTML canvas 元素关联的 Surface。但可以通过调用 MakeSWCanvasSurface 来覆盖。 MakeCanvasSurface 也是可以指定替代颜色空间或 gl 属性的地方。这个Surface会硬件加速 const surface = ck.MakeCanvasSurface('foo'); // 获取画布 const canvas = surface.getCanvas(); function draw(canvas) { canvas.clear(CanvasKit.WHITE); canvas.drawRRect(rr, paint); } const paint = new CanvasKit.Paint(); paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0)); paint.setStyle(ck.PaintStyle.Stroke); // ck.PaintStyle.Fill 表示填充 paint.setAntiAlias(true); surface.drawOnce(draw,paint); paint.delete() } 二 canvasKit 基础 api A. CanvasKitInit 用于加载 canvaskit-wasm 文件 用于浏览器端使用,返回一个promise对象,完成后会返回一个ck对象 - 用户创建画布,画笔,绘制图形api等 B. Ck 对象中的方法 1. 创建画布 const surface = ck.MakeOnScreenGLSurface;// (DT 那边的用法) const surface = ck.MakeCanvasSurface('canvas') ; // 获取canvas 属性用于绘制图形 const canvas = surface.getCanvas(); 2. 创建画笔 Paint - paint.setColor():设置画笔颜色(类似于 strokeStyle 和 fillStyle) - paint.setAlphaf():设置透明度 - paint.setAntiAlias():抗锯齿 - paint.setBlendMode():设置混合模式 - paint.setStyle():设置画笔样式 (ck.PaintStyle.Stroke 描边 ,ck.PaintStyle.Fill 填充 ) - paint.setStrokeWidth():设置描边宽度 - paint.setColorFilter():设置颜色筛选器 - paint.setImageFilter():设置图像筛选器 - paint.setMaskFilter():设置掩码筛选器 - paint.setShader():设置着色器 const paint = new ck.Paint(); // ck.Color4f 以(1,1,1,1.0) 方式创建颜色 // ck.Color 以rgba方式创建颜色 // paint.setColor 创建 stroke 或者fill 模式的颜色 paint.setColor(ck.Color4f(0.9, 0, 0, 1.0)); // 代表是stroke 或者 fill :使用描边模式还是填充模式 // ck.PaintStyle.Stroke 描边模式; ck.PaintStyle.Fill 填充模式 paint.setStyle(ck.PaintStyle.Stroke); // 设置画笔锯齿效果,不设置的话 描边会出现锯齿 paint.setAntiAlias(true); 3. 创建路径Path - path.moveTo(x, y):从(x,y)开始绘制一个路径 - path.lineTo():将直线添加到路径 - path.arcTo():将弧线添加到路径 - path.cubicTo():添加贝塞尔曲线 - path.quadTo():添加二次贝齐尔曲线 - path.close():闭合路径 - path.addRect():添加一个矩形到路径 - path.addCircle():添加圆 - path.addOval():添加椭圆 - path.addRoundedRect():添加圆角矩形 - path.addArc():添加圆弧 - path.addPath():添加另一个路径 const path = new ck.Path(); // 通过配置 paint.setStyle(CanvasKit.PaintStyle.Stroke 描边 ,CanvasKit.PaintStyle.Fill 填充) path.moveTo(10,10); path.lineTo(100,100); path.lineTo(50,50); path.close(); 4. Shader 着色器,用于绘制渐变、噪声、平铺等效果。 - shader.MakeColor():设置着色器颜色 - shader.MakeLinearGradient():线性渐变 - shader.MakeRadialGradient():径向渐变 - shader.MakeSweepGradient():扫描渐变 - shader.MakeTwoPointConicalGradient():两点圆锥渐变 - shader.MakeFractalNoise():柏林噪声 - shader.MakeTurbulence():平铺柏林噪声 - shader.MakeBlend():组合多个着色器效果 // 绘制渐变 const { Shader, parseColorString, TileMode } = CanvasKit const shader = Shader.MakeLinearGradient( [0, 0], // 渐变开始点 [50, 50], // 渐变结束点 [ parseColorString('#ff0000'), parseColorString('#ffff00'), parseColorString('#0000ff') ], // 渐变颜色 [0, 0.5, 1], // 颜色范围比例 TileMode.Clamp, // 范围外颜色样式模式 ) paint.setShader(shader) 5. 绘制图形 - canvas.drawRect():绘制一个矩形 - canvas.drawCircle():绘制一个圆 - canvas.drawOval() : 绘制一个椭圆 - canvas.drawLine():绘制一条直线 - canvas.drawPath():绘制一条路径 - canvas.drawArc():绘制一条圆弧 - canvas.drawText():绘制文字 - canvas.drawImage(image, x,y ): 绘制图片 绘制原尺寸的图片 - canvas.drawImageRect(image,someRect,someRect,paint); 绘制指定尺寸的图片 // 绘制矩形 const rect = ck.XYWHRect(x,y,width,height); canvas.drawRect(rect,paint); // 绘制圆角矩形 const rr = ck.RRectXY(rect,radiusX,radiusY); canvas.drawRRect(rr,paint); // 绘制椭圆 const ovalRect = ck.XYWHRect(x,y,width,height); // 椭圆的外层矩形 canvas.drawOval(ovalRect,paint); // 绘制图片 const image = await ajax(src); // 注意 返回的数据需要设置 arrayBuffer 格式 -> xhr.responseType = 'arraybuffer'; // 将arraybuffer转化为 canvaskit格式图片数据 const imageData = ck.MakeImageFromEncoded(image) ?? undefined; canvas.drawImageRect(imageData,ck.XYWHRect(0,0,imageData.width(),imageData.height()), ck.XYWHRect(x,y,width,height), null); // 绘制文本 // 需要加载文本数据 const robotoData = await fetch('https://storage.googleapis.com/skia-cdn/misc/Roboto-Regular.ttf').then((response) => response.arrayBuffer(), ); const fontMgr = ck.FontMgr.FromData([robotoData]); const paraStyle = new ck.ParagraphStyle({ textStyle: { color: ck.BLACK, fontFamilies: ['Roboto'], fontSize: 28, }, textAlign: ck.TextAlign.Left, }); const text = 'c \nanvasfadsfsadfasdfasdfkit\nfasdf asdfas /n dfsdafd fz中'; const builder = ck.ParagraphBuilder.Make(paraStyle, fontMgr); builder.addText(text); const paragraph = builder.build(); paragraph.layout(290); // width in pixels to use when wrapping text canvas.drawParagraph(paragraph, 10, 10); 基础概念 什么是skia? Skia 是 由c++开发的 一款性能优异的2d 图像渲染引擎。目前主要用户chrome 以及安卓的核心产品上,以及作为flutter渲染引擎,skia 能直接与GPU通信,开发者只需要理解Skia的图形概念即可开发图形界面,有了skia他们也不需要理解复杂的webgl指令。以其绝佳的性能以及跨平台的兼容性,写一套代码就能保证在安卓和ios上运行效果保持一致。 什么是 webassembly ? Wasm 是一种 使用非 javasript代码,并使其在浏览器中运行的方法。这些代码可以c、c++、rust 等,他们会被编译进你的浏览器,在cpu上以类似原生的速度运行。这些代码的形式是二进制文件,你可以直接在javasript中把它们当作模版来使用。 什么是canvaskit? Canvaskit 是一个以wasm为编译目标的图形绘制接口,它是将skia的图形绘制api导出到web平台来使用。canvaskit同样适用canvasHTML元素作为画布,但是没有使用画布的原生绘图方法,而是通过画布获取webgl2 上下文并利用 skia 绘图能力 进行绘图,并直接和GPU通信,使得性能大大提升。 Canvaskit 和 canvas的区别? Canvas 是 HTML提供元素,其绘制的中间对象没办法缓存以及绘图部分能力需要借助cpu进行计算,其性能在某些场景下并不尽人意。 Canvaskit 利用skia 通过wasm编译而成,其底层c++代码直接在cpu上运行相当于原生代码的速度,并且skia有提供数据缓存区以及直接同GPU对话,并且提供了布尔运算,文字换行等功能。非常适合比较复杂的2d图形绘制。 参考文档 1. WebGL API文档 2. Canvaskit 官方文档 3. Webassembly 文档 4. 使用 Skia 绘制 2D 图形 5. SkiaSharp Graphics in Xamarin.Forms 6. canvasKit类型文件&API解释(一)