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. Transcendental Cube
    グラフィックス解説

    2023-05-25 SESSIONS 2023 AFTER PARTY@teamLab Office
    @gam0022 / Sho HOSODA

    View full-size slide

  2. 自己紹介

    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年 ボーンデジタル出版

    著者のひとりとして参加

    商業誌でレイマーチングを解説する実績を解禁
    🏆


    View full-size slide

  3. Transcendental Cubeについて

    • Transcendental Cube

    ‣ SESSIONS2023のGLSL Graphics Compo 2位
    🥈

    ‣ Run on twigl classic (300 es) mode



    • Music by @sadakkey

    3

    View full-size slide

  4. ダイジェスト版の動画
    4

    View full-size slide

  5. アジェンダ
    • ライティング

    • モデリング

    • 壁面の演出

    • カメラワーク

    • おわりに

    5

    View full-size slide

  6. アジェンダ
    • ライティング

    • モデリング

    • 壁面の演出

    • カメラワーク

    • おわりに

    6

    View full-size slide

  7. シェーディング

    7
    Global Illumination
    ・Area Light
    ・Material with a blit roughness
    Bloom + Light Beam

    View full-size slide

  8. ライティング

    ライティングで目指したもの⛳


    • Global Illumination

    • Bloom

    8
    GLSL Graphics Compoでは
    1Pass実装が必須
    Mad Tracingで実現できた!

    View full-size slide

  9. Mad Tracing

    • Mad Tracing [Virgill 2018]

    9
    End of time by Alcatraz & Altair | 1st Place at 4K Intro, Nordlicht 2018

    View full-size slide

  10. Mad Tracing

    ざっくり言うと…


    • Path Tracingの亜種

    • Bloomもまとめて計算できる

    10

    View full-size slide

  11. Path Tracing

    • Global Illuminationが可能なレンダリング手法

    • BRDFに応じて確率的に反射方向をサンプリング、光源からの影響を計算

    ‣ レイを大量に飛ばす💪(モンテカルロ法でレンダリング方程式を数値的に積分) 

    11
    An illustration of reflection from a glossy surface. | Download Scientific Diagram

    View full-size slide

  12. Path Tracing

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

    View full-size slide

  13. Raymarching

    • レイマーチングのループ

    13
    ro1
    ro = RayOrigin
    rd1
    rd = RayDirection

    View full-size slide

  14. Mad Tracing

    • マーチングループ中に周囲の空間をサンプリング

    ‣ レイの周囲の光源の影響を蓄積することでBloomを実現

    14
    ro1
    rd1
    ro2’’’
    rd2’’’
    rd2’’
    rd2’
    rd2
    ro2’’
    ro2’
    ro2

    View full-size slide

  15. Path Tracing vs Mad Tracing

    • Path Tracing

    ‣ 物体表面に衝突してから確率的にサンプリング

    ‣ Bloomできない


    • Mad Tracing

    ‣ 物体表面に衝突する前から確率的にサンプリング

    ‣ Bloomできる

    15

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  18. 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

    View full-size slide

  19. 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

    View full-size slide

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

    View full-size slide

  21. Mad Tracing

    メリット

    • Global Illuminationだけでなく

    Bloomもまとめて計算できる

    • 実装が短い


    デメリット

    • 負荷が高い

    ‣ 複雑なモデリング(距離関数)と組み合わせるのは厳しい

    21

    View full-size slide

  22. アジェンダ
    • ライティング

    • モデリング

    • 壁面の演出

    • カメラワーク

    22

    View full-size slide

  23. モデリング

    シンプルなCubeをIFS(Iterated Function System)で複雑にした

    23
    Cube IFS-ed Cube

    View full-size slide

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

    View full-size slide

  25. IFS(Iterated Function System)

    SDF for raymarching (距離関数のスキル) by @gaziya5


    25

    View full-size slide

  26. IFS(Iterated Function System)

    • IFSの実装自体は簡単

    ‣ forの中で適当に空間操作するだけ


    • IFSのパラメーター調整は難しい

    ‣ 今回のIFSは6次元のパラメーター

    • 平行移動(vec3)+ 回転(vec3)

    • これは比較的少ない方なので、通常はさらに増えていく

    ‣ 6次元の広大なパラメーター空間から、良い絵が出せる住所を探す必要がある

    • 砂漠からオアシスを見つけるような作業🌴

    • 試行錯誤が必要

    26

    View full-size slide

  27. モデリングの試行錯誤

    Unityでパラメーター調整してからGLSLに移植

    • UnityのInspectorの方がパラメーターの調整がしやすい

    • カメラも操作しやすい

    27
    Unity GLSL(Shadertoy)

    View full-size slide

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

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

    28

    View full-size slide

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

    29

    View full-size slide

  30. 情報量のバランスを考慮して最終調整

    • Mad Tracingによってライティングの情報量が多い

    • ジオメトリーの情報量は抑えた方がバランスが良さそう🤔と考えた

    ‣ 両方情報量MAXだと、GPU負荷、動画のビットレートなども厳しい…

    30

    View full-size slide

  31. IFSのバリエーション

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

    31

    View full-size slide

  32. アジェンダ
    • ライティング

    • モデリング

    • 壁面の演出

    • カメラワーク

    • おわりに

    32

    View full-size slide

  33. 壁面の演出

    部屋のジオメトリーも超シンプル(装飾のない完全な直方体) 

    床や壁がシンプルにすることで反射を活かしたかった 

    壁面のEmissiveのパターンによって情報量を増やした 

    33

    View full-size slide

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

    View full-size slide

  35. 壁面の演出:警告

    35

    View full-size slide

  36. ダイジェスト版の動画
    36

    View full-size slide

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

    View full-size slide

  38. アジェンダ
    • ライティング

    • モデリング

    • 壁面の演出

    • カメラワーク

    • おわりに

    38

    View full-size slide

  39. カメラワーク

    汎用カット:2パターンのカメラワークをランダムに選択

    • Cubeを中心に回転する軌道カメラ(乱数バリエーションあり)

    • CubeをLookAtする定点カメラ(乱数バリエーションあり)


    専用カット

    • 序盤

    • 終盤

    • 壁面に注目するカット

    39
    汎用カットを活用することで
    コードを圧縮

    View full-size slide

  40. カメラワーク(専用カット)

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

    View full-size slide

  41. アジェンダ
    • ライティング

    • モデリング

    • 壁面の演出

    • カメラワーク

    • おわりに

    41

    View full-size slide

  42. インスピレーションを得た作品①

    Life by @setchi

    2nd place at GLSL Graphics Compo, TokyoDemoFest 2018


    [TDF2018] Life (FullHD 1080p)


    シンプルなジオメトリーがだんだん複雑になっていく

    このコンセプトを参考にした

    42

    View full-size slide

  43. インスピレーションを得た作品②

    delight by mercury

    1st place at Combined Intro Compo, Under Construction 2015 


    delight - mercury | 64k | 60fps 



    Global Illuminationが綺麗 

    音楽との同期が素晴らしい 

    展開も良い


    43

    View full-size slide

  44. インスピレーションを得た作品③

    Delayed by kaneta

    #Shader1weekCompo #S1C001 neort


    壁面のパターンが床に反射するのがカッコいいので参考にした 

    44

    View full-size slide

  45. インスピレーションを得た作品④

    新世紀エヴァンゲリオンの演出

    45

    View full-size slide

  46. おわりに

    • みなさんの制作の参考になったら嬉しいです

    • レイマーチングはいいぞ!

    46

    View full-size slide