Skip to content

Custom materials#86

Open
tychedelia wants to merge 8 commits intoprocessing:mainfrom
tychedelia:custom-materials
Open

Custom materials#86
tychedelia wants to merge 8 commits intoprocessing:mainfrom
tychedelia:custom-materials

Conversation

@tychedelia
Copy link
Member

@tychedelia tychedelia commented Mar 10, 2026

Custom Materials

Adds support for user defined materials, using WESL.

Background (Runtime Materials)

The implementation here is a bit tricky. In Bevy, materials are Rust structs that are processed at compile time with the AsBindGroup macro, which produces a shader layout and associated metadata for the given material type/struct.

This kind of compile time material definition doesn't work for Processing, since our goal is to allow users to define materials in Python at runtime.

Consequently, we take advantage of recent work in Bevy, which allows supplying material information to the renderer at runtime as extracted data. These changes are being made in Bevy in order to support more runtime material definition in the upcoming Bevy Editor.

Warning

This is a relatively low level API that is likely to change in the future.

Reflection

One of the most difficult things for new learners of WebGPU is understanding shader layout (i.e. BindGroupLayout). Our goal here is to abstract over layout entirely so that users only need to know the bare minimum which is that they have to add shader resources as @group(3) @binding(1), etc.

To do so, we use shader reflection, which introspects the user's shader and generates a layout for that shader automatically. As such, when the user sets a value for their shader, we simply can look up to see if that value has a matching declaration in the shader, or ignore it.

This also means that, in the context of hot reloading, we do not require the user to specify every value. We can default initialize values for the user until they modify their sketch to set that value.

Bevy Integration

We are using a library I maintain, bevy_naga_reflect, which has a type DynamicShader that wraps a naga module and provides runtime reflection over its bind group layout. Given a compiled shader module, DynamicShader can:

  • Enumerate all uniform and storage bindings
  • Generate BindGroupLayoutDescriptor entries for a given bind group index
  • Create GPU bind group bindings from the reflected structure, with default-initialized values
  • Allow setting individual fields by name using Bevy's reflection system

This is what lets us bridge the gap between Bevy's compile-time material system and Processing's runtime API. The CustomMaterial asset stores a DynamicShader alongside a handle to the compiled Shader asset, and implements ErasedRenderAsset to produce PreparedMaterial data for the renderer.

Custom Imports

We provide a shader prelude that shadows Bevy's own types (i.e. bevy_pbr::forward_io::VertexOutput). Additionally, I'm packaging Lygia which users can import as well.

Demo

Screenshot 2026-03-10 at 1 08 39 PM

@tychedelia tychedelia marked this pull request as ready for review March 10, 2026 20:10
@tychedelia tychedelia requested a review from catilac March 10, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant