2019年9月5日 | ブログ記事

レイマーチ#とは

Fogrex

この記事はtraP夏のブログリレー2019 9月5日の記事です。

やあみんな!レイマーチって知ってる?

知らん人もいるでしょうね。
レイマーチはレイトレーシングの一種です。

違い

ラスタライズ法

いわゆる"ポリゴン"を二次元平面に投影する。一般的。3Dモデリングやる人はわかるはず。
Rasterize
この方式では曲面(例えば球)を正確に表現するためにはポリゴンを無限に細分化する必要があり、不可能。また、頂点が決定していないボリュームデータ(CT画像など、空間内の"場"の値で表現されたデータ)の可視化にはひと工夫[1]必要

レイマーチング法

視点に入ってくる光を逆算する。数式で物体を表現できるのでピクセルレベルで正確!!!!
ボリュームデータの表現が簡単!!!!激重
Raymarching

レイマーチング入門(glsl)

レイマーチングではカメラいっぱいに四角形のポリゴンを書き、フラグメントシェーダー(3Dモデルの見た目を制御するシェーダー)で記述する。
※画面上の位置からカメラの視点と方向を算出する方法はここでは説明しません

距離関数

物体を数式で表現する場合、物体表面からの距離を戻り値とする。半径1の球を描く。
なおボリュームデータを扱う場合は数式は用意せず、距離は固定長となる。

float sphere(in vec3 pos, in float r)
{
    return length(pos) - r;
}
// 物体の記述はmap関数で行う
float map(in vec3 pos)
{
    return sphere(pos, 1.0);
}

レイマーチ本体

物体表面までの距離を求めてその分だけレイを進行(マーチ)させる。
距離が一定値(0.0001)以下なら表面に到達したとして終了。

// レイの始点と方向を引数にする
vec4 rayCast(in vec3 origin, in vec3 dir)
{
    vec3 pos = origin;
    float dist;
    // 最大STEP回の計算をする
    for(int i=0;i<STEP;i++)
    {
        dist = map(pos);
        if(dist < 0.0001)
        {
            // 物体に到達したら白
            return vec4(1.0,1.0,1.0,1.0);
        }
        // レイをすすめる
        pos += dir * dist;
    }
    // 物体に到達しなかったら背景色(黒)
    return vec4(0.0,0.0,0.0,1.0);
}

これを実行しましょう。
WhiteSphere
真っ白ですね。
コード内では物体に到達したら白、そうでなければ黒、としか判断していませんね。
ここでLambert反射モデルを利用しましょう。
要は表面の法線と光の方向の内積を取り、環境光の分を足すものです

vec3 calcNorm(in vec3 pos)
{
    vec2 d = vec2(0.0001, 0.0);
    // 偏微分で法線を求める
    return normalize(vec3(
        map(pos + d.xyy) - map(pos - d.xyy),
        map(pos + d.yxy) - map(pos - d.yxy),
        map(pos + d.yyx) - map(pos - d.yyx)
    ) / d.x);
}
vec4 lambertLighting(in vec3 pos)
{
    // LIGHT_DIRは光の向き。-を掛けることで太陽方向へのベクトルになる。BASE は環境光。
    vec3 normal = calcNorm(pos);
    return vec4(vec3(clamp(dot(normal, -LIGHT_DIR), 0.0, 1.0) + BASE), 1.0);
}
vec4 rayCast(in vec3 origin, in vec3 dir)
{
    vec3 pos = origin;
    float dist;
    // 最大STEP回の計算をする
    for(int i=0;i<STEP;i++)
    {
        dist = map(pos);
        if(dist < 0.0001)
        {
            // 物体に到達したらライティング
            return lambertLighting(pos);
        }
        // レイをすすめる
        pos += dir * dist;
    }
    // 物体に到達しなかったら背景色(黒)
    return vec4(0.0,0.0,0.0,1.0);
}

LambertSphere
イイ感じにライティングされているので、立体感も出ています。

レイマーチング応用

距離関数の種類を増やし、数を増やし、オブジェクト同士の結合を行い、ライティングを適切にやればいろいろできます。
一例として僕の作品置いておきます。
※激重です。スマホやよわよわPCでは満足に見れません!
EggLikeMan
EggLikeMan
EggLikeMan2
レイマーチを複数回すれば反射や影もできます。
Reflect
楽しい!

まとめ

長くなったのでまとめです。レイマーチでは反射、屈折や、ボリュームデータ、数式が正確に表現できるので遊び甲斐があります。また近年の映画製作ではレイトレーシングがないと成り立たないので、CGに興味がある人は概要ぐらい知っておいても損はないと思います。

注釈


  1. 実はボリュームデータをラスタライズ法で表現する手段としてマーチングキューブズ法がある ↩︎

この記事を書いた人
Fogrex

シェーダーとVRやってます レイマーチ楽しい(゚∀。)

この記事をシェア

このエントリーをはてなブックマークに追加

関連する記事

2019年9月23日
ヒダッカソンに参加しました
oribe
2019年9月22日
ベクターレイヤーを使おう
Sigma1023
2019年9月21日
5,000円の液タブ(笑)を買ってみた。
Hinaruhi
2019年9月20日
Kotlin + GradleでReactアプリを作る
RLook
2019年9月19日
定理証明で遊ぼう(しなさい)
stretchybox
2019年9月18日
ノートはmacかwinか
Kejun

活動の紹介

カテゴリ

タグ