Structures Overview
In WebGPU, shaders process complex multi-dimensional data such as 3D meshes, material properties, and physical lights. A structure is a user-defined custom data type that groups related variables into a single, cohesive unit.
Rather than managing disjointed values or passing numerous individual parameters to functions, you can declare structures to modularize and logically model your physical graphics pipelines.
Declaring Structures
Structures are declared at module scope (outside of functions) using the struct keyword, followed by the struct name and a comma-separated list of member variables inside braces:
struct Vehicle {
num_wheels: u32,
mass_kg: f32, // Note: trailing commas are fully optional
} // Semicolons after closing braces are also optional
Each member variable must have a unique identifier and a concrete data type.
Constructing Structure Values
You can construct a structure value using its functional constructor. WGSL provides two primary initialization paths:
1. All-Member Constructor
To initialize a structure with specific values, pass arguments to the constructor in the exact order members were declared:
MyStruct(val_1, val_2, ...)
- Here, val_1 initializes the first member of the structure, val_2 initializes the second member, and so on.
- The types of the passed expressions must strictly match the declared types of the members. For example:
2. Zero-Initialized Constructor
If a structure is constructible, you can omit all arguments to construct a value with all members recursively initialized to zero (or equivalent default states):
MyStruct()
- For instance,
Vehicle()constructs aVehiclewithnum_wheelsset to0uandmass_kgset to0.0.
Accessing and Mutating Members
Use standard dot-member notation (.) to read or write a member variable:
- Reading:
const wheels = a_bicycle.num_wheels;retrieves the current value of the member. - Mutating: Within functions, if a structure is declared as a mutable variable (
var), individual members can be updated directly without affecting the remaining data:
Structural Nesting
WGSL supports structural nesting, meaning a structure can contain another structure as a member variable. This is highly useful in graphics to represent hierarchical entities, such as nested material lighting structures:
struct Material {
color: vec3<f32>,
shininess: f32,
}
struct PointLight {
position: vec3<f32>,
intensity: f32,
material: Material, // Nested struct member!
}
To initialize nested structures, nest their constructors:
const default_material = Material(vec3<f32>(1.0, 0.5, 0.0), 32.0);
const light = PointLight(vec3<f32>(0.0, 5.0, -2.0), 1.5, default_material);
You can read nested members by chaining dot-member accessors together:
light.material.shininessaccesses the nestedshininessmember.
/*
* Copyright ©2026 Michael R. Bernstein. All new modifications licensed under Apache 2.0.
* Upstream lineage ©2023 governed by original BSD 3-Clause. See README.md.
*/
struct Vehicle {
num_wheels: u32,
mass_kg: f32, // The last comma is optional
} // Semicolon is optional
// Constructing a structure value by providing values for all members.
const a_bicycle = Vehicle(2, 10.5);
//const bad_vehicle = Vehicle(1.5, 20); // Error: num_wheels must be u32
// Get a member from a structure with '.' and then the member name.
const bike_num_wheels = a_bicycle.num_wheels;
fn add_cargo(v: Vehicle, cargo_mass: f32) -> Vehicle {
var result = v;
// Use dot-member notation to update a single member.
// The other members are unchanged.
result.mass_kg += cargo_mass;
return result;
}
const all_zeros_vehicle = Vehicle();
struct Material {
color: vec3<f32>,
shininess: f32,
}
struct PointLight {
position: vec3<f32>,
intensity: f32,
material: Material,
}
const default_material = Material(vec3<f32>(1.0, 0.5, 0.0), 32.0);
const light = PointLight(vec3<f32>(0.0, 5.0, -2.0), 1.5, default_material);
const light_shininess = light.material.shininess;