█▓▒░⡷⠂μ GL⠐⢾░▒▓█
Micro WebGL2 / WebGPU Graphics Library for Rust
## Overview
`mugl` is a minimal, modern WebGL 2.0 / WebGPU 3D graphics abstraction layer. It provides a simplified WebGPU-style API that runs on the web using WebGL 2.0, and other platforms using native WebGPU.
## Install
```toml
[dependencies]
mugl = "0.1"
```
Features:
- `backend-webgl` - enables WebGL 2.0 backend for WASM. Requires [`mugl/wasm`](https://github.com/andykswong/mugl) npm package for glue code. (see [usage](#hello-world))
- `backend-wgpu` - enables WebGPU backend based on `wgpu`
- `std` - enables `std` support
- `wasm-bindgen` enables `wasm-bindgen` integration
- `serde` - enables `serde` serialize/deserialize implementations
## [Documentation](https://docs.rs/mugl)
See Docs.rs: https://docs.rs/mugl
## Usage
### [Examples](./examples)
Several examples can be found in this repository. Use npm to run the below examples on web: ```npm install && npm start```
| Screenshot | Source | Run Script |
|------------|--------|------------|
|![basic](./screenshots/basic.png)|[basic](./examples/app/basic.rs)|```cargo run --features backend-wgpu --example basic```|
|![instancing](./screenshots/instancing.png)|[instancing](./examples/app/instancing.rs)|```cargo run --features backend-wgpu --example instancing```|
|![stencil](./screenshots/stencil.png)|[stencil](./examples/app/stencil.rs)|```cargo run --features backend-wgpu --example stencil```|
### Hello World
Below is the minimal WASM app to draw the triangle in the basic example using the WebGL backend (See full example code [here](./examples/app/basic.rs)):
```rust
use mugl::{prelude::*, webgl::*};
// (Optional) Define a unique app ID to use in JS glue code. Required only when multiple WASM modules use mugl.
#[no_mangle]
pub extern "C" fn app_id() -> ContextId { ContextId::set(123); ContextId::get() }
#[no_mangle]
pub extern "C" fn render() {
app_id(); // Make sure we call ContextId::set() before any API call.
// 1. Create device from canvas of id "canvas"
let canvas = Canvas::from_id("canvas");
let device = WebGL::request_device(&canvas, WebGLContextAttribute::default(), WebGL2Features::empty())
.expect("WebGL 2.0 is unsupported");
// 2. Create buffer
let vertices: &[f32] = &[
// position color
0.0, 0.5, 0.0, 1.0, 0.0, 0.0, 1.0,
0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0,
-0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 1.0
];
let vertices: &[u8] = bytemuck::cast_slice(vertices);
let buffer = device.create_buffer(BufferDescriptor { usage: BufferUsage::VERTEX, size: 3 });
device.write_buffer(&buffer, 0, vertices);
// 3. Create shaders
let vertex = &device.create_shader(ShaderDescriptor {
usage: ShaderStage::VERTEX,
code: "#version 300 es
layout (location=0) in vec3 position;
layout (location=1) in vec4 color;
out vec4 vColor;
void main () {
gl_Position = vec4(position, 1);
vColor = color;
}
".into(),
});
let fragment = &device.create_shader(ShaderDescriptor {
usage: ShaderStage::FRAGMENT,
code: "#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 outColor;
void main () {
outColor = vColor;
}
".into(),
});
// 4. Create pipeline
let pipeline = device.create_render_pipeline(RenderPipelineDescriptor {
vertex,
fragment,
buffers: &[VertexBufferLayout {
stride: core::mem::size_of::<[f32; 7]>() as BufferSize,
step_mode: VertexStepMode::Vertex,
attributes: &[
VertexAttribute { shader_location: 0, format: VertexFormat::F32x3, offset: 0 },
VertexAttribute { shader_location: 1, format: VertexFormat::F32x4, offset: core::mem::size_of::<[f32; 3]>() as BufferSize },
],
}],
bind_groups: &[],
targets: Default::default(),
primitive: Default::default(),
depth_stencil: Default::default(),
multisample: Default::default(),
});
// 5. Create default pass
let pass = device.create_render_pass(RenderPassDescriptor::Default {
clear_color: Some(Color(0.1, 0.2, 0.3, 1.0)),
clear_depth: None,
clear_stencil: None,
});
// 6. Render
{
let encoder = device.render(&pass);
encoder.pipeline(&pipeline);
encoder.vertex(0, &buffer, 0);
encoder.draw(0..3, 0..1);
encoder.submit();
}
device.present();
}
```
To run the above WASM module, you need the dependency on `mugl` NPM package and the following JS glue code:
```shell
npm install --save mugl
```
```javascript
import { set_context_memory } from "mugl/wasm";
import { memory, app_id, render } from "hello_world.wasm";
set_context_memory(app_id(), memory); // Required only if `wasm-bindgen` feature is not enabled
// 1. Create canvas with id "canvas"
const canvas = document.createElement("canvas");
canvas.id = "canvas";
canvas.width = canvas.height = 512;
document.body.appendChild(canvas);
// 2. Call render in WASM
render();
```