17个版本

0.5.4 2020年12月15日
0.5.3 2020年11月21日
0.5.2 2020年1月25日
0.4.1 2020年1月19日
0.1.3 2020年1月10日

#741图形API

每月49次下载

MIT/Apache

1.5MB
2K SLoC

概述

一个库,允许您使用Vertex buffer objects以安全API快速绘制各种简单的2D几何原语和精灵。使用构建器模式提供方便的API。主要设计目标是能够高效地绘制数千个形状。使用glutin和opengl es 3.0。

屏幕截图

screenshot

示例

查看github示例,或查看crate文档。

演示

演示精灵由以下提供


lib.rs:

概述

一个库,允许您使用vertex buffer objects以安全API快速绘制各种简单的2D几何原语和精灵。使用构建器模式提供方便的API。主要设计目标是能够高效地绘制数千个形状。使用glutin和opengl es 3.0。

管道

egaku2d绘图管道的工作方式如下

    1. 选择一个绘图类型(特定的形状或精灵)并设置特定形状或精灵的必需值。
    1. 通过调用 add() 构建大量顶点。
    • 2.1 可选地将顶点保存到GPU上的静态vbo,以便稍后快速绘制,通过调用 save()
    1. 通过调用 send_and_uniforms() 将顶点数据发送到GPU并设置必需的着色器统一变量。
    • 3.1 设置可选的统一变量,例如 with_color()
    1. 通过调用 draw() 绘制顶点。

此外,还有一种方法可以将我们保存的顶点绘制到GPU上。为此,我们不是按照步骤1和2操作,而是使用保存的顶点,然后通过调用 uniforms() 设置统一变量,然后通过调用 draw() 来绘制。

使用此流程,用户可以高效地绘制成千上万的圆,例如,但有一个前提是它们都具有相同的半径和颜色/透明度值。此API不允许用户高效地绘制成千上万的圆,其中每个圆都具有不同的颜色或半径。这是一个设计决策,以便每个顶点尽可能轻量(只是一个x和y位置),使其在设置和发送到GPU时更有效。

主要设计目标

主要目标是创建一个非常高效的简单2D图形库。特别关注通过使用紧凑的顶点、点精灵以及允许用户自行将顶点数据保存到GPU上来减少CPU和GPU之间的流量。

提供安全的API也是一个目标。所有绘制函数都需要对画布的可变版本,确保它们按顺序执行。用户被防止使用原子计数器创建多个系统实例。该系统还不会实现Send,以便顶点缓冲区出界时的drop调用也能按顺序发生。如果用户自行调用OpenGL函数,那么可能会失去一些安全保证。然而,如果用户不这样做,此API应该是完全安全的。

编写快速的着色器程序是次要目标。这是一个2D绘图库,尽管大多数硬件都是为了处理3D而设计的。这意味着使用此库时GPU很可能没有得到充分利用。因此,决定没有理由创建一个不可旋转的精灵着色器来节省GPU时间,例如。特别是由于顶点布局大小相同(32位对齐)([f32;2],i16,i16[f32;2],i16),因此发送更少的数据到GPU没有带来收益。

使用形状

用户可以绘制以下内容

形状 表示 OpenGL原语类型
(,半径) POINTS
对齐矩形 (startx,endx,starty,endy) TRIANGLES
对齐正方形 (,半径) POINTS
线条 (,,厚度) TRIANGLES
箭头 (point_start,point_end,厚度) TRIANGLES

使用精灵

此crate还允许用户绘制精灵。您可以上传一个瓦片集纹理到GPU,然后使用与形状绘制API相似的API绘制成千上万的精灵。精灵是使用OpenGL POINTS原语绘制的点精灵,以减少需要发送到GPU的数据。

每个精灵顶点由以下组成

  • 位置:[f32;2]
  • 索引:u16 - 用户可以在一个瓦片中索引多达256x256个不同的精灵。
  • 旋转:u16 - 这在内部被归一化为浮点数。用户传递一个以弧度为单位f32浮点数。

因此,每个精灵顶点紧凑地占12字节(4*3=12字节)。

每个纹理对象都有从x和y坐标创建此索引的函数。在GPU上,索引将被分成x和y坐标。如果索引大于纹理维度x乘以纹理维度y,则它将被取模,以便可以映射到瓷砖集。因此,索引不可能有'无效'值。但显然,用户从一开始就应该选择映射到瓷砖集中有效瓷砖的索引。

旋转在GPU上被归一化到浮点数。由于瓷砖索引大小为u16,这意味着您可以有一个最大为256x256瓷砖的纹理。用户只需通过API传递一个f32即可。旋转以弧度为单位,0表示无旋转,顺时针旋转时增加。

批处理绘图

虽然您可以通过多次调用add()来高效地绘制成千上万的对象,但您可能已经将所有顶点数据嵌入到某个地方,在这种情况下,通过遍历数据结构来构建另一个随后发送到GPU的列表可能看起来很浪费。egaku2d有Batches,允许您将顶点映射到您可能已有的现有数据结构中。这使得我们可以通过将整个数据结构发送到GPU来跳过构建新的顶点列表。

这种方法的缺点是,您可能有一个顶点数据列表,但由于每个元素都与许多其他数据相关联,它可能不是紧密打包的,在这种情况下,我们可能最终会向GPU发送大量无用的数据。

目前这仅支持圆形绘制。

视图

左上角是原点(0,0),x和y分别向右和向下增长。

在窗口模式下,窗口的尺寸默认设置为正好缩放到世界。例如,如果用户创建了一个800x600大小的窗口,并在400,300处绘制一个圆圈,则圆圈将出现在窗口中心。同样,如果用户有一个800,600分辨率的显示器,并以全屏模式开始,并在400,300处绘制一个圆圈,它也将出现在屏幕中心。

缩放x和y的比例固定为1:1,以便在形状中无扭曲。用户可以通过x或y手动设置缩放,另一个轴自动推断,以保持1:1的比例。

全屏

全屏被放在功能门后面,因为在某些平台(如wayland Linux)上它不起作用。我怀疑这是glutin的问题,所以我暂时禁用了它,希望glutin离开alpha后它将工作。我认为问题是,当窗口大小改变时,我无法使用resize()手动更改上下文的大小以匹配。

示例

use axgeom::*;
let events_loop = glutin::event_loop::EventLoop::new();
let mut glsys = egaku2d::WindowedSystem::new([600, 480], &events_loop,"test window");

//Make a tileset texture from a png that has 64 different tiles.
let food_texture = glsys.texture("food.png",[8,8]).unwrap();

let canvas = glsys.canvas_mut();

//Make the background dark gray.
canvas.clear_color([0.2,0.2,0.2]);

//Push some squares to a static vertex buffer object on the gpu.
let rect_save = canvas.squares()
  .add([40., 40.])
  .add([40., 40.])
  .save(canvas);

//Draw the squares we saved.
rect_save.uniforms(canvas,5.0).with_color([0.0, 1.0, 0.1, 0.5]).draw();

//Draw some arrows.
canvas.arrows(5.0)
  .add([40., 40.], [40., 200.])
  .add([40., 40.], [200., 40.])
  .send_and_uniforms(canvas).draw();

//Draw some circles.
canvas.circles()
  .add([5.,6.])
  .add([7.,8.])
  .add([9.,5.])
  .send_and_uniforms(canvas,4.0).with_color([0., 1., 1., 0.1]).draw();

//Draw some circles from f32 primitives.
canvas.circles()
  .add([5.,6.])
  .add([7.,8.])
  .add([9.,5.])
  .send_and_uniforms(canvas,4.0).with_color([0., 1., 1., 0.1]).draw();

//Draw the first tile in the top left corder of the texture.
canvas.sprites().add([100.,100.],food_texture.coord_to_index([0,0]),3.14).send_and_uniforms(canvas,&food_texture,4.0).draw();

//Swap buffers on the opengl context.
glsys.swap_buffers();

依赖关系

~16MB
~140K SLoC