Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2892

GLSLのレイマーチングをAWKで実装したらどんな感じか

$
0
0

生成物は以下。

ray_marching.png

GLSLをAWKで実装したらどんな感じになるのか、以下の点から気になったためやってみる。

  • ピクセル単位の処理をするGLSLが、行単位の処理に特化したAWKにどう書き換わるか?
  • レイマーチングはシンプルな考えで実現されているが、GLSL以外でもシンプルに実装できるか?
  • AWKは制約が大きい言語だが、自然に実装できるか?
  • どれほどの速度が出るか

GLSLのレイマーチング自体は、

を参考にさせて頂く。

全貌として、

  • ImageMagickのテキストから画像データへの変換を利用する
  • Bashで1行にピクセルでのx、y値を指定したデータをAWKに渡す
  • AWKでは、1行に対応して、ImageMagickが解釈できる1ピクセルを出力していく
  • 1フレ分の画像データだけを出力する(動画を生成したかったが遅すぎて断念)
#!bash
size=256
{
    echo "# ImageMagick pixel enumeration: $size,$size,255,srgb"
    for y in $(seq 0 $((size - 1))); do
        for x in $(seq 0 $((size - 1))); do
            echo $x $y;
        done;
    done | awk -v size=$size -f ray_marching.awk
} | convert - ray_marching.png

AWKで気を付ける必要がある点は以下。

  • ローカル変数は、引数の最後に複数スペースを置いて引数として同様に並べる
  • returnで配列は返せない、なので第一引数を利用する
function vec2(v, x, y) {
    v[0] = x;
    v[1] = (y == "") ? x : y;
}

function vec3(v, x, y, z) {
    v[0] = x;
    v[1] = (y == "") ? x : y;
    v[2] = (z == "") ? x : z;
}

function vec3Clone(v, v0) {
    vec3(v, v0[0], v0[1], v0[2]);
}

function vec3Length(v) {
    return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

function vec3Add(v, v0) {
    vec3(v, v[0] + v0[0], v[1] + v0[1], v[2] + v0[2]);
}

function vec3Sub(v, v0) {
    vec3(v, v[0] - v0[0], v[1] - v0[1], v[2] - v0[2]);
}

function vec3Mul(v, s) {
    vec3(v, v[0] * s, v[1] * s, v[2] * s);
}

function vec3Mod(v, s) {
    vec3(v,
        v[0] % s + (v[0] < 0 ? s : 0),
        v[1] % s + (v[1] < 0 ? s : 0),
        v[2] % s + (v[2] < 0 ? s : 0));
}

function vec3Dot(v0, v1) {
    return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2];
}

function vec3Clamp(v, min, max) {
    vec3(v,
        (v[0] < min) ? 0 : ((v[0] > max) ? max : v[0]),
        (v[1] < min) ? 0 : ((v[1] > max) ? max : v[1]),
        (v[2] < min) ? 0 : ((v[2] > max) ? max : v[2]));
}

function vec3Normalize(v,  l) {
    l = vec3Length(v);
    if (l == 0.0) {
        vec3(v, 0.0);
        return;
    }
    vec3(v, v[0] / l, v[1] / l, v[2] / l);
}

function getDistance(pos, size,  p, vs) {
    vec3Clone(p, pos);
    vec3Mod(p, 6);
    vec3(vs, 2.0);
    vec3Sub(p, vs);
    return vec3Length(p) - size;
}

function getNormal(v, pos, size,  ep, v0, v1, v2, baseDistance) {
    ep = 0.0001;
    vec3(v0, pos[0] - ep, pos[1], pos[2]);
    vec3(v1, pos[0], pos[1] - ep, pos[2]);
    vec3(v2, pos[0], pos[1], pos[2] - ep);
    baseDistance = getDistance(pos, size);
    vec3(v,
        baseDistance - getDistance(v0, size),
        baseDistance - getDistance(v1, size),
        baseDistance - getDistance(v2, size));
    vec3Normalize(v);
}

BEGIN {
    vec3(lightDir, 1.0);
    vec2(resolution, size);
}

{
    vec2(fragCoord, $2, $1);
    vec2(pos,
        (fragCoord[0] * 2.0 - resolution[0]) / resolution[1],
        (fragCoord[1] * 2.0 - resolution[1]) / resolution[1]);
    vec3(col, 0);
    vec3(cameraPos, 0.0, 0.0, 10.0);
    vec3(ray, pos[0], pos[1], 0.0);
    vec3Sub(ray, cameraPos);
    vec3Normalize(ray);
    vec3Clone(cur, cameraPos);
    size = 0.75;
    for (i = 0; i < 99; ++i) {
        d = getDistance(cur, size);
        if (d < 0.0001) {
            getNormal(normal, cur, size);
            dot = vec3Dot(normal, lightDir);
            vec3(col, dot);
            vec3(ambient, 1.0, 0.7, 0.2);
            vec3Add(col, ambient);
            break;
        }
        vec3Clone(delta, ray);
        vec3Mul(delta, d);
        vec3Add(cur, delta);
    }
    vec3Clamp(col, 0.0, 1.0);
    printf "%d,%d: (%d,%d,%d)\n",
        $2, $1,
        int(col[0] * 255.99), int(col[1] * 255.99), int(col[2] * 255.99)
}

感想は以下。

  • レイマーチングはGLSL以外でもシンプルに実現できる
  • AWKの制約はあるものの、それでもある程度自然なコードで実現できる
  • 1フレ256x256を生成するだけでも数分掛かり非常に遅い

Viewing all articles
Browse latest Browse all 2892

Trending Articles