Slide 1

Slide 1 text

Transcendental Cube グラフィックス解説
 2023-05-25 SESSIONS 2023 AFTER PARTY@teamLab Office @gam0022 / Sho HOSODA

Slide 2

Slide 2 text

自己紹介
 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年 ボーンデジタル出版
 著者のひとりとして参加
 商業誌でレイマーチングを解説する実績を解禁 🏆


Slide 3

Slide 3 text

Transcendental Cubeについて
 • Transcendental Cube
 ‣ SESSIONS2023のGLSL Graphics Compo 2位 🥈
 ‣ Run on twigl classic (300 es) mode 
 
 
 • Music by @sadakkey
 3

Slide 4

Slide 4 text

ダイジェスト版の動画 4

Slide 5

Slide 5 text

アジェンダ • ライティング
 • モデリング
 • 壁面の演出
 • カメラワーク
 • おわりに
 5

Slide 6

Slide 6 text

アジェンダ • ライティング
 • モデリング
 • 壁面の演出
 • カメラワーク
 • おわりに
 6

Slide 7

Slide 7 text

シェーディング
 7 Global Illumination ・Area Light ・Material with a blit roughness Bloom + Light Beam

Slide 8

Slide 8 text

ライティング
 ライティングで目指したもの⛳
 
 • Global Illumination
 • Bloom
 8 GLSL Graphics Compoでは 1Pass実装が必須 Mad Tracingで実現できた!

Slide 9

Slide 9 text

Mad Tracing
 • Mad Tracing [Virgill 2018]
 9 End of time by Alcatraz & Altair | 1st Place at 4K Intro, Nordlicht 2018

Slide 10

Slide 10 text

Mad Tracing
 ざっくり言うと…
 
 • Path Tracingの亜種
 • Bloomもまとめて計算できる
 10

Slide 11

Slide 11 text

Path Tracing
 • Global Illuminationが可能なレンダリング手法
 • BRDFに応じて確率的に反射方向をサンプリング、光源からの影響を計算
 ‣ レイを大量に飛ばす💪(モンテカルロ法でレンダリング方程式を数値的に積分) 
 11 An illustration of reflection from a glossy surface. | Download Scientific Diagram

Slide 12

Slide 12 text

Path Tracing
 12 https://www.youtube.com/watch?v=1Ji21iGW4nY レイを複数方向に サンプリングすることで ブラー(ぼかし)を実現

Slide 13

Slide 13 text

Raymarching
 • レイマーチングのループ
 13 ro1 ro = RayOrigin rd1 rd = RayDirection

Slide 14

Slide 14 text

Mad Tracing
 • マーチングループ中に周囲の空間をサンプリング 
 ‣ レイの周囲の光源の影響を蓄積することでBloomを実現 
 14 ro1 rd1 ro2’’’ rd2’’’ rd2’’ rd2’ rd2 ro2’’ ro2’ ro2

Slide 15

Slide 15 text

Path Tracing vs Mad Tracing
 • Path Tracing
 ‣ 物体表面に衝突してから確率的にサンプリング
 ‣ Bloomできない
 
 • Mad Tracing
 ‣ 物体表面に衝突する前から確率的にサンプリング
 ‣ Bloomできる
 15

Slide 16

Slide 16 text

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回参照 周囲の空間をサンプリング

Slide 17

Slide 17 text

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の 衝突後のサンプリングも兼ねている

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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にする

Slide 21

Slide 21 text

Mad Tracing
 メリット
 • Global Illuminationだけでなく
 Bloomもまとめて計算できる
 • 実装が短い
 
 デメリット
 • 負荷が高い
 ‣ 複雑なモデリング(距離関数)と組み合わせるのは厳しい
 21

Slide 22

Slide 22 text

アジェンダ • ライティング
 • モデリング
 • 壁面の演出
 • カメラワーク
 22

Slide 23

Slide 23 text

モデリング
 シンプルなCubeをIFS(Iterated Function System)で複雑にした
 23 Cube IFS-ed Cube

Slide 24

Slide 24 text

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の実装自体はこれだけで簡単

Slide 25

Slide 25 text

IFS(Iterated Function System)
 SDF for raymarching (距離関数のスキル) by @gaziya5
 
 25

Slide 26

Slide 26 text

IFS(Iterated Function System)
 • IFSの実装自体は簡単
 ‣ forの中で適当に空間操作するだけ
 
 • IFSのパラメーター調整は難しい
 ‣ 今回のIFSは6次元のパラメーター
 • 平行移動(vec3)+ 回転(vec3)
 • これは比較的少ない方なので、通常はさらに増えていく
 ‣ 6次元の広大なパラメーター空間から、良い絵が出せる住所を探す必要がある
 • 砂漠からオアシスを見つけるような作業🌴
 • 試行錯誤が必要
 26

Slide 27

Slide 27 text

モデリングの試行錯誤
 Unityでパラメーター調整してからGLSLに移植
 • UnityのInspectorの方がパラメーターの調整がしやすい
 • カメラも操作しやすい
 27 Unity GLSL(Shadertoy)

Slide 28

Slide 28 text

モデリングの試行錯誤(制作初期)
 制作初期(4/15)は複雑なジオメトリーだったが、最終的にはシンプルに落ち着いた 
 28

Slide 29

Slide 29 text

モデリングの試行錯誤(制作初期)
 29

Slide 30

Slide 30 text

情報量のバランスを考慮して最終調整
 • Mad Tracingによってライティングの情報量が多い
 • ジオメトリーの情報量は抑えた方がバランスが良さそう🤔と考えた
 ‣ 両方情報量MAXだと、GPU負荷、動画のビットレートなども厳しい…
 30

Slide 31

Slide 31 text

IFSのバリエーション
 中盤の複雑なIFSのシーンもパラメーター調整のみで実現 
 31

Slide 32

Slide 32 text

アジェンダ • ライティング
 • モデリング
 • 壁面の演出
 • カメラワーク
 • おわりに
 32

Slide 33

Slide 33 text

壁面の演出
 部屋のジオメトリーも超シンプル(装飾のない完全な直方体) 
 床や壁がシンプルにすることで反射を活かしたかった 
 壁面のEmissiveのパターンによって情報量を増やした 
 33

Slide 34

Slide 34 text

// 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

Slide 35

Slide 35 text

壁面の演出:警告
 35

Slide 36

Slide 36 text

ダイジェスト版の動画 36

Slide 37

Slide 37 text

// 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 平行移動と回転

Slide 38

Slide 38 text

アジェンダ • ライティング
 • モデリング
 • 壁面の演出
 • カメラワーク
 • おわりに
 38

Slide 39

Slide 39 text

カメラワーク
 汎用カット:2パターンのカメラワークをランダムに選択
 • Cubeを中心に回転する軌道カメラ(乱数バリエーションあり)
 • CubeをLookAtする定点カメラ(乱数バリエーションあり)
 
 専用カット
 • 序盤
 • 終盤
 • 壁面に注目するカット
 39 汎用カットを活用することで コードを圧縮

Slide 40

Slide 40 text

カメラワーク(専用カット)
 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 } } }

Slide 41

Slide 41 text

アジェンダ • ライティング
 • モデリング
 • 壁面の演出
 • カメラワーク
 • おわりに
 41

Slide 42

Slide 42 text

インスピレーションを得た作品①
 Life by @setchi
 2nd place at GLSL Graphics Compo, TokyoDemoFest 2018 
 
 [TDF2018] Life (FullHD 1080p) 
 
 シンプルなジオメトリーがだんだん複雑になっていく 
 このコンセプトを参考にした 
 42

Slide 43

Slide 43 text

インスピレーションを得た作品②
 delight by mercury
 1st place at Combined Intro Compo, Under Construction 2015 
 
 delight - mercury | 64k | 60fps 
 
 
 Global Illuminationが綺麗 
 音楽との同期が素晴らしい 
 展開も良い
 
 43

Slide 44

Slide 44 text

インスピレーションを得た作品③
 Delayed by kaneta
 #Shader1weekCompo #S1C001 neort
 
 壁面のパターンが床に反射するのがカッコいいので参考にした 
 44

Slide 45

Slide 45 text

インスピレーションを得た作品④
 新世紀エヴァンゲリオンの演出
 45

Slide 46

Slide 46 text

おわりに
 • みなさんの制作の参考になったら嬉しいです
 • レイマーチングはいいぞ!
 46