VJ ゆるWriteup (VRChat Sequence 16 2023-12-18)
VJ ゆるWriteup (VRChat Sequence 16 2023-12-18)
こちらはジェネ系VJ Advent Calendar 2023の18日目の記事です
要約
- GLSL(Fragment Shader)だけでジェネVJした
はじめに
VRChatのクラブイベント「Sequence」でVJをしました
#SEQ_VRC お疲れさまでした!@loser4dim さんと100%GLSLでVJしました
— Renard_VRC (@Renard_VRC) December 18, 2023
GLSLに愛https://t.co/Xg42rrwZf1 pic.twitter.com/vEfRhWF4nY
使ったもの
- Sh4der Jockey
- Touch Designer
- ComfyUI
構成
全体の構成はこれだけです
TDは動画をNDIで飛ばす装置として使いました
Sh4der Jockey側のパイプラインはこんな感じです
GはIncludeして使いまわすやつで、Fがfragment本体
uvmapがこんな感じだったので、真ん中に出力するの(mainout)と横の二つ(subout)に分けました
なんか
SDXLでフッテージ作る
TDからNDIで送るフッテージはSDXLで作りました
ComfyUIを使いました
こんな感じでノードで使えるので、Automatic1111よりやりやすいです
動画作るのはこれでやりました 他にもいろいろ入れたけど
一つの動画が3.875秒なので、pixelsortで無理やり32個つなげたのを作りました
せっかくなので共有します
1024x1024で長さ2:04です pixelsortのタイミングも同じなので、重ねられます
入力について
Sh4der Jockey側で入力を受けるのですが、MDI以外は受けられないのでゲームパッドを仮想MIDIに変換しました
やり方↓
gamepad to midiできました ありがとうございますhttps://t.co/Px9Hm3qQ4a pic.twitter.com/LhRbF2LA6C
— Renard_VRC (@Renard_VRC) December 12, 2023
触手のイージング
触手の動きはこんな感じです
float tr=1-exp(-time*5); tr+=(.5-.5*cos(TAU*tr))*.5;
初速大きめに動き過ぎて、ちょっと戻る感じ
感想
- 楽しかった
- loserさんのDJについていくのが難しかったので、次はもっとうまくやりたい
- ストロボ最強
VJ ゆるWriteup (VRChat WIREDVEIN 2023-12-01)
VJ ゆるWriteup (VRChat WIREDVEIN 2023-12-01)
こちらはジェネ系VJ Advent Calendar 2023の5日目の記事です
まだ参加者が少ないのでもっと皆さんに書いて欲しい~~
要約
- UnityHDRPでジェネVJやったよ
はじめに
VRChatのクラブイベント「WIREDVEIN」でVJをしました
昨日のWIREDVEINで行ったジェネVJの映像です
— Renard_VRC (@Renard_VRC) December 2, 2023
DJ:@KesiraeVRhttps://t.co/LRkeL8OQLD pic.twitter.com/1lH7Rb86j4
動機
- 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など下さればあげます
どういう感じで書き換えたかはこちらの殴り書きをご覧ください
RenderingDebuggerが有用でした
Kanetaさんのレイマーチングシェーダーが無ければこれは無理でした ありがとうございます
平面のシェーダーをPBRの力で良く見せる
今回顕微鏡で見る対象はyonatanさんのこれを考えていました
#つぶやきGLSL https://t.co/DJz37Mx6gK
— yonatan (@zozuar) February 2, 2023
vec2 n,q,p=(FC.xy-.5*r)/r.y;
float d=dot(p,p),S=9.,i,a,j;
for(mat2 m=rotate2D(5.);j++<30.;){
p*=m;
n*=m;
q=p*S+t*4.+sin(t*4.-d*6.)*.8+j+n;
a+=dot(cos(q)/S,vec2(.2));
n-=sin(q);
S*=1.2;
}
o+=(a+.2)*vec4(4,2,1,0)+a+a-d; pic.twitter.com/EBVpCJXhMO
これをそのまま出すと内臓のぬるっとした感じが出ないので、PBRの力を借ります
HDRPでShaderをコードで書くのは苦行ですので、ShaderGraphを使います
コードが必要な個所はCustomFunctionNodeを使えばいいので簡単です
今回はyonatanさんのシェーダーをベースに内臓をRenderTextureに書き、ShaderGraph側でHeightToNormal Nodeを使ったり、ClearCoatやDepthOffsetをやるとぬるっとしました
— Renard_VRC (@Renard_VRC) November 24, 2023
これで完成なのですが、謎のエラーが出ました
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スタイル(?)にとても合いました
とても押しやすく疲れにくく、タイミングも合いやすかったです
マッピングはこんな感じでした
コントローラーはこれです なんでもいいので安いやつです
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をしました
#VRC_ECHO
— Renard_VRC (@Renard_VRC) October 28, 2023
先ほどのECHOで@KesiraeVR さんのVJをさせていただきました!
いつものGLSL100%ではなく、趣向を変えてUnity HDRPを使ってレイマーチングしてみましたhttps://t.co/fnAKOfKZwz pic.twitter.com/f9vyDsqWto
動機
- Sh4derJockey(GLSL)だけでVJするのは楽しいが、ずっとやってると表現が凝り固まってしまう気がしたので
- 楽したい 正規直交基底を書かなくても絵を出したい
- モダンなリアルタイムレンダリングを触ってみたい
コンセプト
アバターと生と死的な感じ
イメージ/やりたいこと
- ré3ちゃんを踊らせたい
- CUEで0b4k3さんが荒ぶるやつ
- 人型モデルが歩く、VJでよく見るやつ
- ré3ちゃんには赤い輪郭線があり、Milk Inside a Bag of Milk Inside a Bag of Milkというゲームを思い出したので、言葉の羅列を画面いっぱいに出したくなった
- 適当なレイマーチングをいれる
いざ実装
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を歪めた文字を三回重ねただけです
感想(光)
感想(闇)
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以上で、接続点の並びによって曲線/直線で描き分けるなど、描画を変えるといいと思います
結果
#GLSL
— Renard_VRC (@Renard_VRC) August 11, 2023
Truchet Tiling pic.twitter.com/Zb87vQbq0Y
ソースコード
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;
確かに等幅にならないことが確認できます
これは、ノイズの変化量が一定でないため起こります
改善案
ノイズに対する勾配を取ることを考えます
手順は以下のようにします
- uvをx,yそれぞれの方向に少しだけずらしてノイズを取り、勾配を計算する
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回)評価するので若干重いですが、その分きれいな等高線が得られます
また、副次的に勾配が取れるのでこのような表現も簡単にできます
#GLSL
— Renard_VRC (@Renard_VRC) July 5, 2023
等高線ゴねぇ pic.twitter.com/4ejcV1zPqG
ブログをはじめました
元々ブログはポートフォリオ兼ブログの方に書いていたのですが、Google検索に全く引っかからないので今後はこちらで書こうと思います。
よろしくお願いいたします。