Authoring Shaders
Learning WGSL
We recommend these resources to learn how to write WGSL shaders:
- Read Tour of WGSL for an introduction tutorial.
- The WGSL spec.
- You will find good recommendations about WebGPU in general in https://github.com/mikbry/awesome-webgpu
- Join your favorite community: WESL Discord, Bevy Discord, Rust GameDev Discord
- (more to add Coming Soon)
Learning WESL Extensions
WESL currently provides these functionalities:
- Imports allow splitting code in multiple files.
- Packages are collections of general-purpose shader modules published to npm (JavaScript) and crates.io (Rust).
- Conditional Translation is a mechanism to conditionally include/exclude chunks of code. Similar to
#ifdef
(C/C++) or#[cfg]
(Rust). - …More Coming Soon!
Import Statements
Import statements are placed at the top of your file. All they do is add a declaration present in another file to the scope.
import sdf::primitives::box; // import function `box` in module `primitives` in package `sdf`.
import package::bindings::mySampler; // import declaration `mySampler` from file `bindings.wesl` located at the root of this package.
import super::util; // import the entire sibling module `./util.wesl`.
import package::animals::{ // you can nest imports with `{ }`.
bird,
mammals::{ dog, cat }
};
- What can be imported? - You can import all top-level declarations in a file: functions, structs, variables and aliases. You can also import whole modules.
- How to locate a file? - The first segment indicate where to look for the file:
- If it is
super
, start from the folder containing the current file. You can chainsuper
s to walk up the file tree. - If it is
package
, start at the package root (provided to the linker). - Any other name corresponds to an external package.
- If it is
Qualified names
In addition to import statements, you can write directly the path to a declaration at the usage site.
import package::math;
fn main() {
package::my_mod::my_submod::my_fn(); // import statement not required.
let x = math::constants::PI; // module `math` was imported, you can access its declarations with a qualified name.
}
Shader entrypoints and co.
We recommend putting shader entrypoints and pipeline overridable constants in the root module. This ensures that they do not get mangled.
@if
attribute
The @if(condition)
attribute makes the following element optionally included in the final shader, depending on the condition
.
The condition
is made of feature flags which are set when you invoke the linker (wesl-js or wesl-rs).
@if(debug_mode && !raytracing_disabled) // attributes on top-level declarations
var<private> debug_count_iterations: i32 = 0;
@if(is_2d)
alias point = vec2f;
@if(!is_2d) // the @else block is in discussion, meanwhile just negate the if condition.
alias point = vec3f;
struct Line {
start_point: point, end_point: point,
@if(colored) color: vec4f, // attributes on struct members
}
fn fibonacci(n: u32) {
@if(use_lookup_table) { // attributes on blocks `{ }`
const lut = array(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144);
return lut[n];
}
// ...
}
The condition is a boolean expression containing: feature names, &&
(and), ||
(or), !
(not), true
and false
.
You can place attributes on declarations (functions, structs, variables, etc.), statements, struct members and function parameters.
Next Steps
Visit the Reference page for the complete documentation of WESL Extensions.