Renard's Blog

かきます

『WING IT!!!』MV解説

『WING IT!!!』MV解説

これは何

  • 2025/04/02にTHE LUV BUGSよりリリースされた楽曲『WING IT!!!』のMVを制作した

youtu.be

  • この記事では、Renardが担当する部分について解説する (主にUnity部分)

担当

  • キャラクターモデリング:O.G.I.
  • モデリング、一部映像(遊園地の爆破)、破壊アニメーション:kinankomoti
  • それ以外:Renard

  • 私とkinankomotiはいろいろ話し合いながら制作した

  • キャラクターモデルに関しては、O.G.I.さんが同レーベルの他のMVで使用したモデルを使わせて頂いた

youtu.be

MV全体の構成

  • 大きく分けて「遊園地」「宇宙」「コンクリート」の3つのパートで構成されている

遊園地

宇宙

コンクリート

  • 全体を通して、歌詞は3D空間に出現するようになっている

構想

  • 作業は全てfigjam上で行った
  • 歌詞を読み合って解釈をし、方向性を統一
  • 役割のざっくりとした分担 (というよりは技術スタックによる制限に近いが)
  • リファレンスを集めながら、やってみたい表現を羅列
    • foil balloonのような、膨らむsoftbodyの風船
    • Iridescentの質感(構造色)
    • 歌詞を質量のある3Dとして見せるリリックモーション (ex: サカナクション/アルクアラウンド
    • 動画コーデックによるグリッチ
    • など
  • 曲を大きな3つのセクションに分け、それぞれに対しシーンを定める
  • さらに分割した小セクションで細かい演出を割り当て
  • モデルを仮置きし、絵コンテ的なものを添える(絵が描けないので)

  • ある程度固まって取り掛かかった後は、TODOの洗い出し -> TODOの消化のイテレーション

  • TODOは依存関係が深いものから優先付け (例えば、文字のモデルが無いと歌詞が配置できないので優先度は高い、など)

Unityのタイムライン構成

  • Unity Recorder
  • サウンド
    • 歌詞データ制御
    • 1拍、1小節のclipを大量に生やしてスナップするやつ
  • カメラ制御
    • 切り替えはCinemachine、パラーメータをAnimationで制御
  • キャラクター制御
    • 位置をParentConstraint、表情や動きはAnimation
  • ライト制御
    • 色やon/off (Activation Track)
  • オブジェクト制御
    • Animationでメリーゴーランドやゴーカートなどを動かしたり、on/offを切り替えたり

歌詞のシステム

  • MusicXMLが貰えたので、C#でパーサーを作り、UnityのTimelineのTrackとして実装した
  • 歌詞と時間の情報をjsonで書き出し、kinankomotiはhoudiniを使って文字のsoftbodyシミュレーションをvellumで回し、ペラペラ文字を膨らませる
  • fbxで文字を書き出す際に、tangentに膨らんだあとの座標を書き込み、歌詞の出現時の時間をuvに書き込む

  • Unity側ではTimelineの時間をシェーダーに渡し、文字の膨らむアニメーションは全てシェーダー
    • 一気に膨らんで、空に飛んでいくような動きを実装
  • 後は配置だけすれば勝手に歌詞が出現していく仕組み
  • 文字の色については、干渉薄膜をベースとしてシェーダーを書いた
  • https://github.com/kinakomoti-321/TFI_LUTCreator

カメラ制御

歌詞駆動制御

  • 歌詞を映す画角の際は、歌詞の位置に合わせて自動で決まるようにした
  • Cinemachine Target Groupをベースに、現在発音中の歌詞と直近の幾つかを自動的にGroupにする(重み付けは時間に対するカーブ)
    • Groupにすると、Transformが回転と位置の重心になり、Cinemachine Followで勝手に歌詞を正面に捉えて追従できる
    • またCinemachine Group Framingによって、歌詞をどの程度絵に含めるか制御
    • 幾つかのフレーズでGroupを区切って、カメラの遷移を自然に (「安直な」「ストーリーで」「横転」みたいな、歌詞の区切りは重みづけを残さず切る的な)

Cinemachineの活用

  • 歌詞が無い時はSplineDollyやOrbitalFollow、キャラクターに対してFollow(Lock To Target)など
    • パラメータはAnimationで制御
  • Dampingや、Recomposer、Camera Offset、Rotation Composerで調整
    • Tilt、Pan、Dutch、DollyZoomなどもできる
  • 手振れ感(?)はBasic Multi Channel Perlinで実現できる

キャラクターアニメーション

レンダリング

  • RayTracingの設定(Volume)を2つ、レンダリング用のmaxのやつと、editor上で確認する用のraymarchingとか使った安いやつを用意
  • Unityには一応PathTracingもあるが、使い物にならなかったので使わなかった
    • UnityRecorderはパストレ用のAccumulationの設定があるが、色々うまくいかない
  • DOFはCinemachine Auto Focusで、ピントが合う感じができる

  • MotionBlur、Tonemapping、Bloomなど軽くポスプロを入れ、あとはAEに任せる
  • 録画はUnityRecorderで、ProRes 4444 XQにすると一番画質が良い (より良くする場合、1フレ1フレ撮ってロスレスで書き出し、後で合成するのがよろしいと思う)

グリッチ

  • 色々なコーデックでdatabendingを行い、良いやつを組み合わせて合成
  • Datamoshはffglitchやそのforkなどで行った
    • AEのDatamoshプラグイン(有料)はffglitchのラッパーなので嫌で買わなかったが、後になって思うと、買った方が簡単だよね 嫌なんだけどね
  • NTSC風データ損失エフェクトはNTSC-rsを使用

コンポジット(AE)

  • コンポジットって何?
  • Deep Glow、Magic Bulletとか色々かけた
  • 色については何も分かんないので勘で

  • このパチンコみたいなロゴはがっつりエフェクトで作った 元のレンダリングはHoudini(kinankomoti)
  • Unity Timelineの使い方が悪いのか、カメラ切り替えのタイミングで1フレームおかしくなることがあり(キャラだけ残るとか)、それをごまかすため色々やった
    • モーションブラーで誤魔化す (BCC Temporal Blur + BCC Directional Blur)
    • Twixtorで1フレだけ伸ばす
    • AIで1フレだけ伸ばす

おわりに&感想

  • 製作期間は2カ月半ほど
  • MVの制作経験が無かったので不安だったが、できた
  • Unityでアニメーションカーブをつけるとき、Curve Masterに助けられた これが無いと無理
  • Unityのレンダリングには限界があるので、Blenderを使えるようになりたいでづ
  • 締切2週間前からAEを学び出しました。プラグインが軒並み高いのに、SapphireとかMagic Bulletとか、みんな使いすぎです ああああ
  • 参考にしたいからMVをいっぱい見たんだけど、心が折れそうになりました この世界 表現者多すぎ

SESSIONS2024 WriteUp

これはSESSIONS Advent Calendar 2024の記事です

adventar.org

SESSIONS2024 WriteUp

こんにちは、Renardと申します

これは何

2024/11/16~2024/11/17に日本科学未来館で行われたSESSIONSというイベントのWriteUpです

sessions-party.com

沢山の提出ありがとうございました。 私は作品の上映とか管理とかを主にやっていました

解説1 (Code Graphics Compo)

youtu.be

この、『[SESSIONS2024] 【ゆっくり解説】3分で分かる!「Classic GLSL Graphics」の作り方【SESSIONS】』はCode Graphics Compoで優勝した作品です

詳しく言うと「Code Graphics」という部門のサブカテゴリである「Classic GLSL Graphics」のレギュレーションに則っています

sessions-party.com

簡単に言うと、外部ライブラリ/リソース無しの生WebGL fragment shaderでお絵描きする部門です(shadertoy的な感じ)

こんな感じで、GLSLが49296文字あるだけです

https://twigl.app/?ol=true&ss=-OBizEQsMADi_j40QI53

ここから動かせますが、20秒くらいコンパイルでブラウザが固まるので気をつけてください

文字の表示について

この作品のおもしろポイントはja/en字幕があるとこくらいなので、それだけ解説します(他はシェーダーお絵描き)

実はコードの前半部分(25000字くらい)はプログラムで自動生成したものなので、見た目より労力はかかってません。 作品全体で2,3日くらいでした

このツールはUnityのエディタ拡張として実装されていて、文章を入れると美咲フォント(8x8のビットマップフォント)に変換してくれます

scrapbox.io

こんな感じで縦横が区点に対応しており、一枚は全角(JIS X 0208-1990)でもう一枚が半角(JIS X 0201-1976)になっています。

適当なサイトから拾ってきたJIS X to Unicode表を気合いでパースし、文字->Unicode->Texture2Dからサンプル->ビット(uint)という感じで変換してます

美咲フォントは全角8x8、半角4x8(それぞれ横1pxは余白)になっているので、全角はuint(32bit)2個、半角はuint1個でデータを表せます

という事でフォントデータはuintの配列で表し、文章自体は全角半角区別せずにインデックスで管理します(重複した形をまとめるため)

変換結果はこんな感じになります

const int HEXCOUNT=26;
const int TEXTCOUNT=2;
const int MAXTEXTLEN=17;
const uint[] HEX=uint[HEXCOUNT](
0x00000000u,//Null
0x0E0021E0u,//こ(l)
0x03000070u,//こ(r)
0x884C2210u,//ん(l)
0x00005520u,//ん(r)
0x19119590u,//に(l)
0x4F4A60C0u,//ち(l)
0x07034210u,//ち(r)
0x1D11D3D0u,//は(l)
0x17111350u,//は(r)
0x35535550u,//R
0x00257160u,//e
0x00355550u,//n
0x00346560u,//a
0x00531110u,//r
0x44655560u,//d
0x444C21E0u,//と(l)
0x00300070u,//と(r)
0x8F9F9F80u,//申(l)
0x07474700u,//申(r)
0x222222C0u,//し(l)
0x00004210u,//し(r)
0x8F8FA9E0u,//ま(l)
0x07070160u,//ま(r)
0x0F42C0C0u,//す(l)
0x17111100u//す(r)
);
const int[] TEXT0=int[MAXTEXTLEN](1,2,3,4,5,2,6,7,8,9,0,0,0,0,0,0,0);
const int[] TEXT1=int[MAXTEXTLEN](10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0);
int[] TEXTLEN = int[TEXTCOUNT](10,17);
int[MAXTEXTLEN] getText(int i){int[MAXTEXTLEN] t;if(i==0)t=TEXT0;else if(i==1)t=TEXT1;return t;}

ボトルネックは明らかにgetTextで、これはもうどうしようもなかったです レギュレーションが30secだったので、それに合わせて文章を考えました

レンダリングはこんな感じです

bool inuv(vec2 p)
{
    return all(lessThanEqual(vec2(0),p)) && all(lessThan(p,vec2(1)));
}
float printHalf(vec2 p,int t)
{
    ivec2 i = ivec2(p * vec2(4,8));
    return float(inuv(p) && ((HEX[t] >> (i.x + i.y * 4)) & 1u) == 1u);
}

動くコード(Unity拡張+twiglサンプル)はこちらにあるので、試したい方はためしてみてください

github.com

おわりに&感想

  • なぜUnity拡張なのか : もともと趣味で作ってたプロジェクトがあり、それを流用したから
  • Classic GLSL GraphicsのレギュレーションにMRTを追加したのは私です 個人的に2passのリッチなレンダリングなどを期待していましたが、私以外誰も使っていなかったのでちょっと残念でした(自分はlife gameくらいにしか使ってない)
  • ShaderJamでComputeShaderを使ってみて、その場で色々な面白さが分かってきたのがとてもShaderという感じで嬉しかった
  • 今回のSESSIONSはいろいろなコミュニティに届いていておもしろかったです

VJ ゆるWriteup (VRChat Sequence 16 2023-12-18)

VJ ゆるWriteup (VRChat Sequence 16 2023-12-18)

こちらはジェネ系VJ Advent Calendar 2023の18日目の記事です

qiita.com

要約

  • GLSL(Fragment Shader)だけでジェネVJした

はじめに

VRChatのクラブイベント「Sequence」でVJをしました

使ったもの

構成

全体の構成はこれだけです
TDは動画をNDIで飛ばす装置として使いました

構成

Sh4der Jockey側のパイプラインはこんな感じです
GはIncludeして使いまわすやつで、Fがfragment本体

パイプライン

uvmapがこんな感じだったので、真ん中に出力するの(mainout)と横の二つ(subout)に分けました

uvmap

なんか

SDXLでフッテージ作る

TDからNDIで送るフッテージはSDXLで作りました

ComfyUIを使いました

こんな感じでノードで使えるので、Automatic1111よりやりやすいです

動画作るのはこれでやりました 他にもいろいろ入れたけど

github.com

一つの動画が3.875秒なので、pixelsortで無理やり32個つなげたのを作りました

駅のホーム
女の子

せっかくなので共有します

drive.google.com

1024x1024で長さ2:04です pixelsortのタイミングも同じなので、重ねられます

入力について

Akai midi mixと適当なゲームパッドでやりました

Sh4der Jockey側で入力を受けるのですが、MDI以外は受けられないのでゲームパッドを仮想MIDIに変換しました

やり方↓

触手のイージング

触手の動きはこんな感じです

float tr=1-exp(-time*5);
tr+=(.5-.5*cos(TAU*tr))*.5;

初速大きめに動き過ぎて、ちょっと戻る感じ

graphtoy.com

感想

  • 楽しかった
  • loserさんのDJについていくのが難しかったので、次はもっとうまくやりたい
  • ストロボ最強

VJ ゆるWriteup (VRChat WIREDVEIN 2023-12-01)

VJ ゆるWriteup (VRChat WIREDVEIN 2023-12-01)

こちらはジェネ系VJ Advent Calendar 2023の5日目の記事です

qiita.com

まだ参加者が少ないのでもっと皆さんに書いて欲しい~~

要約

  • UnityHDRPでジェネVJやったよ

はじめに

VRChatのクラブイベント「WIREDVEIN」でVJをしました

動機

  • UnityHDRPのレイマーチングシェーダーを新しいHDRPに対応させたい

イメージ/やりたいこと

  • レイマーチングでピカピカする

  • 顕微鏡で内臓を見る (見るとこはコントローラーで動かせる)

顕微鏡

実装/知見

レイマーチングを新しいHDRPに対応させる

前回の記事より

気を付けることとして、HDRP6.9.0を使わないと動きません
HDRP、とくに9.x以前はpreviewになっていてバージョン間の互換性がほぼないので、気をつけポイントです

今回は HDRP14.0.8+Unity2022.3.8f1でやりたいので、それに対応させます

  • HDRP6.9.0のLit Shader
  • HDRP14.0.8のLit Shader

のDiffを見ながら、書き換えるべき箇所を一つづつ書き換えていきました

  • 古い方の"HDRP/Lit":879Line 45997chars 15pass
  • 新しい方の"HDRP/Lit":1303Line 69811chars 21pass

という感じで、結構増えています(これは.shaderだけなので、全部展開するともっと増えていると思われます)

ただ、中身を見てみるとそこまで破壊的な変更はなく大きな変更はDXR系のパスくらいなので、レイトレに対応しないのであればやれそうです

結果的に半日かけて書き換え出来ました Shader欲しい方はDMなど下さればあげます

どういう感じで書き換えたかはこちらの殴り書きをご覧ください

hackmd.io

RenderingDebuggerが有用でした

Kanetaさんのレイマーチングシェーダーが無ければこれは無理でした ありがとうございます

平面のシェーダーをPBRの力で良く見せる

今回顕微鏡で見る対象はyonatanさんのこれを考えていました

これをそのまま出すと内臓のぬるっとした感じが出ないので、PBRの力を借ります

HDRPでShaderをコードで書くのは苦行ですので、ShaderGraphを使います
コードが必要な個所はCustomFunctionNodeを使えばいいので簡単です

今回はyonatanさんのシェーダーをベースに内臓をRenderTextureに書き、ShaderGraph側でHeightToNormal Nodeを使ったり、ClearCoatやDepthOffsetをやるとぬるっとしました

これで完成なのですが、謎のエラーが出ました

Shader error in ‘Shader Graphs/ShareNakami_Organ’: Opcode DerivCoarseX not valid in shader model lib_6_3(closesthit). at ShareNakami_Organ(15738) (on d3d11)

これはHDRPでRayTrace OnにしてるとDDX/DDXらへんが使えないので発生します なんかEditor上だと無視しても普通に見えたのでやったーと思っていたのですが、ビルドできませんでした
(公式の人も無理だと言ってる)

最悪なことにRayTraceをOffにしても何故かエラーが止まず、泣く泣くNormal取るのはあきらめました はあ

有用なライブラリなど

特にMinisとInput Systemの組み合わせはマッピングをすっきり書けてかなり良かったです

今回はコントローラーとMidiを併用したので、そこらへんもいい感じに書けるのが良いでした

マッピング/入力について

コントローラーは当初「顕微鏡動かすならスティックだよな~」程度に考えていたのですが、コントローラーは拍を全部押して取る私のVJスタイル(?)にとても合いました

とても押しやすく疲れにくく、タイミングも合いやすかったです

マッピングはこんな感じでした

mapping

コントローラーはこれです なんでもいいので安いやつです

F310ゲームパッド - コンソールスタイル - ロジクールゲーミング

感想

今回やる気が出るまで時間がかかり、前日/当日に8割実装したため、本番直前のビルドで不具合が発生しまくりました(5分前くらいまでビルドしてました)
結局Cookie入れたAreaLightが無いことにされていたのはなぜだったんだ まあできたのでOKです

後AfterParty用に朝日をMidiで操作して昇らせるシーンがあったのですが、AfterPartyが消滅したので深淵に葬られました

VJ ゆるWriteup (VRChat ECHO 2023-10-28)

VJ ゆるWriteup (VRChat ECHO 2023-10-28)

要約

  • UnityHDRP+レイマーチングでジェネVJシステムを作ったよ

はじめに

VRChatのクラブイベント「ECHO」でVJをしました

動機

  • Sh4derJockey(GLSL)だけでVJするのは楽しいが、ずっとやってると表現が凝り固まってしまう気がしたので
  • 楽したい 正規直交基底を書かなくても絵を出したい
  • モダンなリアルタイムレンダリングを触ってみたい

コンセプト

アバターと生と死的な感じ

イメージ/やりたいこと

  • ré3ちゃんを踊らせたい

r3isen.booth.pm

  • CUEで0b4k3さんが荒ぶるやつ

4つ打ちに合わせて確率で荒ぶる

  • 人型モデルが歩く、VJでよく見るやつ

歩く

Milkinsideabagofmilkinsideabagofmilk

もじ

  • 適当なレイマーチングをいれる

いざ実装

HDRPでレイマーチングをやる

Kaneta先生の記事に全部書いてあります

気を付けることとして、HDRP6.9.0を使わないと動きません HDRP、とくに9.x以前はpreviewになっていてバージョン間の互換性がほぼないので、気をつけポイントです 対応するエディタのバージョンもあるので、これを見ます https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@14.0/manual/System-Requirements.html

今回はUnity2019.2.0f1を使います

映像をOBSに送る

https://github.com/keijiro/KlakSpout/tree/v1

v1じゃないと動きません 2019なので

Midi入力を受けとる

https://github.com/keijiro/MidiJack

Minisは動きません 2019なので

文字のシェーダーを書く

"TextMeshPro/Mobile/Distance Field"のシェーダーを改造します

やることは単純で、ノイズでuvを歪めた文字を三回重ねただけです

感想(光)

  • いつもより労力が少ない
    • BRDFを書かなくても絵が出る
  • 文字を自由に出せて楽しい
  • カメラワークが簡単
  • CPUが自由に使える
  • 意外と軽い
  • デバッグがしやすい

感想(闇)

  • GUIをポチる時間があまり楽しくない
    • やっぱり作ってて楽しいのは板ポリGLSL
  • HLSLが嫌い
  • 結局最後の1日でSDF作ることになって、あんまり凝ったものが出せなかった
  • ドラムパッドが押しにくすぎる
    • Midiファイターがほしい

Truchet Tilingのやり方

Truchet Tilingのやり方

要約

・↑これ、Truchet Tilingって言うらしい

・セルを構成する辺の中点に固有のハッシュ値を割り当てることで線の接続/切断を表すとうまくいく

・一番下にソースコードも載せておきました

方針

1.uv=fract(n*uv)などでuvをグリッド状にする

2.floor(n*uv)+.5でセルの中央の点を得る

3.中央点から四方向に座標を足して、辺の中点を得る

4.中点の座標をハッシュ関数にかけて、線の接続/切断を決める

(ex:hash12(mid)<.5)

5.1で得たuvと4で得た接続情報を元にセルの中身を描画する

実装

1~3は割愛します

4

vec2 tci(vec2 uv)
{
  return round(uv*2.)*.5;
}
const vec2[4] dir = vec2[4](vec2(1,0),vec2(0,1),vec2(-1,0),vec2(0,-1));
vec2[4] quv;
int co=0;

float th=mix(.2,.8,sin(time)*.5+.5);
for(int i=0;i<4;i++)
{
    vec2 id=tci(cen+dir[i]*.5);
    if(hash12(id)<th)quv[co++]=dir[i];
}

辺の中点idを得る際、隣のセルとハッシュ値を一致させるため、計算誤差が怖いのでtciという関数を作って丸め込んでいます。

また後に使うため、接続すると決めた点を配列quvに入れています。

5

// qn:接続点がn個のセルの描画をする
float q0(vec2 uv)
float q1(vec2 uv,vec2 p0)
float q2(vec2 uv,vec2 p0,vec2 p1)
  vec2 p=uvf*2.-1.;
  if(co==0)c+=q0(p);
  if(co==1)c+=q1(p,quv[0]);
  if(co==2)c+=q2(p,quv[0],quv[1]);
  if(co==3)
  {
    if(hash12(cen+.42)<.5)
      c+=q1(p,quv[0]),c+=q2(p,quv[1],quv[2]);
    else
      c+=q1(p,quv[1]),c+=q2(p,quv[0],quv[2]);
  }
  if(co==4)
  {
    if(hash12(cen)<.5)
      c+=q2(p,quv[0],quv[1]),c+=q2(p,quv[2],quv[3]);
    else
      c+=q2(p,quv[0],quv[2]),c+=q2(p,quv[1],quv[3]);
  }

接続する点の数coで描画の場合分けをします。 また、セルのuvであるuvfを扱いやすい形に変えます(uv*2.-1.)

q0,q1,q2という関数を定義します。これは接続点がn個のセルの描画をする関数です。 今回の場合、接続点3,4つの描画は(q1+q2)と(q2,q2)で代用しました。ここら辺はいい感じの関数を作るといいと思います

気を付けるのはq2以上で、接続点の並びによって曲線/直線で描き分けるなど、描画を変えるといいと思います

結果

ソースコード

https://gist.github.com/Forenard/eb96f682c46aeb3b10cacd6812f29ba0

GLSLでノイズの等高線をきれいに描く

GLSLでノイズの等高線をきれいに描く

要約

  • 普通にやると線が等幅にならない
  • 勾配を取って、その方向の直線上で評価するときれいな等高線が得られる

このような等高線が得られます

愚直にやった場合

まず考えつくのはfract(noise(uv)*5.0)<0.2のような実装ですが、これでは等高線が等幅になりません

実際にやってみると

vec2 p=FC.xy/r;
float l=.2,f=5.;
float n=snoise2D(p*5.)*.5+.5;
float c=float(fract(n*f)<l);
o+=c;

(ここから動かせます)

確かに等幅にならないことが確認できます

これは、ノイズの変化量が一定でないため起こります

改善案

ノイズに対する勾配を取ることを考えます

手順は以下のようにします

  1. uvをx,yそれぞれの方向に少しだけずらしてノイズを取り、勾配を計算する
  2. floor(noise(uv)*5.0)floor(noise(uv+dir*w)*5.0)を比べて、領域が違う場合線を引く (dir:勾配の方向、w:線の太さ)

こんなイメージです

vec2 uv=FC.xy/r;
float w=.005,f=5.;

#define noise(p) snoise2D((p)*5.)
const vec2 e=vec2(1e-4,0);
float n0=noise(uv+e.xy);
float n1=noise(uv-e.xy);
float n2=noise(uv+e.yx);
float n3=noise(uv-e.yx);
vec2 dir=normalize(vec2(n0-n1,n2-n3));
float i0=floor((noise(uv)*.5+.5)*f);
float i1=floor((noise(uv+dir*w)*.5+.5)*f);
float c=float(abs(i0-i1)>.5);

o+=c;

(ここから動かせます)

追記

@phi16さんより https://iquilezles.org/articles/distance/ を使ってはどうかと教えて頂いたのでそちらでも実装してみます

vec2 uv=FC.xy/r;
float w=.01,f=5.;

#define noise(p) snoise2D((p)*5.)
const vec2 e=vec2(1e-4,0);
float n0=noise(uv+e.xy);
float n1=noise(uv-e.xy);
float n2=noise(uv+e.yx);
float n3=noise(uv-e.yx);
vec2 g=vec2(n0-n1,n2-n3)/(2.*e.x);
float i0=fract((noise(uv)*.5+.5)*f);
float c=float(i0/length(g)<w);

o+=c;

(ここから動かせます)

あまり見た目が変わらず、こちらの方が軽いのでこっちでいい気がします ただ6回やってるので場合によっては私の実装の方が綺麗に出るかもしれません(分からず)

また、今回は実装していませんがsmoothstepを使うことでジャギが軽減されます

まとめ

ノイズを6回(iq先生の方法だと5回)評価するので若干重いですが、その分きれいな等高線が得られます

また、副次的に勾配が取れるのでこのような表現も簡単にできます