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次下载
1.5MB
2K SLoC
概述
一个库,允许您使用Vertex buffer objects以安全API快速绘制各种简单的2D几何原语和精灵。使用构建器模式提供方便的API。主要设计目标是能够高效地绘制数千个形状。使用glutin和opengl es 3.0。
屏幕截图

示例
查看github示例,或查看crate文档。
演示
演示精灵由以下提供
- Henry Software - https://henrysoftware.itch.io/pixel-food
- rvros - https://rvros.itch.io/animated-pixel-hero
- savvycow - https://savvycow.itch.io/loudypixelsky
lib.rs
:
概述
一个库,允许您使用vertex buffer objects以安全API快速绘制各种简单的2D几何原语和精灵。使用构建器模式提供方便的API。主要设计目标是能够高效地绘制数千个形状。使用glutin和opengl es 3.0。
管道
egaku2d绘图管道的工作方式如下
-
- 选择一个绘图类型(特定的形状或精灵)并设置特定形状或精灵的必需值。
-
- 通过调用
add()
构建大量顶点。
- 2.1 可选地将顶点保存到GPU上的静态vbo,以便稍后快速绘制,通过调用
save()
。
- 通过调用
-
- 通过调用
send_and_uniforms()
将顶点数据发送到GPU并设置必需的着色器统一变量。
- 3.1 设置可选的统一变量,例如
with_color()
。
- 通过调用
-
- 通过调用
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