Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Transcendental Cube / グラフィックス解説

Transcendental Cube / グラフィックス解説

SESSIONS 2023 AFTER PARTY@teamLab Officeの発表資料です。
https://teamlab.connpass.com/event/282028/

アニメーション対応版(Googleスライド)はこちらです。
https://docs.google.com/presentation/d/1uU2ivgBEPPYcUbj7crjDQA4_rABf4xBZczDeL3VKHlw/edit?usp=sharing

がむ

May 25, 2023
Tweet

More Decks by がむ

Other Decks in Programming

Transcript

  1. 自己紹介
 2 @gam0022(がむ)/ Sho HOSODA
 グラフィックスエンジニア/Unityでモバイルゲームを開発
 WORMHOLE by @gam0022 &

    @sadakkey 
 1st place in Combined Demo Compo at Tokyo Demo Fest 2018 
 Made with Unity 
 RE: SIMULATED by @gam0022 & @sadakkey 
 1st place in PC 64K Intro at Revision 2020 
 Original WebGL Framework 
 Unityゲーム プログラミング・バイブル 2nd Generation 
 2021年 ボーンデジタル出版
 著者のひとりとして参加
 商業誌でレイマーチングを解説する実績を解禁 🏆

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

    🥈
 ‣ Run on twigl classic (300 es) mode 
 
 
 • Music by @sadakkey
 3
  3. Mad Tracing
 • Mad Tracing [Virgill 2018]
 9 End of

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


    ‣ Bloomできない
 
 • Mad Tracing
 ‣ 物体表面に衝突する前から確率的にサンプリング
 ‣ Bloomできる
 15
  5. Mad Tracing
 16 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回参照 周囲の空間をサンプリング
  6. Mad Tracing
 17 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はPathTracingの 衝突後のサンプリングも兼ねている
  7. Mad Tracing
 18 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
  8. Mad Tracing
 19 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
  9. Mad Tracing
 20 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にする
  10. Mad Tracing
 メリット
 • Global Illuminationだけでなく
 Bloomもまとめて計算できる
 • 実装が短い
 


    デメリット
 • 負荷が高い
 ‣ 複雑なモデリング(距離関数)と組み合わせるのは厳しい
 21
  11. IFS(Iterated Function System)
 24 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の実装自体はこれだけで簡単
  12. IFS(Iterated Function System)
 • IFSの実装自体は簡単
 ‣ forの中で適当に空間操作するだけ
 
 • IFSのパラメーター調整は難しい


    ‣ 今回のIFSは6次元のパラメーター
 • 平行移動(vec3)+ 回転(vec3)
 • これは比較的少ない方なので、通常はさらに増えていく
 ‣ 6次元の広大なパラメーター空間から、良い絵が出せる住所を探す必要がある
 • 砂漠からオアシスを見つけるような作業🌴
 • 試行錯誤が必要
 26
  13. // Mad Tracing用の乱数を再利用 // 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.))); 壁面の演出
 34
  14. // 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)); 壁面の演出:警告
 37 IFSのパラメーターは配列で定義 2DのIFS 平行移動と回転
  15. カメラワーク(専用カット)
 40 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 } } }
  16. インスピレーションを得た作品①
 Life by @setchi
 2nd place at GLSL Graphics Compo,

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

    Under Construction 2015 
 
 delight - mercury | 64k | 60fps 
 
 
 Global Illuminationが綺麗 
 音楽との同期が素晴らしい 
 展開も良い
 
 43