BevyでのShader勉強のメモ。Bevy は WebGPU (wgpu クレート) を使っているのでそっちも理解する必要がある。
- WebGPUの概観をつかむため、 WebGPU のチュートリアルの"基本" 読み、
- Rustでの扱い方を学ぶため
wgpuチュートリアル の Beginner / The Depth Buffer まで読んだ - そろそろBevy上でシェーダ触るか、ということで公式Examplesを写経し、何をどのように触れるのかを理解する ←今ここ
公式のExamplesを写経して理解したことをメモる
Animated
独自のフラグメントシェーダを適用する例。シェーダへの固有のInputなし。
フラグメントシェーダ内では mesh_view_bindings を通して時間を受け取っている。この時間を使ってアニメーションする。
独自のフラグメントシェーダを適用するために、 bevy::pbr::Material トレイトを実装する。これはどのようにレンダリングされるかを表現するトレイト。
レンダリングの対象となるメッシュを持つ Mesh3d コンポーネントに対し、 Material を MeshMaterial3d コンポーネントに包んで対応付けする。これを MaterialPlugin のシステムがレンダリングしてくれる。
Material は AsBindGroup をともに実装する必要がある。
bevy::render::render_resource::AsBindGroup トレイト&deriveマクロはBindGroupを表現するトレイトと、それを宣言的に実装するderiveマクロ。シェーダに渡したいリソースを表現する。
StandardMaterial などはこれらを実装している。
サンプルのフラグメントシェーダ内にて mesh_view_bindings::globals を #import ( naga_oil ? ) している。これは WGSL 自体の機能ではないらしい。LSPの支援を得ようとすると設定に苦労しそう。
WESL が使えるようなので、そちらを選択する手もあるかも? 'wgsl-analyzer' は WESLも experimentalだがサポートしているとか。あとで写経してみる。
WESL
シェーダ言語を WESL とする例。
import と @if の例が入っている。
import
wesl から別の wesl を import するメカニズム。
custom_material.wesl にて util.wesl から import した関数を呼びだしている。
custom_material.wesl
import super::util::make_polka_dots;
// 中略
fn fragment(
mesh: VertexOutput,
) -> @location(0) vec4<f32> {
return make_polka_dots(mesh.uv, material.time.x);
}
Rust側にて事前に util.wesl をロードしておく必要がある。例ではロード後、解放されないようリソースにハンドルを保持している。
main.rs
impl Plugin for CustomMaterialPlugin {
fn build(&self, app: &mut App) {
let handle = app
.world_mut()
.resource_mut::<AssetServer>()
.load::<Shader>("shaders/util.wesl");
app.insert_resource(UtilityShader(handle));
}
}
なお、事前のロードを削除してみると custom_material.wesl ロード時に ModuleNotFound エラーが発生する。
thread 'Async Compute Task Pool (0)' (29112) panicked at /home/mori/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_shader-0.18.0/src/shader_cache.rs:238:30:
called `Result::unwrap()` on an `Err` value: Error(Diagnostic { error: ResolveError(ModuleNotFound(ModulePath { origin: Absolute, components: ["shaders", "util"] }, "Invalid asset id")), detail: Detail { source: None, output: None, module_path: Some(ModulePath { origin: Absolute, components: ["shaders", "custom_material"] }), display_name: None, declaration: None, span: None } })
@if
Conditional Translation :
WESLのtranslator にパラメタを渡し、1つのweslコードから複数のバージョンの出力コードを得る方法らしい。
Rust の #[cfg(feature = "...")] 構文に似たもの、とのこと。
WESL の translaator がどのタイミングで動作するものかが気になるところ。
ちゃんと確認して無いが、 RenderPipelineDescriptor にてパラメタを渡している点をみるに、
レンダーパスを生成~描画する度に translation する機会があるとみてよさそうである。
例では、Spaceキー押下により util.wesl の関数の動作を切り替えている。
util.wesl
@if(!PARTY_MODE) {
let color1 = vec3<f32>(1.0, 0.4, 0.8); // pink
let color2 = vec3<f32>(0.6, 0.2, 1.0); // purple
dot_color = mix(color1, color2, is_even);
is_dot = step(dist_from_center, 0.3);
} @else {
// 略
Rust側はなかなか面倒そう?
カスタムしたマテリアルの Material::specialize() にてRenderPipelineDescriptor を書き換え、そこでパラメタを渡している。
impl Material for CustomMaterial {
// 略
fn specialize(
_pipeline: &MaterialPipeline,
descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayoutRef,
key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> {
let fragment = descriptor.fragment.as_mut().unwrap();
fragment.shader_defs.push(ShaderDefVal::Bool(
"PARTY_MODE".to_string(),
key.bind_group_data.party_mode,
));
Ok(())
}
}
key.bind_group_data には Material の bind_group_data() にて作成したものが入っている。 これを使って CustomMaterial のフィールドの値を渡している。
main.rs : #[bind_gropu_data(CustomMaterialKey)] を指定し、 &CustomMaterial から CustomMaterialKey を生成可能にすることで上記 key として渡せる。
// This is the struct that will be passed to your shader
#[derive(Asset, TypePath, AsBindGroup, Clone)]
#[bind_group_data(CustomMaterialKey)]
struct CustomMaterial {
// Needed for 16 bit alignment in WebGL2
#[uniform(0)]
time: Vec4,
party_mode: bool,
}
#[repr(C)]
#[derive(Eq, PartialEq, Hash, Copy, Clone)]
struct CustomMaterialKey {
party_mode: bool,
}
impl From<&CustomMaterial> for CustomMaterialKey {
fn from(material: &CustomMaterial) -> Self {
Self {
party_mode: material.party_mode,
}
}
}
Extended material
未
Post processing - Custom render pass
未
Instancing
未
Render depth to texture
未
未
Custom render phase
未
Custom phase item
未
TODO:
- WESL の translation の機会は?