Notan: 跨平台多媒体应用开发-简单、强大、无限可能
前言
在这里,我们不生产代码,我们只是代码的搬运工。如果你厌倦了和那些固执的平台特异性代码较劲,那么 Notan 就是你的瑞士军刀——小巧、多功能,而且绝对不会让你在紧急情况下用来开瓶盖。
便携式多媒体层
Notan 是一个简单且便携的层,旨在让您在其上构建自己的多媒体应用,而无需过多担心平台特定的代码。
主要目标是提供一组 API 和工具,可以以符合人体工程学的方式用于创建您的项目,而不强制任何结构或模式,始终尽量不干扰您的工作。
示例
在线演示[1]。
你想打开一个窗口吗?
窗口打开[2]
use notan::prelude::*;
#[notan_main]
fn main() -> Result<(), String> {
notan::init().build()
}
你想画一个三角形吗?
绘制三角形[3]
use notan::prelude::*;
use notan::draw::*;
#[notan_main]
fn main() -> Result<(), String> {
notan::init().draw(draw)
.add_config(DrawConfig)
.build()
}
fn draw(gfx: &mut Graphics) {
let mut draw = gfx.create_draw();
draw.clear(Color::BLACK);
draw.triangle((400.0, 100.0), (100.0, 500.0), (700.0, 500.0));
gfx.render(&draw);
}
直接渲染三角形怎么样?
渲染器三角形[4]
use notan::prelude::*;
//language=glsl
const VERT: ShaderSource = notan::vertex_shader! {
r#"
#version 450
layout(location = 0) in vec2 a_pos;
layout(location = 1) in vec3 a_color;
layout(location = 0) out vec3 v_color;
void main() {
v_color = a_color;
gl_Position = vec4(a_pos - 0.5, 0.0, 1.0);
}
"#
};
//language=glsl
const FRAG: ShaderSource = notan::fragment_shader! {
r#"
#version 450
precision mediump float;
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 color;
void main() {
color = vec4(v_color, 1.0);
}
"#
};
#[derive(AppState)]
struct State {
clear_options: ClearOptions,
pipeline: Pipeline,
vbo: Buffer,
}
#[notan_main]
fn main() -> Result<(), String> {
notan::init_with(setup).draw(draw).build()
}
fn setup(gfx: &mut Graphics) -> State {
let clear_options = ClearOptions::color(Color::new(0.1, 0.2, 0.3, 1.0));
let vertex_info = VertexInfo::new()
.attr(0, VertexFormat::Float32x2)
.attr(1, VertexFormat::Float32x3);
let pipeline = gfx
.create_pipeline()
.from(&VERT, &FRAG)
.with_vertex_info(&vertex_info)
.build()
.unwrap();
#[rustfmt::skip]
let vertices = [
0.5, 1.0, 1.0, 0.2, 0.3,
0.0, 0.0, 0.1, 1.0, 0.3,
1.0, 0.0, 0.1, 0.2, 1.0,
];
let vbo = gfx
.create_vertex_buffer()
.with_info(&vertex_info)
.with_data(&vertices)
.build()
.unwrap();
State {
clear_options,
pipeline,
vbo,
}
}
fn draw(gfx: &mut Graphics, state: &mut State) {
let mut renderer = gfx.create_renderer();
renderer.begin(Some(state.clear_options));
renderer.set_pipeline(&state.pipeline);
renderer.bind_buffer(&state.vbo);
renderer.draw(0, 3);
renderer.end();
gfx.render(&renderer);
}
寻找更多示例?
当然!查看 examples folder[5]。在那里你会发现一些示例,比如渲染、窗口、输入等...
安装
从 crates.io[6] 添加 notan
到您的项目。main
分支应该是 crates.io
上的最新版本。
WebAssembly
我们将网络视为一等公民。WebAssembly 编译非常简单,我们建议使用 trunk[7]。您只需要在项目中添加一个 index.html
文件,然后运行 trunk serve
即可看到它在工作。
这里有一个简单的 index.html
文件示例:
<html>
<head>
<title>Notan 应用</title>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="minimal-ui, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<style>
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background-color: #252526;
}
* {
outline: none;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<canvas id="notan_canvas"></canvas>
</body>
</html>
然而,您也可以使用 wasm-bindgen[8] 或 wasm-pack[9]。
工作原理
非常简单,Notan 定义了一组 API(如窗口、图形、输入、音频等)作为“核心”。在“核心”之下是 backends
,这些是添加对任何平台的支持的 crates,管理平台特定的代码并将其翻译为我们的“核心” API。任何人都应该能够轻松创建和使用自定义后端。在“核心”之上,我们可以构建更符合人体工程学的 API,这些 API 可以使用 Notan 为任何后端构建。
最终用户只需要担心在 Notan 之上构建他们的应用,无论编译目标是什么,都应该没问题。当然,如果您想针对不同的目标,编写可移植的代码有时可能会有些棘手,但 Notan 应该能够帮助您解决大部分问题。
支持的平台
Web 浏览器( wasm32
)- WebGL2窗口 - OpenGL 3.3 MacOS - OpenGL 3.3 Linux - OpenGL 3.3
这些平台目前的图形后端使用的是 glow.rs[10],它允许我们轻松地针对 WebGl2、GL 和 GL ES。
性能
人们喜欢看到性能数字和基准测试(我也喜欢),但事实是,任何基准测试或数字在没有计算这些数字的完整上下文的情况下都没有价值。
我们还没有检查(_尚未_)代码的哪些部分可以更改以提高性能。保持良好的 API、少量样板代码和高性能之间的平衡并不容易。然而,这是我们自项目诞生以来就试图通过这个项目实现的目标。
Notan 试图为用户提供一个简单的构建事物的方式,性能将取决于很多因素,包括用户代码。
让我们看一个简单的例子,2D 绘图 API 建立在图形 API 之上,它有很大的改进空间,但在我机器上运行示例 draw_bunnymark[11] 时,我得到了一些不错的数字。
在 MacBook(2.3Hz i9 - 16GB RAM)上:
本地:85000 只兔子,60FPS Chrome:78000 只兔子,60FPS
在高端桌面(Archlinux)上:
本地:205000 只兔子,60FPS Chrome:131000 只兔子,60FPS
让我们记住 bunnymark
的条件在真实项目中很少见。然而,它被广泛用于测试 2D 绘图 API 的性能。
集成
Notan 旨在尽可能模块化。它足够灵活,允许通过插件(例如:FpsLimit[12])更改事件生命周期的工作方式,或者允许我们使用 _图形扩展_(例如:egui[13] 或 draw[14])在图形 API 之上轻松绘制自定义内容。
甚至任何后端都可以从代码中轻松 _插入_,只需使用 init_with_backend
。
我们包括一些这些插件或图形扩展作为项目的一部分,通过特性标志提供。然而,每个人都可以创建自己的插件或扩展来扩展 Notan。
为什么?
作者一直在寻找一个项目,使我使用单一代码库创建多媒体应用(我的情况下是游戏),不太强迫如何做到这一点,支持多个平台,并将网络视为一等公民。
我发现直到我找到 Haxe[15] 和 Kha[16],完美的匹配。然而,我不喜欢构建系统、缺乏工具和 IDE,以及语言本身做事的方式。所以,过了一会儿,我决定再次寻找,我看到 Rust 有一个很棒的 WebAssembly 编译器以及其他目标,并且检查了所有这些框。
在过去的三年里,我一直在不同的仓库中以不同的名称和多次“重新开始”的时间工作在这个项目上。这是我学习 Rust 和 OpenGL 的地方,你可以说我的沙箱和我的爱好。
然而,我觉得它现在的状态可能对不止我一个人有用。
Notan 的名字来自 not an engine
。项目的主要目的是作为一个基础,提供基本但有用的功能集。
它们是:
平台抽象(桌面、移动等...) 窗口 图形渲染 文本渲染 绘制 2D API 音频 输入(键盘、鼠标等...) 通过 egui 的简单 UI
其他一切,如粒子、物理等...,不在项目的范围之内 可以通过外部 crates、插件或扩展添加。尽管我们总是可以谈论 如果您认为某些内容应该是项目的一部分。
那么...接下来呢?
逐步改进项目,目标是更成熟的和更好的 API,修复,更好的平台支持,更多的图形后端等...
github 地址
https://github.com/Nazariglez/notan/tree/develop
在线演示: https://nazariglez.github.io/notan-web/
[2]窗口打开: https://nazariglez.github.io/notan-web/examples/window_open.html
[3]绘制三角形: https://nazariglez.github.io/notan-web/examples/draw_triangle.html
[4]渲染器三角形: https://nazariglez.github.io/notan-web/examples/renderer_triangle.html
[5]examples folder: examples
[6]crates.io: https://crates.io
[7]trunk: https://trunkrs.dev/
[8]wasm-bindgen: https://github.com/rustwasm/wasm-bindgen
[9]wasm-pack: https://rustwasm.github.io/wasm-pack/
[10]glow.rs: https://github.com/grovesNL/glow
[11]draw_bunnymark: examples/draw_bunnymark.rs
[12]FpsLimit: crates/notan_extra/src/fps_limit.rs
[13]egui: crates/notan_egui
[14]draw: crates/notan_draw
[15]Haxe: https://haxe.org/
[16]Kha: https://kha.tech/