You use shaders in Rust by compiling them into binary blobs or embedding them as strings, then passing them to a graphics API like wgpu, glium, or winit via a shader module. The most modern and recommended approach is using the wgpu crate, which handles shader compilation, validation, and binding automatically.
For a quick start, you can embed GLSL or WGSL shader code directly in your Rust source using the include_str! macro, then create a shader module. Here is a minimal example using wgpu with WGSL:
use wgpu::{ShaderModuleDescriptor, ShaderSource};
// Embed the shader code directly
const SHADER_SOURCE: &str = include_str!("shaders/basic.wgsl");
fn create_shader_module(device: &wgpu::Device) -> wgpu::ShaderModule {
device.create_shader_module(ShaderModuleDescriptor {
label: Some("Basic Shader"),
source: ShaderSource::Wgsl(SHADER_SOURCE),
})
}
Your basic.wgsl file would look like this:
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {
let pos = array<vec2<f32>, 3>(
vec2<f32>(0.0, 0.5),
vec2<f32>(-0.5, -0.5),
vec2<f32>(0.5, -0.5)
);
return vec4<f32>(pos[vertex_index], 0.0, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
If you prefer to keep shaders in separate .glsl files and compile them at runtime, you can use the glsl-to-wgsl toolchain or libraries like glslang to convert them, though embedding is generally safer for distribution. For more complex projects involving uniform buffers or textures, you define struct types in WGSL that match your Rust struct layouts, ensuring memory alignment matches the GPU's expectations.
// Rust struct matching WGSL uniform block
#[repr(C)]
struct Uniforms {
model: [[f32; 4]; 4],
time: f32,
}
In WGSL:
struct Uniforms {
model: mat4x4<f32>,
time: f32,
}
@group(0) @binding(0)
var<uniform> uniforms: Uniforms;
Always ensure your Rust data structures are #[repr(C)] and aligned correctly to avoid undefined behavior when passing data to the GPU. The wgpu crate will validate these bindings at runtime, making it easier to catch mismatches early.