kugi's notebook

やったこと、思ったことなどつらつらと書きます

モーションブラーのレンダリング

はじめに

こちらの記事はレイトレーシング Advent Calendar 2021の18日目の記事です。

(大遅刻をしてしまいました...)

qiita.com

こちらのアドカレに投稿させて頂くのは初めてになります。

昨年までは広島で蛍光現象のレンダリングについて研究をしていて、 現在は東京でゲームプログラマをしています。

最近はレイトレを改めて一から勉強するため、1週間レイトレをベースに進めています。

github.com

RAY (Notionに残しているメモ)

今回は Ray Tracing: The Next Week から「モーションブラー」についての記事になります。

モーションブラー

モーションブラーは撮影のタイミングで被写体やカメラが移動することによって起きます。 シャッタースピードが速いカメラで動いている被写体を撮影すると、被写体が止まっているような写真を撮ることができます。

実験してみた

以下のように、左から右へ移動する赤甲羅を被写体にし、カメラのシャッタースピードを変えて撮影します。

iPhoneのカメラでは設定からシャッタースピードを変えることができないみたいなので、 「Mカメラ」というアプリを使って変更しました。

M=Camera

M=Camera

  • KANAME OHARA
  • Photo & Video
  • Free
apps.apple.com

シャッタースピード 1/15秒

シャッタースピード 1/500秒

変更したのはシャッタースピードのみなので、露光時間が短い 1/500 秒の方では少し暗めの画像になっていますが、赤甲羅のブレは少なく、くっきりとした輪郭が撮影できています。

逆に露光時間の長い、シャッタースピード 1/15 秒の方では、モーションブラーがつき、赤甲羅が横に走って行ったということ画像からがわかります。

モーションブラーのレンダリング

それでは、モーションブラーありの画像をレンダリングしていきます。

モーションブラーはシャッタースピードを変えることで表現することができます。 そのため、カメラとカメラから飛ばすレイに時間の概念を導入します。

このカメラではシャッターが t_0 から t_1 の間開いていることになり、シャッタースピードt_1 - t_0 秒です。

get_ray()関数は時刻 $t$ においてカメラから飛ばしたレイを取得する関数です。 (ここで時刻 t[t_0, t_1])

// カメラ
class camera {
  public:
    /// ...

    float _t0 {0.0f};
    float _t1 {1.0f};

    /// ...

    ray get_ray(float u, float v) {
        float time = _t0 + drand48() * (_t1 - _t0); // 時刻 t は t_0から t_1の間
        return ray(origin, lower_left_corner + u * horizontal + v * vertical - origin, time); // 時刻 t に飛ばしたレイを返す
    }
}

class ray {
 public:
  ray(const vec3& origin, const vec3& direction, float time = 0.0f) {
    _origin = origin;
    _direction = direction;
    _time = time;
  }
  vec3 origin() const { return _origin; }
  vec3 direction() const { return _direction; }
  float time() const { return  _time; }
  vec3 point_at_parameter(float t) const { return _origin + t * _direction; }

  vec3 _origin; // レイの原点
  vec3 _direction;  // レイの進む方向
  float _time; // 時間
};

そして、描画するオブジェクトに対しても時間の概念を持たせます。

Ray Tracing: The Next Weekではt_0からt_1の間でp_0からp_1に移動する球を実装します。

/// 移動球
class moving_sphere : public hitable {
 public:
  moving_sphere() {}
  moving_sphere(vec3 cen0, vec3 cen1,
                float t0, float t1,
                float r,
                material *m) :
                center0(cen0), center1(cen1),
                time0(t0), time1(t1),
                radius(r),
                mat_ptr(m) {};
  virtual bool hit(const ray &r, float tmin, float tmax, hit_record &rec) const;
  vec3 center(float time) const; // 時刻 t における球の中心位置
  vec3 center0, center1;
  float time0, time1;
  float radius; // 球の半径
  material *mat_ptr; // 球のマテリアル
};

/// time0 から time1の間で移動する球の
/// 時刻 t における中心座標
vec3 moving_sphere::center(float time) const {
  return center0 + ((time - time0) / (time1 - time0)) * (center1 - center0);
}

そして、レイと球の交差判定、散乱、反射を計算する際にはある時刻 t におけるレイの計算として処理を拡張します。 時刻tにおけるレイを用いて計算をすることで、時刻t_0から時刻t_1の間にカメラのセンサーが受け取った像の平均値としてシミュレーションすることができます。

描画結果

モーションブラーなし

モーションブラーあり

モーションブラーありの方では、時刻t_0から時刻t_1の間、左から右に移動する球を用いました。

まとめ

今回はじめてレイトレーシングアドベントカレンダーに参加させていただきました。 (にもかかわらず遅刻してしまい申し訳ないです...)

本当は研究もしていた蛍光現象の記事を書きたかったのですが、 準備が間に合いませんでした...。

来年こそはしっかり準備をして、蛍光現象についてのアドカレ記事を書きたいです。

頑張るぞ〜〜〜!

引用

https://raytracing.github.io/books/RayTracingTheNextWeek.html

週末レイトレーシング 第二週 (翻訳)