数学

【GLSL】距離関数を使ってみる・回転・合成・量産『レイマーチング』入門(3)


Warning: Trying to access array offset on value of type bool in /home/mochaccino8/coinbaby8.com/public_html/wp-content/themes/mag_tcd036/functions/short_code.php on line 36

Warning: Trying to access array offset on value of type bool in /home/mochaccino8/coinbaby8.com/public_html/wp-content/themes/mag_tcd036/functions/short_code.php on line 36

Warning: Trying to access array offset on value of type bool in /home/mochaccino8/coinbaby8.com/public_html/wp-content/themes/mag_tcd036/functions/short_code.php on line 36

プログラミングでかっこいい映像がつくれるということで始めた『レイマーチング(Ray Marching』という手法。

前回の記事。

お手本の映像はこちら。

今回は、『立方体(りっぽうたい)』の『距離関数』を使ったり、回転したり、合成させたりしてみました。

アオキ
『立方体』は、四角形の3D版ですかね。
Sponsored link

『レイマーチング』入門 距離関数 立方体をつくる

『レイマーチング(Ray Marching)』という手法では、

『距離関数』がとても重要な役割になっていて、

描画したい図形によって『距離関数』を変える必要がでてきます。

今回登場したのはこんな『立方体』。

『距離関数』はこちら。

//10. 立方体の距離関数
float box_d(vec3 pos){
    return length(max(abs(pos) - vec3(1.0, 1.0, 1.0), 0.0));
}

『距離関数』を立体的に見せるための『法線ベクトル』はこちら。

// 11. 立方体の法線ベクトル
vec3 box_normal(vec3 pos){
    float delta = 0.001;
    return normalize(vec3(
        box_d(pos + vec3(delta, 0.0, 0.0)) - box_d(pos - vec3(delta, 0.0, 0.0)),
        box_d(pos + vec3(0.0, delta, 0.0)) - box_d(pos - vec3(0.0, delta, 0.0)),
        box_d(pos + vec3(0.0, 0.0, delta)) - box_d(pos - vec3(0.0, 0.0, delta))
        ));
}
アオキ
sphere_normal を box_normal に変えただけですね。

『レイ(Ray)』の判定を、sphere_d からbox_d に変更すれば『立方体』が表示されます。

    // 5. Rayの判定
    float t = 0.0, d;
    for (int i = 0; i < 64; i++ ){ //何回でもok 十分な数
        d = box_d(ray.pos); // sphere_d から box_d に変更

カメラがより過ぎてた感があるので 少しカメラを後ろに下げておきます。

    //2. 始点の定義 (カメラの姿勢が定まる
    vec3 camera_pos = vec3(0.0, 0.0, -8.0); //カメラの位置 -4.0 -> 8.0 

『レイマーチング』入門 距離関数 回転させてみる

『立方体』を正面から見ただけでは3D感がでないので、

回転させてみることにします。

物体を回転させるにはいくつかの方法があるのですが、

今回の動画では『回転行列』を使っていました。

『回転行列』を使うと、
決まった法則でかけ算することで、
座標を回転させることができます。

Wikipedia 回転行列

X軸の回転行列はこちら。

コードにするとこう。

//12. 回転行列(X軸)
mat3 x_axis_rot(float angle){
    float c = cos(angle);
    float s = sin(angle);
    return mat3(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); 

}

『mat3』という型が、3×3の行列を表すようです。

『回転行列』の

  • 左上、中上、右上
  • 左中、中中、右中
  • 左下、中下、右下 

という順番で書いています。

Y軸の回転行列はこちら。

コードはこう。

//12. 回転行列(Y軸)
mat3 y_axis_rot(float angle){
    float c = cos(angle);
    float s = sin(angle);
    return mat3(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0,  c);
}

実際に使う時はマウス位置のX、Yと連動させています。

   //13. 回転行列追加
   mat3 rot = x_axis_rot(mouse_pos.x) * y_axis_rot(mouse_pos.y);

『レイ(Ray)』の位置も回転させています。

    // 5. Rayの判定
    float t = 0.0, d;
    for (int i = 0; i < 64; i++ ){ //何回でもok 十分な数
        d = box_d(rot * ray.pos); //距離関数から現在の距離を求める // rot をかけ算
        if (d < 0.001) {  //計算できた距離が十分に0に近かったら
            break; //衝突したという判定
   //8. 光源が当たる方向を定義(z方向に光があたるように
//   vec3 light_dir = vec3(0.0, 0.0, 1.0);
   vec3 light_dir = vec3(- mouse_pos, 1.0); //マウスを光源としてみる
   vec3 normal = sphere_normal(rot * ray.pos); // rot をかけている
アオキ
これで、マウスの動きに合わせて『立方体』をぐるぐる動かすことができます。

Sponsored link

『レイマーチング』入門 距離関数 合成してみる

『距離関数』は単独で使うのはもちろん、

2つの『距離関数』を合成させることもできます。

作成済みの2つの『距離関数』を合成してみます。

  • 『球』の『距離関数』
  • 『立方体』の『距離関数』
//14. 距離関数はくっつけることができる(差集合、積集合など)
float object_d(vec3 pos){
    return max(sphere_d(pos), box_d(pos));
}

min関数 やmax 関数を使うことで合成できるようですが、
うまく雰囲気が掴めてないのでやりながら覚えていく方がいいかなと思います。

立体的に見せるための『法線ベクトル』も作成します。

// 15.  合成距離関数 3D表現(法線ベクトル)
vec3 object_normal(vec3 pos){
float delta = 0.001;
    return normalize(vec3(
        object_d(pos + vec3(delta, 0.0, 0.0)) - object_d(pos - vec3(delta, 0.0, 0.0)),
        object_d(pos + vec3(0.0, delta, 0.0)) - object_d(pos - vec3(0.0, delta, 0.0)),
        object_d(pos + vec3(0.0, 0.0, delta)) - object_d(pos - vec3(0.0, 0.0, delta))
    ));
}
アオキ
名前が object_normal になっただけですね。

『レイ(Ray)』の判定なども box_d から object_d に変更します。

    // 5. Rayの判定
    float t = 0.0, d;
    for (int i = 0; i < 64; i++ ){ //何回でもok 十分な数
        d = object_d(rot * ray.pos); //距離関数から現在の距離を求める // rot をかけ算 // object_dに変更
        if (d < 0.001) {  //計算できた距離が十分に0に近かったら
            break; //衝突したという判定
   //8. 光源が当たる方向を定義(z方向に光があたるように
//   vec3 light_dir = vec3(0.0, 0.0, 1.0);
   vec3 light_dir = vec3(- mouse_pos, 1.0); //マウスを光源としてみる
   vec3 normal = object_normal(rot * ray.pos); // rot をかけている // object_dに変更

こんな感じで表示されればOKです。

アオキ
おぅ、みごとにまざっとりますな・・
アオキ
ちなみに、『球』の距離関数の値を2.0 -> 1.5 あたりに変更すると、合成具合を調整できます。
// 1. 球の距離関数
float sphere_d(vec3 pos){
    return length(pos) - 1.5; // 原点にある位置ベクトルから半径を引くと算出できる // 2.0->1.5
}

他にもたくさんの『距離関数』があるようです。

『距離関数』の海外サイト

うまく組み合わせたり加工することで、いろんな画像がつくれるようで。

GLSLレイマーチング研究_距離関数について勉強してみた

アオキ
ここはつど調整しながら作っていく感じでしょうな。

『レイマーチング』入門 距離関数 スムースに合成してみる

『距離関数』2つの合成をよりスムーズにするための方法もあるようです。

// 16. スムース
float smin(float d1, float d2, float k ){
    float res = exp(-k * d1) + exp(-k * d2);
    return -log(res) / k;
}
アオキ
ここはわからん・・

どうやら『exp』 はネイピア数で、『log』が対数だそうですが、

アオキ
だとしてもなにがなんだか・・

なので、素直にコピペしておきます。

こちらの記事により詳細が書かれています。

オブジェクト同士を補間して結合する

2019/8/24 追記
『対数』と『ネイピア数』記事書きました。


合成用の距離関数に、作成した『smin関数』を設定します。

//15. 距離関数はくっつけることができる(差集合、積集合など)
float object_d(vec3 pos){
    return smin(sphere_d(pos), box_d(pos), 5.0); //sminに変更
    //return max(sphere_d(pos), box_d(pos)); //コメントアウト
}

こんな感じに表示されればOKです。

アオキ
確かにさっきよりスムーズに合成されてますな。
Sponsored link

『レイマーチング』入門 距離関数 量産してみる

『レイマーチング(Ray Marching)』では、複数の画像を同時に見せるのも簡単なようで、

『mod』関数を使うと実現できます。

mod関数は数学では『合同式』というそうです。

GLSLの数学的な関数で感じたこと

//16. 距離関数はくっつけることができる(差集合、積集合など)
float object_d(vec3 pos){
    vec3 p = mod(pos, 8.0) - 4.0; // mod関数を使う
    return smin(sphere_d(p), box_d(p), 5.0); // 引数にpをつける
    //return max(sphere_d(pos), box_d(pos));
}

これだけでこんな映像が表現されます。

アオキ
な、なんかこうどわーっと・・(語彙力

カメラの動きを調整することで、
前に進むような映像もつくることができます。

    //2. 始点の定義 (カメラの姿勢が定まる
    vec3 camera_pos = vec3(0.0, 0.0, -8.0 + time * 5.0); //カメラの位置 // timeの5倍を足している
    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); //カメラの横向きベクトル (上向きベクトルと前向きベクトルの外積

完成版のコードはこちら。

#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) - 1.5; // 原点にある位置ベクトルから半径を引くと算出できる // 2.0->1.5
}


//10. 立方体の距離関数
float box_d(vec3 pos){
    return length(max(abs(pos) - vec3(1.0, 1.0, 1.0), 0.0));
}

// 16. スムース
float smin(float d1, float d2, float k ){
    float res = exp(-k * d1) + exp(-k * d2);
    return -log(res) / k;
}


//14. 距離関数はくっつけることができる(差集合、積集合など)
float object_d(vec3 pos){
    vec3 p = mod(pos, 8.0) - 4.0; // mod関数を使う
    return smin(sphere_d(p), box_d(p), 5.0); // 引数にpをつける
    //return max(sphere_d(pos), box_d(pos));

}


// 7. 球の法線ベクトル
vec3 sphere_normal(vec3 pos){
    float delta = 0.001;
    return normalize(vec3(
        sphere_d(pos + vec3(delta, 0.0, 0.0)) - sphere_d(pos - vec3(delta, 0.0, 0.0)),
        sphere_d(pos + vec3(0.0, delta, 0.0)) - sphere_d(pos - vec3(0.0, delta, 0.0)),
        sphere_d(pos + vec3(0.0, 0.0, delta)) - sphere_d(pos - vec3(0.0, 0.0, delta))
        ));
}

// 11. 立方体の法線ベクトル
vec3 box_normal(vec3 pos){
    float delta = 0.001;
    return normalize(vec3(
        box_d(pos + vec3(delta, 0.0, 0.0)) - box_d(pos - vec3(delta, 0.0, 0.0)),
        box_d(pos + vec3(0.0, delta, 0.0)) - box_d(pos - vec3(0.0, delta, 0.0)),
        box_d(pos + vec3(0.0, 0.0, delta)) - box_d(pos - vec3(0.0, 0.0, delta))
        ));
}

// 15.  合成距離関数 3D表現(法線ベクトル)
vec3 object_normal(vec3 pos){
float delta = 0.001;
    return normalize(vec3(
        object_d(pos + vec3(delta, 0.0, 0.0)) - object_d(pos - vec3(delta, 0.0, 0.0)),
        object_d(pos + vec3(0.0, delta, 0.0)) - object_d(pos - vec3(0.0, delta, 0.0)),
        object_d(pos + vec3(0.0, 0.0, delta)) - object_d(pos - vec3(0.0, 0.0, delta))
    ));
}



//3. Rayの定義 (構造体)
struct Ray{
    vec3 pos; //Rayの現在の座標
    vec3 dir; //Rayの進行方向
};

//12. 回転行列(X軸)
mat3 x_axis_rot(float angle){
    float c = cos(angle);
    float s = sin(angle);
    return mat3(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); 

}

//12. 回転行列(Y軸)
mat3 y_axis_rot(float angle){
    float c = cos(angle);
    float s = sin(angle);
    return mat3(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0,  c);
}

    
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, -8.0 + time * 5.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の進行方向はカメラの姿勢から求めることができる

  //13. 回転行列追加
   mat3 rot = x_axis_rot(mouse_pos.x) * y_axis_rot(mouse_pos.y);

    // 5. Rayの判定
    float t = 0.0, d;
    for (int i = 0; i < 64; i++ ){ //何回でもok 十分な数
        d = object_d(rot * ray.pos); //距離関数から現在の距離を求める
        if (d < 0.001) {  //計算できた距離が十分に0に近かったら
            break; //衝突したという判定
        } 
        t += d; //当たらなかったら
        ray.pos = camera_pos + t * ray.dir; //rayの座標更新 どれだけrayを進めるかというと最も近いオブジェクトまでの距離(突き抜け防止
    }
    
   //8. 光源が当たる方向を定義(z方向に光があたるように
//   vec3 light_dir = vec3(0.0, 0.0, 1.0);

   vec3 light_dir = vec3(- mouse_pos, 1.0); //マウスを光源としてみる
   vec3 normal = object_normal(rot * ray.pos);

   float l = dot(normal, - light_dir); //法線ベクトルと光源のベクトルの内積(dot)
    

    //6. あたったら色を変える 
   //9, 色を内積の値にする
    if (d < 0.001) {
        gl_FragColor = vec4(l, l, l, 1.0);
    } else {
        gl_FragColor = vec4(0);
    }
    
}

ここに貼り付けると動くと思います。

GLSLSandbox

※そこそこ負荷が高いので古いパソコンだと動かないかも、です。

アオキ
結構な行数になったけど、一つずつ抑えて覚えていけばなんとかなる、かな・・

『レイマーチング』入門 まとめ

先人のありがたい動画のおかげで、『レイマーチング(Ray Marching)』のたくさんの知識をまとめてえることができました。

もちろんまだまだわからないことがたくさんありはしますが、

『守破離』の精神で、まずはコピーしつつ解読してネタを増やしていきたいなと思います。

アオキ
GLSL(シェーディング)を自在に操れるようになればまさに魔法使いな気分になれそうですな。

『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】波のつくり方簡易まとめ。波もプログラムでつくれます【コピペスタイル】

 

アオキ
ツイッターでも記事ネタ含めちょろちょろ書いていくので、よろしければぜひフォローお願いしますm(_ _ )m

アオキのツイッターアカウント


関連記事一覧 (一部広告あり)

コメント

  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

CAPTCHA


最近の記事

アーカイブ

  1. バックエンド

    【Laravel第4弾】Vue.js3(CompositionAPI+Scrip…
  2. アート

    サイエンス×アート のおすすめ本【子どもと一緒に読みたい本】
  3. オンライン教材

    【JavaScript】初心者向けの動画をリリースしました【Udemy】
  4. オンライン教材

    【Vue.js】初心者向け講座をリリースしました。【Udemy】
  5. MENTA

    MENTA「メンタ」始めました
PAGE TOP