LoginSignup
1
0

More than 1 year has passed since last update.

jqでレイマーチング

Last updated at Posted at 2022-12-10

成果物

video1.gif

コード

def mod($d): fmod(.; $d) | if . < 0 then . + $d else . end;
def vec3Add($v): { x: (.x + $v.x), y: (.y + $v.y), z: (.z + $v.z) };
def vec3Mul($s): .[] *= $s;
def vec3Mod($s): .[] |= mod($s);
def vec3Sub($v): vec3Add($v | vec3Mul(-1));
def vec3Div($s): vec3Mul(1 / $s); 
def vec3Dot($v): .x * $v.x + .y * $v.y + .z * $v.z;
def vec3Length: vec3Dot(.) | sqrt;
def vec3Normalize: vec3Div(vec3Length);

def positionToSphereDistance($r): vec3Length - $r;

. as $t |

def positionToDistance:
  vec3Add({ x: (0.25 * $t), y: (0.125 * $t), z: (-0.5 * $t) }) |
  vec3Add({ x: 4, y: 4, z: 4 }) | vec3Mod(8) | vec3Add({ x: -4, y: -4, z: -4 }) |
  positionToSphereDistance(1.0);

def positionToNormal:
  positionToDistance as $dist |
  def e: 0.001;
  {
    x: ($dist - (.x -= e | positionToDistance)),
    y: ($dist - (.y -= e | positionToDistance)),
    z: ($dist - (.z -= e | positionToDistance))
  } |
  vec3Normalize;

({ x: -0.5, y: -0.5, z: 1 } | vec3Normalize) as $lightDirection |

def positionToLuminance:
  positionToNormal | vec3Dot($lightDirection);

def screenPositionToLuminance:
  def camPos: { x: 0, y: 0, z: 4 };
  (vec3Sub(camPos) | vec3Normalize) as $rayDir |
  def positionToLuminance(i): 
    positionToDistance as $dist |
    if i >= 32 then 0
    elif $dist < 0.0001 then positionToLuminance
    else vec3Add($rayDir | vec3Mul($dist)) | positionToLuminance(i + 1)
    end;
  camPos | positionToLuminance(0);

def luminanceToString:
  [0, ([., 1] | min)] | max |
  def grayscaleStrs: [" ", ".", ":", "+", "o", "0"];
  grayscaleStrs[(grayscaleStrs | length - 0.001) * . | floor];

def canvasToText: [.[] | [.[] | luminanceToString] | add] | join("\n");

def canvasSize: { x: 64, y: 32 };

def canvas:
  [
    range(canvasSize.y) | (2 * (. / canvasSize.y) - 1) as $y | [
      range(canvasSize.x) |
      { x: (2 * (. / canvasSize.x) - 1), $y, z: 0 } | screenPositionToLuminance
    ]
  ];

canvas | canvasToText

を、raymarching.jq として保存し、シェルスクリプトから

for t in $(jq -nr 'range(1024)'); do echo $t | jq -fr raymarching.jq; done

で呼び出す。

全体の流れ

  • raymarching.jq は、時間を受け取りそれに対応するアスキーアートを返す
    • 時間は . as $t としてしまい、以降ファイル内どこでも参照可能にしている
  • canvas で明るさを0~1で表した二次元配列を生成し、それを canvasToText でアスキーアートに変換しそれを結果としている
  • 画面位置ごとの明るさは screenPositionToLuminance で求める
  • レイの進行は positionToLuminance の再帰
    • 新しい位置で positionToLuminance を再度呼び、距離が一定以下になったら再帰をやめている

その他

  • 定数は、defas $var を使う
    • 計算があるものは as $var のほうが良い?
  • fmod は負のときに返り値も負になるのでGLSLと同じで常に正になるよう、mod を用意した
  • | を行頭か行末どちらに書くか迷うが、関数先頭など、記述不要箇所でも自動でパイプを受け取るのであえて強調する必要性が低そうなので末尾にした
  • 関数はフィルタが基本になるので、hogeToFuga みたいな関数名にすると分かりやすい気がする

参考

1
0
0

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
  3. You can use dark theme
What you can do with signing up
1
0