search
LoginSignup
19

More than 5 years have passed since last update.

posted at

updated at

Organization

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

前回の記事から

レイマーチングのテンプレートシートを作りました.

では、これから

GLSLについて、研究していこうと思います.
まずは、形から、研究していこうと思います.

距離関数編

参考記事:modeling with distance functions

図形の関数をまとめた海外のサイトがあります.

この図形の形についてまとめていこうと思います.

まず、距離関数って?

数学の距離関数

距離関数とググると、数学の距離関数が出てきます。

数学の距離関数とは、

「距離」の定義を満たす関数

のことです.

数学には、距離と呼ばれるものが、sqrt(p.x*p.x+p.y*p.y+p.z*p.z) で以外にいろいろあります。

それで、その距離の性質を満たす関数を距離関数と言います。

※混乱しない程度にご参照ください。

では、Shaderでいう距離関数とは

恐らく,

中心からそれぞれのピクセルまでの距離を計算した関数という意味

で,距離関数とつけられています.

その上で、それぞれの図形の中心からそれぞれのピクセルまでの距離を計算したことで、この形の距離関数と言っていると思われます。

ということで、前置きはここまでにして、距離関数の記事を書きます。

ちなみに、前回のチートシートをのってけて置きます。

// ============================================================================
// レイマーチングチートシート
// ============================================================================

precision mediump float;
// 解像度 (512.0, 512.0)
uniform vec2  resolution;   
// mouse (-1.0 ~ 1.0)
uniform vec2  mouse;        
// time (1second == 1.0)
uniform float time;         
// previous scene texture
uniform sampler2D prevScene;

// 図形の距離関数
float dGraphic(vec3 p){
    // ここに図形の距離関数を書く
        /*
        今回はここをいじります。
        */
    return length(p) - 0.5;
}

// 床の距離関数
float dFloor(vec3 p){
    // ここに床の距離関数を書く
    return dot(p, vec3(0.0, 1.0, 0.0)) + 1.0;
}

// 二つの図形を合成するの距離関数
float distanceHub(vec3 p){
    // 二つの図形の描く距離関数を書く
    return min(dGraphic(p), dFloor(p));
}

// 法線
vec3 genNormal(vec3 p){
    float d = 0.001;
    // 法線を生成
    return normalize(vec3(
        distanceHub(p + vec3(  d, 0.0, 0.0)) - distanceHub(p + vec3( -d, 0.0, 0.0)),
        distanceHub(p + vec3(0.0,   d, 0.0)) - distanceHub(p + vec3(0.0,  -d, 0.0)),
        distanceHub(p + vec3(0.0, 0.0,   d)) - distanceHub(p + vec3(0.0, 0.0,  -d))
    ));
}

// カメラのワーク
void main(){
    // スクリーンスペースを考慮して座標を正規化
    vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
    // カメラを定義
    vec3 cPos         = vec3(0.0,  0.0,  3.0); // カメラの位置
    vec3 cDir         = vec3(0.0,  0.0, -1.0); // カメラの向き(視線)
    vec3 cUp          = vec3(0.0,  1.0,  0.0); // カメラの上方向
    vec3 cSide        = cross(cDir, cUp);      // 外積を使って横方向を算出
    float targetDepth = 1.0;                   // フォーカスする深度

    // カメラの情報からレイを定義
    vec3 ray = normalize(cSide * p.x + cUp * p.y + cDir * targetDepth);
    // マーチングループを組む
    float dist = 0.0;  // レイとオブジェクト間の最短距離
    float rLen = 0.0;  // レイに継ぎ足す長さ
    vec3  rPos = cPos; // レイの先端位置(初期位置)

    // レイが進む処理(マーチングループ)
    for(int i = 0; i < 32; ++i){
        dist = distanceHub(rPos);
        rLen += dist;
        rPos = cPos + ray * rLen;
    }

    // レイとオブジェクトの距離を確認
    if(abs(dist) < 0.001){
        // 法線を算出
        vec3 normal  = genNormal(rPos);
        // ライトベクトルの定義(マウスの影響を受けるように)
        vec3 light   = normalize(vec3(mouse + 1.0, 1.0));
        // ライトベクトルとの内積を取る
        float diff   = max(dot(normal, light), 0.1);
        // diffuse を出力する
        gl_FragColor = vec4(vec3(diff), 1.0);
    }else{
        // 衝突しなかった場合はそのまま黒
        gl_FragColor = vec4(vec3(0.0), 1.0);
    }
}

Sphere(球)

これは、一番基本的な距離関数だと思います.
式で書くと

p.x*p.x+p.y*p.y+p.z*p.z = s*s, s(半径)

です.

実際、

float sdSphere( vec3 p, float s )
{
  return length(p)-s;
}

と記事には載っていて、length(p) は、 sqrt(p.x*p.x+p.y*p.y+p.z*p.z) のことを指していて, s は半径をさしています.

Sphere(球)の関数をいじる.

Sphere関数は、

sqrt(p.x*p.x+p.y*p.y+p.z*p.z) - s

sphere.PNG

なので、p.x*p.x+p.y*p.y+p.z*p.z をいじってみよう!

双曲線にしてみる.

二次元の円 p.x*p.x+p.y*p.y の式の親戚に 双曲線 p.x*p.x-p.y*p.y という物があります。

レイマーチングは、3次元なので、式として、p.x*p.x-p.y*p.y+p.z*p.z , p.x*p.x+p.y*p.y-p.z*p.z, p.x*p.x-p.y*p.y-p.z*p.z のパタンが考えられて、それぞれの平面での双曲線が考えられます.

hyperbola01.PNG

hyperbola02.PNG

y軸に三角関数を入れてみる。

p.x*p.x+sin(p.y*p.y)+p.z*p.z

angelRing01.PNG

天使の輪みたいなのが出来ました。

y軸に三角関数を入れてみる_アニメーションつける

p.x*p.x+sin(p.y*p.y+time)+p.z*p.z

ring.gif

glslsandboxにあげてみる

次は、Boxやります.

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
19