🐼

VRChatのシェーダーで他オブジェクトの座標取得(浪漫編)

2022/11/17に公開

背景

VRChatでシェーダーに対して、別ゲームオブジェクトの座標を渡す方法として以下があります。(他にあったら教えてください。)

方法 アバターに使用 備考 参考
カメラとRenderTexture使う方法 フレンド以外に見えない こちらとかこちら
UdonスクリプトからMaterialに設定する方法 不可 Worldオンリー なし
GrabPassを使う方法 フレンド以外にも見える
不安定(特にミラーがあるワールド)
参考
GPU Instancingを使う方法 フレンド以外にも見える 参考

本記事の内容

以前作成したしらたまちゃんメタボールで、シェーダー内での2つのボールの相互位置把握に上記以外の方法を使ったので紹介します。この方法はアバターに使用でき、フレンド以外にも見えます。
https://zenn.dev/panda_nakami/articles/20220408-vrchat-products#しらたまちゃんメタボール(2022%2F01)

結論

結論を先に書きますと、同じようなことをしたければ本記事の方法でなくGPU Instancingを使う方法が楽に実装でき、処理負荷も小さく、動作も安定すると思います。

本ページの方法は作者がGPU Instancingの方法を知らなかったときに試行錯誤で実現したものです。後からGPU Instancingの方法を知って悔しかったので記事に残します。
こんな方法もあるんだなぁという気持ち程度に。

内容

座標取得回転情報取得の2段構成で記載します。
ただのメタボールでよい場合は座標取得で、トーラスやメビウスの輪を作りたい場合は回転情報取得も必要です。
レイマーチングの方法は本記事では触れません。

座標取得

  1. 用意するもの
    blenderなどで以下のように原点とボーンで繋がれた球体を用意します。

  2. Unityで球を2つ配置
    2つの球のボーンにParentConstraintをつけ、相手の球のルートボーンを相互に設定します。

これで以下のように球体のメッシュが互いのルートボーンに来るようになり、シェーダー内での原点座標が相手の球の中心座標になります。

  1. シェーダー内で自分の球の中心座標を取得
    vertexシェーダー内で頂点座標から法線*半径を減算することで自分の球の中心座標を得られます。(半径は固定)
  1. 描画
    球の中心座標がわかったのでそれぞれのシェーダーでレイマーチングで自他の球体を描きます。
    球を近づけた際に重なった部分がZファイティングしてそうですが今のところ気にはなっていません。

回転情報取得

  1. 用意するもの
    座標取得に使用した球体のUVを編集します。
    UVのxyを極座標のφΘに見立て、φΘから頂点座標を計算できるようにUV座標を変更します。(半径固定)

  1. 相手の球の回転情報
    サイズ1固定にしておけばシェーダー内でUNITY_MATRIX_Mの3x3成分が相手の球の回転情報です。

  2. 自分の球の回転による移動量
    1.で作った球に対してシェーダー内でUVを極座標変換することで、移動も回転もしていない状態の頂点座標(ピュア頂点座標とします)を取得できます。
    自分の球(自球)の頂点座標から座標取得で得た中心座標を減算したものとピュア頂点座標の差が、回転による移動量です。(サイズ固定前提)

  3. 自分の球の回転軸
    ピュア頂点と自球頂点の線分に対して、線分の中心を通る垂直面は回転軸候補の集合である回転軸面となります。(回転軸は原点を通ります。)

ジオメトリシェーダ―では3点とれるので、内2点を使って2つの回転軸面を取得します。2つの面の重なりが回転軸になります。

  1. 自分の球の回転情報
    回転軸と自球頂点、ピュア頂点を使って回転角のSin,Cosを計算します。
    回転軸、回転角のSin,Cosから回転行列が計算できます。
    ロドリゲスの回転公式を使用。

  2. 描画
    中心座標と回転情報がわかったのでレイマーチングでそれぞれのシェーダーで自他のトーラスとかメビウスの輪を描きます。

終わりに

もっと良い計算方法がありそうですが、そんな些細なことよりも最初に書いたGPU Instancingを使う方法がベストと思います。
自分はわざわざ作り直していませんが、こんなことをするよりはるかに楽に実装できると思います。

Discussion