RustでGPUシェーダーが書ける【Rust GPU】

## はじめに

GPUプログラミングといえば、GLSL、HLSL、WGSLなどの専用シェーダー言語を使うのが一般的です。しかし、これらの言語は大規模なコードベースの管理が難しく、現代的なプログラミング言語と比べると機能面で見劣りします。

[Rust GPU](https://github.com/rust-gpu/rust-gpu)は、Rustプログラミング言語を使ってGPUシェーダーを記述できるプロジェクトです。今回は実際にRust GPUをクローンして、サンプルシェーダーを実行してみました。

> **注意**: Rust GPUは現在も活発に開発中の初期段階プロジェクトです。シンプルなシェーダーのコンパイルと実行は機能し、コアライブラリの大部分もコンパイルできますが、まだ実装されていない機能も多くあります。技術的には使用可能ですが、本番環境での使用には適していません。

## Rust GPUのクローンと実行

以下の手順でRust GPUをクローンし、サンプルを実行します。

```bash
# リポジトリをクローン
git clone https://github.com/Rust-GPU/rust-gpu.git
cd rust-gpu

# サンプルシェーダーを実行
cd examples/runners/wgpu
cargo run -- --shader Sky
```

Skyシェーダーが実行されると、美しい空のレンダリングが表示されます。

![Screenshot 2025-04-12 at 20.05.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79562/923d176a-038a-44d8-bdd1-812196c822e4.png)

他のシェーダーも試してみることができます：

```bash
# 最もシンプルなシェーダー
cargo run -- --shader Simplest
```

![Screenshot 2025-04-12 at 20.17.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79562/02f7f2cb-92c3-49f2-864c-cdd3b55e6ab3.png)

```bash
# マウスインタラクション可能なシェーダー
cargo run -- --shader Mouse
```

![Screenshot 2025-04-12 at 20.05.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79562/4ce8c75f-3a78-4319-bb0c-9c6379687350.png)

## サンプルコードを見てみる

### 最もシンプルなシェーダー

`examples/shaders/simplest-shader/src/lib.rs` にある最もシンプルなシェーダーのコードを見てみましょう：

```rust
#[spirv(fragment)]
pub fn main_fs(output: &mut Vec4) {
    *output = vec4(1.0, 0.0, 0.0, 1.0);
}

#[spirv(vertex)]
pub fn main_vs(
    #[spirv(vertex_index)] vert_id: i32,
    #[spirv(position, invariant)] out_pos: &mut Vec4,
) {
    *out_pos = vec4(
        (vert_id - 1) as f32,
        ((vert_id & 1) * 2 - 1) as f32,
        0.0,
        1.0,
    );
}
```

このシェーダーは非常にシンプルで、フラグメントシェーダーは赤色の出力を行い、頂点シェーダーは画面全体を覆う三角形を生成します。`#[spirv(...)]` アトリビュートによって、関数がシェーダーステージとして機能するように指定されています。

### 空のシェーダー

`examples/shaders/sky-shader/src/lib.rs` には、より複雑な空のシェーダーが実装されています。

```rust
#[spirv(fragment)]
pub fn main_fs(
    #[spirv(frag_coord)] in_frag_coord: Vec4,
    #[spirv(push_constant)] constants: &ShaderConstants,
    output: &mut Vec4,
    // ...
) {
    let frag_coord = vec2(in_frag_coord.x, in_frag_coord.y);
    let mut uv = (frag_coord - 0.5 * vec2(constants.width as f32, constants.height as f32))
        / constants.height as f32;
    uv.y = -uv.y;

    let eye_pos = vec3(0.0, 0.0997, 0.2);
    let sun_pos = vec3(0.0, 75.0, -1000.0);
    let dir = get_ray_dir(uv, eye_pos, sun_pos);

    // Preethamの空モデルを評価
    let color = sky(dir, sun_pos, sun_intensity_extra_spec_const_factor);

    *output = tonemap(color).extend(1.0)
}
```

このシェーダーはPreethamの空モデルを実装し、リアルな空をレンダリングします。光の散乱とトーンマッピングを計算して美しい空の表現を実現しています。

## Rustの強みを活かしたGPUプログラミング

Rust GPUの魅力は、Rustならではの機能をGPUプログラミングに持ち込める点です：

### Rustの特徴を活かしたGPUプログラミング

実際のサンプルコードからわかるように、Rust GPUは以下のようなRustの特徴をGPUプログラミングに活かしています：

```rust
// マウスシェーダーにおけるトレイトの活用例（examples/shaders/mouse-shader/src/lib.rs）
pub trait Shape: Copy {
    fn distance(self, p: Vec2) -> f32;
    
    fn union<S>(self, other: S) -> Union<Self, S> {
        Union(self, other)
    }
    
    fn intersect<S>(self, other: S) -> Intersect<Self, S> {
        Intersect(self, other)
    }
    
    // ...
}
```

マウスシェーダーでは、トレイトを使って図形の操作を抽象化しています。これはRustの型システムの強みを活かした実装例です。

また、CPUとGPU間のデータ共有も簡潔に行えます：

```rust
// SharedConstantsの例
#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
pub struct ShaderConstants {
    pub width: u32,
    pub height: u32,
    pub time: f32,
    // ...
}
```

## まとめ

プロジェクトはまだ初期段階であり、リポジトリのREADMEにも記載されているように、本番環境での利用は推奨されていませんが、サンプルシェーダーの実行などの実験的な用途には使えることを確認できました。

興味のある方は、ぜひ[Rust GPU公式リポジトリ](https://github.com/rust-gpu/rust-gpu)をチェックしてみてください。

## 参考リンク

- [Rust GPU公式リポジトリ](https://github.com/rust-gpu/rust-gpu)
- [Rust GPU Book](https://rust-gpu.github.io/rust-gpu/book/)
- [ShaderToy移植プロジェクト](https://github.com/Rust-GPU/rust-gpu-shadertoys) - 例が多数あります