2. 画像の出力
Ray Tracing in One Weekend (v3.2.3): 2 Output an Image / 1.2 画像の出力
この章では,まず PPM 形式の説明があり,サンプル出力が提示されています。
C++ コードは標準出力に PPM 形式で出力します。しかし,本稿では WebAssembly を使用するため,結果を String として返す関数にしました。
rust
pub fn render_image() -> String {
let mut output = String::new();
let image_width = 256;
let image_height = 256;
output.push_str("P3\n");
output.push_str(&format!("{} {}\n", image_width, image_height));
output.push_str("255\n");
for j in (0..image_height).rev() {
for i in 0..image_width {
let r = i as f64 / (image_width - 1) as f64;
let g = j as f64 / (image_height - 1) as f64;
let b = 0.0;
let ir = (255.999 * r) as i32;
let ig = (255.999 * g) as i32;
let ib = (255.999 * b) as i32;
output.push_str(&format!("{} {} {}\n", ir, ig, ib));
}
}
output
}C++ と Rust の違い
カウントダウンループ
原典の C++ は,画像を上から下へ描画するため,行インデックス j を大きい値から 0 へ減らします。
cpp
for (int j = image_height-1; j >= 0; --j) { ... }Rust の .. 範囲は昇順のみです。降順にするには .rev() イテレータを使います。
rust
for j in (0..image_height).rev() { ... }(0..image_height) は 0 から image_height - 1 の範囲を生成し,.rev() で逆順にします。
進捗インジケーター
原典では,レンダリング処理の進捗を示すため std::cerr に残り行数を随時出力する機能が実装されています。
しかし,VitePress 上の WebAssembly では進捗インジケーターは実装できません。WASM 関数を呼び出すと,その関数が完全に終了して制御が JavaScript に戻されるまで,JavaScript のイベントループはブロックされます。進捗表示には DOM の更新が必要ですが,イベントループがブロックされている間はブラウザはレンダリングを行いません。結果として,WASM 内部でループ処理している最中に進捗を表示することは不可能です。
進捗表示を実装するには,以下のような処理の分割が必要になります。
- WASM 内で「1ラインだけ処理」して制御を返す
- JavaScript でイベントループを実行して DOM を更新
- 再び WASM を呼び出す
このような複雑な実装は本稿の実装の対象外とします。本稿では WASM の計算完了を待ってから結果を表示する方式を採用します。