$30 off During Our Annual Pro Sale. View Details »

IFSによるフラクタルのモデリング

がむ
September 01, 2023

 IFSによるフラクタルのモデリング

レイトレ合宿9のセミナー用スライド②です。

Googleスライド
https://docs.google.com/presentation/d/1hqR1Rf-sfX2mTVOMR09xX9OUSwxk0aAXQwDJL6aZRHs/edit#slide=id.g27a8e85b88a_0_0

がむ

September 01, 2023
Tweet

More Decks by がむ

Other Decks in Programming

Transcript

  1. Transcendental Cubeについて
 • Transcendental Cube
 ‣ SESSIONS2023のGLSL Graphics Compo 2位🥈


    ‣ Run on twigl classic (300 es) mode
 
 • 映像: @gam0022(自分)
 • 音楽: @sadakkey さん
 2 YouTube

  2. 6 Consts, Global Variables and Utility Functions 定数、グローバル変数、便利な関数 Scene Modeling

    シーンのモデリング Lighting ライティング Timeline Sequence and Camera Work タイムラインとカメラ制御 Total 9270 chars
  3. シェーダーだけで映像を作る技術
 • twiglは2D用のシェーダー
 ‣ シェーダーの入力
 • スクリーン上の座標 + 時間
 ‣

    通常は3D描画はできない
 
 • レイマーチングという手法で3Dシーンを描画
 ‣ シェーダー内で3D空間やカメラを定義
 ‣ 距離関数(数式)で3Dシーンをモデリング
 9
  4. レイマーチングとは?
 11 float sdBox(vec3 p, vec3 b) { vec3 q

    = abs(p) - b; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } https://iquilezles.org/articles/distfunctions/
  5. レイマーチングとは?
 • レイマーチングを可視化したアニメーション by @kanetaaaaa 
 ‣ Raymarching in Raymarching

    
 ‣ https://twitter.com/kanetaaaaa/status/1141307706139004934 
 ‣ https://qiita.com/kaneta1992/items/21149c78159bd27e0860 
 12
  6. レイマーチングとは?
 • レイマーチングのアルゴリズム
 ‣ 距離関数だけレイを進めるのを繰り返す(マーチング・ループ)
 • 距離関数dが0に近似できたら衝突したと判定
 ‣ abs(d) <

    eps
 ‣ epsは0.001 など適切に設定
 • ループ回数がNを超えたら衝突していないと判定
 ‣ Nは100〜300くらいが一般的
 13 極めてシンプル

  7. 距離関数の操作 = 空間の操作
 • 直感と逆の操作になる 
 ‣ オブジェクト側ではなくて 空間の側を操作
 ‣

    空間はUVと読み替えると理解しやすい 
 ‣ https://www.shadertoy.com/view/sdySR3 
 
 • 2倍に拡大したい
 ‣ p *= 0.5;
 
 • 右に 1.0 移動したい 
 ‣ p.x -= 1.0;
 
 • Y軸にrだけ回転したい 
 ‣ p.xz = rot(-r) * p.xz; 
 14
  8. modによる繰り返し
 • pにmodを適用すると無限に繰り返しができる 
 ‣ p = mod(p, 1.0) -

    0.5; 
 
 • なぜ
 ‣ テクスチャのWrapモードのRepeatのような感じ 
 ‣ 上の例
 • -0.5〜0.5 の範囲の座標が永遠に繰り返される 
 15
  9. IFS(Iterated Function System)
 27 for (int i = 0; i

    < int(_IFS_Iteration); i++) { p1 = abs(p1 + _IFS_Offset.xyz) - _IFS_Offset.xyz; rot(p1.xz, TAU * _IFS_Rot.x); rot(p1.zy, TAU * _IFS_Rot.y); rot(p1.xy, TAU * _IFS_Rot.z); } opUnion(m, sdBox(p1, _IFS_BoxBase.xyz), SOL, roughness, 0.5); // ベース部分 opUnion(m, sdBox(p1, _IFS_BoxEmissive.xyz), SOL, roughness + boxEmi, hue); // 光る部分 forの中で適当に空間を操作 • 折りたたみ(abs) • 平行移動(±offset) • 回転(rot) IFSの実装自体はこれだけで簡単
  10. MengerSponge
 30 d = 0 (フラクタルの深度) n = 1(箱の個数) d

    = 1 n = 18 d = 2 n = 18^2 = 324 d = 3 n = 18^3 = 5832 フラクタルの深度を増やすと、箱の数が 指数関数的に増えてしまう🤯
  11. MengerSpongeを距離関数で表現
 32 float dMenger(float3 z0, float3 offset, float scale) {

    float4 z = float4(z0, 1.0); [loop] for (int n = 0; n < _MengerIteration; n++) { z = abs(z); if (z.x < z.y) z.xy = z.yx; if (z.x < z.z) z.xz = z.zx; if (z.y < z.z) z.yz = z.zy; z *= scale; z.xyz -= offset * (scale - 1.0); if (z.z < - 0.5 * offset.z * (scale - 1.0)) z.z += offset.z * (scale - 1.0); } return (length(max(abs(z.xyz) - float3(1.0, 1.0, 1.0), 0.0)) - 0.05) / z.w; } 箱の距離関数を1回だけ計算 空間の折りたたみ forループ数 = 再帰深度 再帰の深度を増やしても 線形にしか負荷が増えない
  12. MengerSpongeを距離関数で表現
 33 拡大 d = 10 n = 18^10 =

    3.5704672e+12 = 3.5兆個の箱 3.5兆個の箱でも、レイマーチングなら 60FPSで描画できる😎
  13. 折りたたみによるIFSのメリット
 IFSを実現するアプローチ
 
 1. オブジェクトを複製して配置
 a. ❌フラクタルの深度が増えると指数関数的にオブジェクト数が増加
 i. メモリや負荷が非現実的になる 


    
 1. 空間の折りたたみ
 b. レイマーチングで利用できるアプローチ
 c. 💯フラクタルの深度を増えても線形にしか負荷が増えない
 34
  14. IFS(Iterated Function System)
 • IFSの実装自体は簡単
 ‣ forの中で適当に空間操作するだけ
 
 • IFSのパラメーター調整は難しい


    ‣ 今回のIFSは6次元のパラメーター
 • 平行移動(vec3)+ 回転(vec3)
 • これは比較的少ない方なので、通常はさらに増えていく
 ‣ 6次元の広大なパラメーター空間から、良い絵が出せる住所を探す必要がある
 • 砂漠からオアシスを見つけるような作業🌴
 • 試行錯誤が必要
 35
  15. // Hash without Sine by David Hoskins. // https://www.shadertoy.com/view/4djSRW float

    hash12(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * .1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } // uvをfloorして利用 float emi = step(.5, hash12(floor(pos.yz) + 123.23 * floor(beat * 2.))); 壁面の演出
 42 ライティング計算 (Mad Tracing)用の乱数を再利用
  16. // 2DのIFSでマークを生成 vec4[] param_array = vec4[]( vec4(140., 72., 0., 0.),

    vec4(0., 184., 482, 0.), vec4(0., 0., 753., 0.), vec4(541., 156., 453., 0.), vec4(112., 0., 301., 0.), vec4(311., 172., 50., 0.), vec4(249., 40., 492., 0.), vec4(0.)); vec4 param = param_array[int(mod(dice * 33.01, 8.))] / vec2(1200., 675.).xyxy; vec2 p1 = p - param.xy; for (int i = 0; i < 3; i++) { p1 = abs(p1 + param.xy) - param.xy; rot(p1, TAU * param.z); } float d = sdBox(p1, vec2(.2, .05)); mark = saturate(smoothstep(0., .01, d)); 壁面の演出:警告
 45 IFSのパラメーターは配列で定義 2DのIFS ・折りたたみ ・平行移動 ・回転 マーク
  17. インスピレーションを得た作品①
 Life by @setchi
 2nd place at GLSL Graphics Compo,

    TokyoDemoFest 2018 
 
 [TDF2018] Life (FullHD 1080p) 
 
 シンプルなジオメトリーがだんだん複雑になっていく 
 このコンセプトを参考にした 
 49
  18. インスピレーションを得た作品②
 delight by mercury
 1st place at Combined Intro Compo,

    Under Construction 2015 
 
 delight - mercury | 64k | 60fps 
 
 
 Global Illuminationが綺麗 
 音楽との同期が素晴らしい 
 展開も良い
 
 50
  19. SESSIONS 2023とは?
 今年の4月末にC4 LAN 2023 SPRINGの会場および配信のハイブリッドで行われた デモパーティーです。
 
 デモパーティーとはコンピューターを用いた プログラミングやアートに興味のある人々が日本中・世界中から一堂に会し、デ

    モ作品のコンペティション (Compo) やセミナーなどが行われるイベントです。 
 
 パーティという名の通り、勉強会のように堅苦しい感じではなく、みんな賑やかに作品の制作過程を見せ合ったりと参加者同 士でのコミュニケーションが盛んに行われます。 
 
 SESSIONS in C4 LAN 2023 SPRING - Aftermovie 
 https://sessions.frontl1ne.net/ 
 
 54
  20. SESSIONS 2023とは?
 • 共通点:決められた制約の中で「すごい!」と思わせる
 ‣ RTA Japan
 • ゲームの縛りプレイやタイムアタック
 ‣

    デモパーティー
 • GLSL Graphics Compo
 • 容量制限の部門
 
 • シナジーがありそう
 ‣ 共同開催は良かった
 • デモシーンに興味を持ってくれる人が増えると嬉しい
 60
  21. SESSIONSレポート
 • CGWORLD
 ‣ 数キロバイトのプログラムに心血を注ぎ込む! デモシーンの魅力とコンペティション受賞作を紹 介〜SESSIONS in C4 LAN

    2023 SPRING(1) 
 ‣ VR空間でのクラブイベントを成功させるポイント&初心者でもわかるレイマーチング講座〜 SESSIONS in C4 LAN 2023 SPRING(2) 
 • C4 LAN
 ‣ 【C4 LAN】大量のゲーマーがゲーム機持参で勝手に遊ぶだけ。まあまあどうかしてるのに心地い い。ぬるま湯みたいな闇鍋イベントの生存戦略 | ゲーム・エンタメ最新情報のファミ通.com 
 ‣ ゲームイベントとしての認知拡大を目指し、変化を迎えた「C4 LAN 2023 SPRING」レポート | マイナ ビニュース
 64
  22. 質疑応答
 • 映像作るときってどんな映像を作るか、事前に絵コンテ的な物を作成したりもするのでしょうか? 
 ‣ 自分は映像を一人で作るような小規模チームなので絵コンテなどは作らないが、 
 規模の大きいチームなら作ることが多いと思う 
 •

    cpu,gpu,memory資源を節約したいときに、「この表現をしたい時はレイマーチングを使わない方が良いな」 といった判断基準は何かありますか? 
 ‣ 基本的にはポリゴンで描画した方が計算資源を使わない 
 ‣ 再帰深度が高いフラクタルはレイマーチングの方が適している 
 ‣ 雲などのボリュームレンダリングではゲームでもレイマーチングが使われている 
 • https://www.famitsu.com/news/201808/23162812.html 
 • 誰が最初にシェーダーだけで映像を作りだしたのか? 
 ‣ 自己紹介スライドで紹介した @iquilezles さんがBreakPoint 2009で発表した 
 「Elevated」という4KB容量制限のデモがおそらく初 
 ‣ ポリゴンによる手法よりも、レイマーチングの方が短いコードで要素を詰め込める 
 ‣ 容量制限のあるデモシーンではレイマーチングがよく使われる 
 ‣ https://nlab.itmedia.co.jp/games/articles/0904/30/news026.html 
 65
  23. Mad Tracing
 • Mad Tracing [Virgill 2018]
 71 End of

    time by Alcatraz & Altair | 1st Place at 4K Intro, Nordlicht 2018
  24. Path Tracing vs Mad Tracing
 • Path Tracing
 ‣ 物体表面に衝突してから確率的にサンプリング


    ‣ Bloomできない
 
 • Mad Tracing
 ‣ 物体表面に衝突する前から確率的にサンプリング
 ‣ Bloomできる
 77
  25. Mad Tracing
 78 void madtracer(vec3 ro1, vec3 rd1, float seed)

    { scol = vec3(0); vec2 rand = hash23(vec3(seed, iTime, iTime)) * .5; float t = rand.x, t2 = rand.y; vec4 m1, m2; vec3 rd2, ro2, nor2; for (int i = 0; i < 130; i++) { m1 = map(ro1 + rd1 * t, true); // t += m1.y == VOL ? 0.25 * abs(m1.x) + 0.0008 : 0.25 * m1.x; t += 0.25 * mix(abs(m1.x) + 0.0032, m1.x, m1.y); ro2 = ro1 + rd1 * t; nor2 = normal(ro2); rd2 = mix(reflect(rd1, nor2), hashHs(nor2, vec3(seed, i, iTime)), saturate(m1.z)); m2 = map(ro2 + rd2 * t2, true); // t2 += m2.y == VOL ? 0.15 * abs(m2.x) : 0.15 * m2.x; t2 += 0.15 * mix(abs(m2.x), m2.x, m2.y); scol += .015 * (pal(m2) * max(0., m2.z - 1.) + pal(m1) * max(0., m1.z - 1.)); // force disable unroll for WebGL 1.0 if (t < -1.) break; } } mapを2回参照 周囲の空間をサンプリング
  26. Mad Tracing
 79 void madtracer(vec3 ro1, vec3 rd1, float seed)

    { scol = vec3(0); vec2 rand = hash23(vec3(seed, iTime, iTime)) * .5; float t = rand.x, t2 = rand.y; vec4 m1, m2; vec3 rd2, ro2, nor2; for (int i = 0; i < 130; i++) { m1 = map(ro1 + rd1 * t, true); // t += m1.y == VOL ? 0.25 * abs(m1.x) + 0.0008 : 0.25 * m1.x; t += 0.25 * mix(abs(m1.x) + 0.0032, m1.x, m1.y); ro2 = ro1 + rd1 * t; nor2 = normal(ro2); rd2 = mix(reflect(rd1, nor2), hashHs(nor2, vec3(seed, i, iTime)), saturate(m1.z)); m2 = map(ro2 + rd2 * t2, true); // t2 += m2.y == VOL ? 0.15 * abs(m2.x) : 0.15 * m2.x; t2 += 0.15 * mix(abs(m2.x), m2.x, m2.y); scol += .015 * (pal(m2) * max(0., m2.z - 1.) + pal(m1) * max(0., m1.z - 1.)); // force disable unroll for WebGL 1.0 if (t < -1.) break; } } m1.x = distance distance < eps で break しない m2はPath Tracingの 衝突後のサンプリングも兼ねている
  27. Mad Tracing
 80 void madtracer(vec3 ro1, vec3 rd1, float seed)

    { scol = vec3(0); vec2 rand = hash23(vec3(seed, iTime, iTime)) * .5; float t = rand.x, t2 = rand.y; vec4 m1, m2; vec3 rd2, ro2, nor2; for (int i = 0; i < 130; i++) { m1 = map(ro1 + rd1 * t, true); // t += m1.y == VOL ? 0.25 * abs(m1.x) + 0.0008 : 0.25 * m1.x; t += 0.25 * mix(abs(m1.x) + 0.0032, m1.x, m1.y); ro2 = ro1 + rd1 * t; nor2 = normal(ro2); rd2 = mix(reflect(rd1, nor2), hashHs(nor2, vec3(seed, i, iTime)), saturate(m1.z)); m2 = map(ro2 + rd2 * t2, true); // t2 += m2.y == VOL ? 0.15 * abs(m2.x) : 0.15 * m2.x; t2 += 0.15 * mix(abs(m2.x), m2.x, m2.y); scol += .015 * (pal(m2) * max(0., m2.z - 1.) + pal(m1) * max(0., m1.z - 1.)); // force disable unroll for WebGL 1.0 if (t < -1.) break; } } レイのステップに係数を乗算 0.25 や 0.15 ステップを小さくして Volume Rendering
  28. Mad Tracing
 81 void madtracer(vec3 ro1, vec3 rd1, float seed)

    { scol = vec3(0); vec2 rand = hash23(vec3(seed, iTime, iTime)) * .5; float t = rand.x, t2 = rand.y; vec4 m1, m2; vec3 rd2, ro2, nor2; for (int i = 0; i < 130; i++) { m1 = map(ro1 + rd1 * t, true); // t += m1.y == VOL ? 0.25 * abs(m1.x) + 0.0008 : 0.25 * m1.x; t += 0.25 * mix(abs(m1.x) + 0.0032, m1.x, m1.y); ro2 = ro1 + rd1 * t; nor2 = normal(ro2); rd2 = mix(reflect(rd1, nor2), hashHs(nor2, vec3(seed, i, iTime)), saturate(m1.z)); m2 = map(ro2 + rd2 * t2, true); // t2 += m2.y == VOL ? 0.15 * abs(m2.x) : 0.15 * m2.x; t2 += 0.15 * mix(abs(m2.x), m2.x, m2.y); scol += .015 * (pal(m2) * max(0., m2.z - 1.) + pal(m1) * max(0., m1.z - 1.)); // force disable unroll for WebGL 1.0 if (t < -1.) break; } } Volume Renderingの アーティファクト防止のランダム値 Ray OriginのOffset
  29. Mad Tracing
 82 void madtracer(vec3 ro1, vec3 rd1, float seed)

    { scol = vec3(0); vec2 rand = hash23(vec3(seed, iTime, iTime)) * .5; float t = rand.x, t2 = rand.y; vec4 m1, m2; vec3 rd2, ro2, nor2; for (int i = 0; i < 130; i++) { m1 = map(ro1 + rd1 * t, true); // t += m1.y == VOL ? 0.25 * abs(m1.x) + 0.0008 : 0.25 * m1.x; t += 0.25 * mix(abs(m1.x) + 0.0032, m1.x, m1.y); ro2 = ro1 + rd1 * t; nor2 = normal(ro2); rd2 = mix(reflect(rd1, nor2), hashHs(nor2, vec3(seed, i, iTime)), saturate(m1.z)); m2 = map(ro2 + rd2 * t2, true); // t2 += m2.y == VOL ? 0.15 * abs(m2.x) : 0.15 * m2.x; t2 += 0.15 * mix(abs(m2.x), m2.x, m2.y); scol += .015 * (pal(m2) * max(0., m2.z - 1.) + pal(m1) * max(0., m1.z - 1.)); // force disable unroll for WebGL 1.0 if (t < -1.) break; } } コンパイル時間短縮のためのコード 定数のforだとunrollされてコンパイル時 間が伸びる 意味のないifによるダイナミックなループ にして、強制的にloopにする
  30. Mad Tracing
 メリット
 • Global Illuminationだけでなく
 Bloomもまとめて計算できる
 • 実装が短い
 


    デメリット
 • 負荷が高い
 ‣ 複雑なモデリング(距離関数)と組み合わせるのは厳しい
 83
  31. カメラワーク(専用カット)
 86 float prevEndTime = 0., t = 0.; #define

    TL(end) if (t = beat - prevEndTime, beat < (prevEndTime = end)) // 使い方 TL(8.) { ro = vec3(0, -1.36, -12.3 + t * .3); target = vec3(0., -2.2, 0.); fov = 100.; } else TL(16.) { ro = vec3(9.5, -1.36, -12.3 + t * .3); target = vec3(0., -2.2, 0.); fov = 100.; } else TL(20.) { ro = vec3(5.5, -5, -1.2); target = vec3(0., -8., -0.); fov = 100.0 + t; } // ... 続く • シーケンス制御 シンプルにifをたくさん並べているだけ • TLはifのマクロ tは各カットのローカル時間 • 秒単位ではなくbeat単位 音と同期するために 0-8 beat 8-16 beat 16-20 beat } } }