かっこいい『3Dプログラミング』をつくりたいと
ググって見つけた『レイマーチング(Ray Marching)』という手法。
参考記事
いろいろググるとなかなか難しいようなので、
まずは先輩方のソースコードを真似てみることからやってみることにしました。
早速Youtubeでぴったりな動画を発見。
りやちゃんというVtuber?女性キャラクターなのに、
コード書いている人は思いっきり男性という謎な世界観を完全スルーし、
ただひたすらに『レイマーチング(Ray Marching)』のコードの書き方を真似てみました。
『レイマーチング』入門〜まずは2D設定
動画冒頭から、2Dのコードが記載されていて、
別の動画で解説されてはいるものの、
ハズキルーペよろしく、
っとなってしまったので、とりあえずコピペしてスタートすることにしました。
2D初期のコードはこちら。
#ifdef GL_ES precision mediump float; #endif #extension GL_OES_standard_derivatives : enable uniform float time; //時間 uniform vec2 mouse; //マウス位置 uniform vec2 resolution; //解像度 void main( void ) { vec2 pos = ( gl_FragCoord.xy * 2.0 - resolution) / max(resolution.x, resolution.y); //現在の画素位置を0.0-1.0に正規化 vec2 mouse_pos = (mouse - 0.5) * 2.0; mouse_pos.y *= resolution.y / resolution.x; gl_FragColor = vec4(pos, 0.0, 1.0); //最終的な色の描画 }
このコードをGLSLオンラインエディタにコピペすればOKです。
『レイマーチング』入門〜距離関数
『レイマーチング(Ray Marching)』は、
各オブジェクトまでの距離を測って描画するという仕組みで、
図形ごとに『距離関数』なるものが決まっています。
球の場合はこれ。
// 1. 球の距離関数 float sphere_d(vec3 pos){ return length(pos) - 2.0; // 原点にある位置ベクトルから半径を引くと算出できる }
っと思ってしまいますが、
きっと証明するには時間がかかりそうなので、
走りながら考えるスタイルで、今回はスルーする事にします。
他にもたくさんの距離関数があって、組み合わせることもできます。(こちらも別記事で書く予定です)
『レイマーチング』入門〜始点の位置 (カメラの位置)
つぎに『カメラ』の位置と向き。
2次元の時は全く意識しないのですが、
3次元の場合、奥行きが存在するので、
『カメラ』の位置も考える必要がでてきます。
- 手前で撮るか
- 奥から撮るか
- 斜め上から撮るか
- 右側から撮るかなど
現実世界と同じように、カメラの位置を自由に変えることができます。
今回の動画で紹介されていたコードはこちら。
//2. 始点の定義 (カメラの姿勢が定まる vec3 camera_pos = vec3(0.0, 0.0, -4.0); //カメラの位置 vec3 camera_up = vec3(0.0, 1.0, 0.0); //カメラの上向きベクトル vec3 camera_dir = vec3(0.0, 0.0, 1.0); //カメラの前向きベクトル vec3 camera_side = cross(camera_up, camera_dir); //カメラの横向きベクトル (上向きベクトルと前向きベクトルの外積
vec3というのは文字通り、3次元ベクトルです。
3次元なのでX軸、Y軸、Z軸の3つの値を持ちます。
参考記事
カメラの横向きベクトルは、
上向きベクトルと前向きベクトルの『外積(がいせき)』で求まるそうで、
と思いつつも、走りながら後でリサーチする事にします。
『レイマーチング』入門〜レイの定義 構造体
続いて、光源という意味の『レイ(Ray)』の設定。
コードはこちら。
//3. Rayの定義 (構造体) struct Ray{ vec3 pos; //Rayの現在の座標 vec3 dir; //Rayの進行方向 };
structなので『構造体』でつくっています。
構造体は、名前は難しそうですが、要は変数の組み合わせです。
今回は 『レイの位置』と『レイの向き』をそれぞれ『3次元ベクトル』で定義しています。
要は、
『レイの位置』と『レイの向き』それぞれ、
X, Y, Z の3つの値を持つという事になります。
『レイマーチング』入門〜レイの設定
先ほど作成した『レイ』の構造体を実体化しつつ、
『レイの位置』と『レイの向き』の値を設定します。
// 4. Rayの設定 Ray ray; //ここはインスタンスか ray.pos = camera_pos; //Rayの初期位置 ray.dir = normalize(pos.x * camera_side + pos.y * camera_up + camera_dir); // Rayの進行方向はカメラの姿勢から求めることができる
X・・レイのX軸 × カメラの横向き
Y・・レイのY軸 × カメラの上向き
Z・・カメラの前向き
としているようです。
と思いつつ、とりあえず次へ。
『レイマーチング』入門〜レイの衝突判定
『レイマーチング(Ray Marching)』は、
『距離関数』で『オブジェクト(物体)』までの距離を測定しながら前に進み、
『オブジェクト(物体)』との距離がほぼゼロになったら描画するという仕組みになっています。
コードはこちら。
// 5. Rayの判定 float t = 0.0, d; for (int i = 0; i < 64; i++ ){ //何回でもok 十分な数 d = sphere_d(ray.pos); //距離関数から現在の距離を求める if (d < 0.001) { //計算できた距離が十分に0に近かったら break; //衝突したという判定 } t += d; //当たらなかったら ray.pos = camera_pos + t * ray.dir; //rayの座標更新 どれだけrayを進めるかというと最も近いオブジェクトまでの距離(突き抜け防止 }
まず t (時間)をゼロにして、
『距離関数』でオブジェクト(物体)との『距離』を求めて、
『距離』が限りなくゼロに近づいたら衝突したという判定にして、
そうでなければ『レイ』を前に進めています。
『レイマーチング』入門〜レイが物体に衝突したら
最後に、『レイ』が当たったら、白に色を変えます。
//6. あたったら色を変える if (d < 0.001) { gl_FragColor = vec4(1); } else { gl_FragColor = vec4(0); }
gl_FragColorは色つけの組み込み関数です。
vec4として、RGBA 4つの値を持つことができます。
RGBAだと普通 0〜255の範囲になりますが、
GLSLは0 〜 1 の範囲設定になるので、
『正規化』などの手順が必要になります。
今回のコードをまとめるとこうなります。
#ifdef GL_ES precision mediump float; #endif #extension GL_OES_standard_derivatives : enable uniform float time; uniform vec2 mouse; uniform vec2 resolution; // 1. 球の距離関数 float sphere_d(vec3 pos){ return length(pos) - 2.0; // 原点にある位置ベクトルから半径を引くと算出できる } //3. Rayの定義 (構造体) struct Ray{ vec3 pos; //Rayの現在の座標 vec3 dir; //Rayの進行方向 }; void main( void ) { vec2 pos = ( gl_FragCoord.xy * 2.0 - resolution) / max(resolution.x, resolution.y); //現在の画素位置を0.0-1.0に正規化 vec2 mouse_pos = (mouse - 0.5) * 2.0; mouse_pos.y *= resolution.y / resolution.x; //2. 始点の定義 (カメラの姿勢が定まる vec3 camera_pos = vec3(0.0, 0.0, -4.0); //カメラの位置 vec3 camera_up = vec3(0.0, 1.0, 0.0); //カメラの上向きベクトル vec3 camera_dir = vec3(0.0, 0.0, 1.0); //カメラの前向きベクトル vec3 camera_side = cross(camera_up, camera_dir); //カメラの横向きベクトル (上向きベクトルと前向きベクトルの外積 // 4. Rayの設定 Ray ray; //ここはインスタンスか ray.pos = camera_pos; //Rayの初期位置 ray.dir = normalize(pos.x * camera_side + pos.y * camera_up + camera_dir); // Rayの進行方向はカメラの姿勢から求めることができる // 5. Rayの判定 float t = 0.0, d; for (int i = 0; i < 64; i++ ){ //何回でもok 十分な数 d = sphere_d(ray.pos); //距離関数から現在の距離を求める if (d < 0.001) { //計算できた距離が十分に0に近かったら break; //衝突したという判定 } t += d; //当たらなかったら ray.pos = camera_pos + t * ray.dir; //rayの座標更新 どれだけrayを進めるかというと最も近いオブジェクトまでの距離(突き抜け防止 } //6. あたったら色を変える if (d < 0.001) { gl_FragColor = vec4(1); } else { gl_FragColor = vec4(0); } }
GLSL Sandboxに貼り付けて、白い円が表示されたらOKです。
『レイマーチング』入門 その1 まとめ
『レイマーチング(Ray Marching)』の手法自体が独特なこともあり、
まだまだ細かいところまで調べきれていないのですが、
のスタンスで、ガリガリ書いていこうかなと思います。
『GLSL(シェーディング)』関係ではこんな記事も読まれています。
1. 【GLSL】プログラムでかっこいい映像をつくりたい! 〜『TouchDesigner』を見据えて2. 【WebGL】入門 わかりやすく【図解】してみた
3. 【OpenGL】と【DirectX】のバージョンをまとめてみた【シェーダーメイン】【初心者向け】
4. 【GLSL(シェーディング)】でよく使う関数とユーザー関数のまとめ※随時更新
5. 【GLSL】プログラムでかっこいい映像をつくるには『レイマーチング』なるものを覚えればいいらしい
6. 【GLSL】『レイマーチング』入門その1 距離関数とレイとカメラの設定
7. 【GLSL】『レイマーチング』入門(2) 立体的に見せる方法〜光の反射は『内積』で〜
8. 【GLSL】『レイマーチング』入門(3) 距離関数を使ってみる・回転・合成・量産
9. 【TouchDesigner】で『GLSL』を使う方法まとめ【画像あり】
10. 【TouchDesigner】『GLSL MAT』の使い方 3次元でぐりぐり動かしてみる
11. 【GLSL】波のつくり方簡易まとめ。波もプログラムでつくれます【コピペスタイル】
アオキのツイッターアカウント。
この記事へのコメントはありません。