※今回の記事ではShaderToyでのプログラムになります。プラットフォームによってresolutionなどの表記が変わるので注意してください。解説レイを進めるために必要な変数レイのポジションオブジェクトまでの距離レイの行進が終わるタイミングレイを進めるオブジェクトに色をつける続きは次の記事で!!レイマーチングとはとはコードを書いてお絵かきをするとき、普通にかくと平面の描画、つまり二次元の描画になります。人類というのは貪欲で、未知の挑戦が好きなわけで、二次元の絵ばっか描いてたら今度は三次元の絵もコードで書きたい!ってなってくるんですよ。そんな時に編み出した方法?がレイトレーシングと言われるもので、光(レイ)の経路を追跡(トレース)するという手法です。今回のレイマーチングはそのレイトレーシングと呼ばれるものの一種で、レイ(光)をマーチ(行進)させ、レイが何かにぶつかったらそれを描画する、というものです。以下のGIFをご覧ください。シェーダーでのレイマーチングは、スフィアトレーシングと言われる手法を使っています。円を広げて、ぶつかったらレイ(青色)を進め、また円を広げてぶつかったらレイを進め....を繰り返しています。ぶつかったオブジェクトまでの距離が限りなく近くなったら、ぶつかったということにしてそこに色を塗ります。スクリーンの全ピクセル分の方向に飛ばしたたくさんのレイ一つ一つがこの作業を繰り返すことで、描画が成されます。最初に、3つ決めることがあります。1レイを発射するカメラの位置2スクリーン平面をどれだけカメラに近づけるか3飛ばすレイの方向です。書いているところ、これに関しては別の記事に書こうかと思うのでとりあえずここはとばします!とりあえずはおまじないというか決まりごとというかそんな感じのものだと思ってください。これについて書いた記事を書いたらここに貼り付けておきます。//カメラの位置まず、カメラの位置です。どこにカメラを構えるかそのポジションを設定します。まんまUnityを考えていただくとわかりやすいです。ここでは、スクリーンの位置からZ軸が-10の位置にカメラを構えました。//スクリーン平面をどれだけカメラに近づけるかRayと聞くと、一直線に一本飛ばすイメージがありますが、実際にはスクリーンの全ピクセルの方向にとばし、一本一本上記のスフィアトレーシングを行なっています。スクリーンの四隅のピクセルに向かって飛ばしたレイを可視化すると、カメラからスクリーンが近い方が角度が広がるので広角になってオブジェクトが小さく見え、遠いと角度が狭くなって望遠レンズのような感じになってオブジェクトが大きく見えます。真上から見るとわかりやすいです。スクリーンの両はしのピクセルに向かって飛ばしたレイの角度が違います。Rayを飛ばす方向を定めています。X軸とY軸はuvのピクセルの方向にとばし、Z軸はScreenの方向に飛ばす、ということです。normalize()は値を正規化してくれる関数で、本来であればRayがものすごい長さになって大きな値になるのを、normalizeすることで値を1に変換してくれます。することで計算がしやすくなります。距離関数についてレイを進めてオブジェクトを描画するには、距離関数というものが必要になります。距離関数は、スフィアトレーシングをするための道具だと考えるとわかりやすいです。レイの進め方は、「その時点のレイの位置」から「一番近くにあるオブジェクトの距離を計算(=距離関数の戻り値)」して、それが限りなくゼロに近づいたらオブジェクトにぶつかったことにして色を塗る(=描画する)感じです。距離関数は作りたいオブジェクトの形によって変わってきます。以下のサイトは、ShaderToyを作った人が、距離関数を公開?している場所らしいです。ここからコピペしてきましょう!www.iquilezles.org今回は一番簡単なスフィアの距離関数を使用します。length(p)-s;}これが↑スフィアの距離関数です。引数のpはレイのポジション、sはスフィアのサイズです。これでスフィアが出来上がるのが不思議ですね。レイを進めながらこの距離関数に値を渡してあげることで、オブジェクトの描画ができます。レイの進め方まずはこういう絵を作ります。平面的な丸に見えますが、レイマーチングができています。レイマーチング第一歩レイはfor文でちょっとずつ進めていきます。レイを進める部分を追加して、全体的なコードは以下の感じです。vec3(0.0);レイを進める時に使用する変数を4つ用意します。depthは「レイの進んだ距離」を入れるfloatの変数、distは「距離関数から返ってきた値」を入れるfloatの変数、rayPosは「現在のレイの位置」を入れるvec3の変数、colは描画時の色を決めるvec3の変数です。これを作ってからレイを進めます。以下、レイを進めるコードです。ここはレイのポジションをvec3のrayPos変数に入れています。「その方向にRayがこれだけ進んでるよ」という情報になります。「カメラの位置からこの方向にこれだけRayが進んでるよ!今ここだよ!」という情報になるわけです、この情報を、rayPos変数に入れています。1.0);ここでは、distanceFunctionに現在のレイの位置を渡していて、一番近い位置にあるオブジェクトまでの距離が返ってきます。i++){for文の最初、99はレイを進めるのを終わらせる回数です。オブジェクトの表面にぶつからなければ99回でレイを進めるのを終了します。レイが何かにぶつかったらループを抜ける、というのをif文で入れています。break;距離関数から返ってきた値(一番近い位置にあるオブジェクトまでの距離)が、0.0001より小さければ、「ぶつかった」と判断してここでループを抜けます。ちなみにこの図↓だと何にもぶつからないので、99回ループしたところでレイを進めるのが終わります。ぶつかったらループを抜けてそこに色をつけるし、ぶつからなければ、空っぽく青くする、みたいなこともできます。dist;最後にdepthにdistを足すことで、そこから先にレイが進んでいきます。}レイがオブジェクトにぶつかったら、レイを進めるのを終了してぶつかったところに色をつけます。今回は白に塗るので、vec3(1.0)をcolに入れています。なんかややこしいのですが、最初はスフィアトレーシングをしてぶつかってるやつもどんどん色塗っていくのかなとか思ってたんですが、そうじゃないみたい。スフィアトレーシングは「レイをどれだけ進めたらいいか」の指標として行っている、という感じです。ある時点でのレイの位置から一番近いオブジェクトまでの距離分進むということは、絶対にその間に他のオブジェクトがないということだからです。はてなブログProアイドルになりたいと思いながら死んだ顔で仕事をするプログラマー。めーぷるしろっぷをちょんちょんしています。LSDJについての記事を書きます。(1)リンクはてなブログブログをはじめる週刊はてなブログはてなブログPro引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。