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过程宏

Download history 125/week @ 2024-04-20 15/week @ 2024-04-27 1/week @ 2024-05-04 5/week @ 2024-05-18 1/week @ 2024-05-25 4/week @ 2024-06-01 162/week @ 2024-06-08 5/week @ 2024-06-15 11/week @ 2024-06-29 77/week @ 2024-07-13 38/week @ 2024-07-20 10/week @ 2024-07-27

每月136次下载
用于 dunge

MIT 协议

33KB
568

Dunge

基于 WGPU 的简单且可移植的3D渲染。

特性

  • 简单且灵活的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