21个版本
0.3.3 | 2024年7月19日 |
---|---|
0.3.1 | 2024年4月25日 |
0.3.0 | 2024年2月29日 |
0.2.2 | 2023年8月17日 |
0.1.1 | 2022年10月22日 |
#56 in 图形API
每月269次下载
210KB
6.5K SLoC
特性
- 简单灵活的API
- 可自定义顶点、组和实例
- 将着色器代码描述为单个Rust函数
- 高度类型安全,运行时检查最小化
- 支持桌面、WASM和(稍后)Android
- 可选内置窗口和事件循环
应用领域
目前该库仅限个人使用。不过,随着时间的推移,我计划稳定API,以便某人可以用它来完成他们的任务。
入门指南
要开始使用该库,将其添加到您的项目中
cargo add dunge -F winit
如果需要创建窗口应用程序,请指定winit
功能。尽管这不是必需的,例如,您可以直接将场景绘制到RAM中的图像中。
那么,如果您想在屏幕上绘制一些东西呢?假设您想绘制一个简单的彩色三角形。那么首先创建一个顶点类型。为此,为您的结构体派生Vertex
特质
use dunge::prelude::*;
// Create a vertex type
#[repr(C)]
#[derive(Vertex)]
struct Vert {
pos: [f32; 2],
col: [f32; 3],
}
要在GPU上渲染某些内容,您需要编写一个着色器。在dunge中,您可以通过正常的(几乎)Rust函数来完成此操作
// Create a shader program
let triangle = |vert: sl::InVertex<Vert>| {
// Describe the vertex position:
// Take the vertex data as vec2 and expand it to vec4
let place = sl::vec4_concat(vert.pos, sl::vec2(0., 1.));
// Then describe the vertex color:
// First you need to pass the color from
// vertex shader stage to fragment shader stage
let fragment_col = sl::fragment(vert.col);
// Now create the final color by adding an alpha value
let color = sl::vec4_with(fragment_col, 1.);
// As a result, return a program that describes how to
// compute the vertex position and the fragment color
sl::Out { place, color }
};
从代码片段中可以看出,着色器要求您提供两个东西:顶点在屏幕上的位置以及每个片段/像素的颜色。结果是triangle
函数,但如果您在IDE中请求其类型,您可能会注意到它比通常更复杂
impl Fn(InVertex<Vert>) -> Out<Ret<Compose<Ret<ReadVertex, Vec2<f32>>, Ret<NewVec<(f32, f32), Vs>, Vec2<f32>>>, Vec4<f32>>, Ret<Compose<Ret<Fragment<Ret<ReadVertex, Vec3<f32>>>, Vec3<f32>>, f32>, Vec4<f32>>>
这是因为这个函数实际上并没有进行任何计算。它只用于描述在GPU上计算所需内容的步骤。在着色器实例化过程中,此函数用于编译实际的着色器。然而,这使我们免于编写wgsl着色器,并允许在编译时进行类型检查。例如,dunge会检查着色器中的顶点类型是否与渲染期间使用的网格相匹配。它还会检查着色器内部的类型。
现在我们来创建dunge上下文和其他必需的内容
// Create the dunge context
let cx = dunge::context().await?;
// You can use the context to manage dunge objects.
// Create a shader instance
let shader = cx.make_shader(triangle);
您可能会注意到上下文创建需要异步操作。这是WGPU特有的,所以您需要在项目中添加您喜欢的异步运行时。
同时创建一个我们将要绘制的三角形网格
// Create a mesh from vertices
let mesh = {
let data = const {
MeshData::from_verts(&[
Vert { pos: [-0.5, -0.5], col: [1., 0., 0.] },
Vert { pos: [ 0.5, -0.5], col: [0., 1., 0.] },
Vert { pos: [ 0. , 0.5], col: [0., 0., 1.] },
])
};
cx.make_mesh(&data)
};
现在要运行应用程序,我们需要最后两件事:处理程序。一个在每次渲染之前被调用的Update
处理程序,用于控制渲染对象和管理主事件循环
// Describe the `Update` handler
let upd = |ctrl: &Control| {
for key in ctrl.pressed_keys() {
// Exit by pressing escape key
if key.code == KeyCode::Escape {
return Then::Close;
}
}
// Otherwise continue running
Then::Run
};
在这里我们没有做任何特别的事情,我们只是检查是否按下了Esc键并在必要时结束主循环。请注意,此处理程序仅用于使用具有winit
功能的窗口。
第二个Draw
处理程序直接用于在最终帧中绘制内容
// Create a layer for drawing a mesh on it
let layer = cx.make_layer(&shader, view.format());
// Describe the `Draw` handler
let draw = move |mut frame: Frame| {
use dunge::color::Rgba;
// Create a black RGBA background
let bg = Rgba::from_bytes([0, 0, 0, !0]);
frame
// Select a layer to draw on it
.layer(&layer, bg)
// The shader has no bindings, so call empty bind
.bind_empty()
// And finally draw the mesh
.draw(&mesh);
};
注意:要创建一个图层,我们需要知道窗口的格式。猜测它是可能的,但最好直接从视图对象中获取它。您可以从特殊的
make
辅助函数中获取视图,该辅助函数在处理程序初始化时调用闭包,并将必要的数据传递给它。
现在您可以将两个步骤合并到一个处理程序中,运行应用程序并查看窗口
let make_handler = |cx: &Context, view: &View| {
let upd = |ctrl: &Control| {/***/};
let draw = move |mut frame: Frame| {/***/};
dunge::update(upd, draw)
};
// Run the window with handlers
dunge::window().run_local(cx, dunge::make(make_handler))?;
您可以从这里找到此示例的完整代码这里,并使用以下方法运行它
cargo run -p window
示例
有关使用窗口的更多示例,请参阅示例目录。要构建和运行示例,请
cargo run -p <example_name>
构建和运行wasm示例
cargo x build <example_name>
cargo x serve <example_name>
如果系统上已经安装了wasm-pack
,构建脚本将找到它并使用它来编译wasm工件。否则,将在本地安装wasm-pack
。要防止此行为,请添加no-install
标志
cargo x --no-install build <example_name>
最终,它将启动本地服务器,您可以在浏览器中打开https://127.0.0.1:3000来查看正在运行的应用程序。仅支持WebGPU后端,因此请确保您的浏览器支持它。
还可以查看测试目录中的创建单个图像的小示例。
依赖关系
~11–51MB
~836K SLoC