ねこでじ(Nekodigi)

Nekodigi’s diary

学習中の気づきをまとめています。応援よろしくお願いします

【Processing】Ray Marchingを実装する!

成果物

www.youtube.com
今回は、Ray Tracing法の一種である、Ray Marchingのプログラムを作成しました。前回のPath Tracingでは、Rayと面との交差を求めていましたが、このプログラムでは、面との距離を求め、その分だけRayを進め、ぶつかるまで(距離が極小になる)まで繰り返すという仕組みになっています。Ray Marchingは、物体の融合・切り取り、3Dマンデルブロ集合の表示で非常に高いパフォーマンスを発揮します。また、Path Tracingにクオリティは劣るものの、影を付けるなどの処理も行うことができます。

コード

今回のコードはRay_Marching_1_2_combineという名前です。1_0, 1_1, 1_2, 1_3となるにしたがって、機能が増えて難しくなっているので、難しい方はバージョンの低い方から見ることをお勧めします。
github.com

仕組み

基本編

Distance Estimator

Ray Marchingでは、Distance Estimatorで求めた、物体までの距離、物体の色、をもとに計算を行います。Distance Estimatorについては前回の記事と動画をご覧ください。
www.youtube.com
nekodigi.hatenablog.com
今回使ったDistance Estimatorはこちらのサイトをもとにしています。サイトのpは、視点ーRayの原点と同じです。
Inigo Quilez :: fractals, computer graphics, mathematics, shaders, demoscene and more

ピクセル色の取得

Rayが物体に衝突した際は、衝突した物体の色×明るさ×影という計算を行ってピクセルの色を求めます。
明るさを求めるには法線ベクトルが必要ですが、Ray Marchingでは、Rayをx,y,zずらして6回発射し、面の勾配を求めるようにして法線を求めています。

PVector EstimateNormal(PVector eye){//Get normal with difference method
  float x = SceneInfo(new PVector(eye.x+EPSILON, eye.y, eye.z)).dst - SceneInfo(new PVector(eye.x-EPSILON, eye.y, eye.z)).dst;
  float y = SceneInfo(new PVector(eye.x, eye.y+EPSILON, eye.z)).dst - SceneInfo(new PVector(eye.x, eye.y-EPSILON, eye.z)).dst;
  float z = SceneInfo(new PVector(eye.x, eye.y, eye.z+EPSILON)).dst - SceneInfo(new PVector(eye.x, eye.y, eye.z-EPSILON)).dst;
  return new PVector(x, y, z).normalize();
}

後は、法線と光源に向かうベクトルの内積で明るさが求められます。
影の計算は、Rayの衝突地点から光源まで、何もなければ1、何かにぶつかれば、影の濃さ(定数)を返す、というようになっています。実際には少し違いますが、おおよそこのような仕組みです。
たったこれだけで、レンダリングが可能です。具体的にどのような処理をしているかは、実際のコードで確かめてみてください。

ブーリアン(合体、融合、カット、交差)

Distance Estimatorのコードを書き換えることで、これらの処理が可能です。(色の処理もほぼ同様に行います。)

  • 合体・通常のDistance Estimatorでは、より距離が近いオブジェクトがあったとき、より近い距離を結果として返します。
  • 融合・線形補間などを利用して、比較対象の距離と最新の距離の間の距離を返すようにすると、Blend(融合)の処理が可能です。こちらのサイトをもとにしています。

Inigo Quilez :: fractals, computer graphics, mathematics, shaders, demoscene and more

  • カット・比較対象の距離<-最新の距離の時、ー最新の距離を、そうでないとき比較対象の距離を返します。
  • 交差・より遠い距離を結果として返します。

ちなみに、ブーリアンには順番や子オブジェクトなどの情報が密接にかかわってきます。今回は、子オブジェクトに対して、再帰的にブーリアン演算を行うことで親子関係を考慮したブーリアンを行っています。