11个版本
0.3.3 | 2024年7月19日 |
---|---|
0.3.2 | 2024年6月9日 |
0.3.1 | 2024年4月25日 |
0.3.0 | 2024年2月29日 |
0.2.2 | 2023年8月17日 |
#900 在 过程宏 中
每月136次下载
用于 dunge
33KB
568 行
特性
- 简单且灵活的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>
最终,它将启动一个本地服务器,你可以在浏览器中打开http://localhost:3000来查看应用程序正在运行。仅支持WebGPU后端,所以请确保你的浏览器支持它。
还可以在测试目录中查看创建单个图像的小示例。
依赖关系
~260–700KB
~17K SLoC