任意の場所を常に向くカメラ
VRChat 公式のカメラには Look at me モードがあって、常に自分の顔?を向いてくれるようになる。これを、任意の点を向けるようにしたい。
できたもの
— あずま (@hogextenv) 2022年5月2日
VirtualLens2 を使って、レンズが常に白い Cube を向くようにした。写真の向き (縦にしたり斜めにしたり) は、持っているカメラの向きで変えられるようにしてある。
あとは白い Cube (とカメラ本体) がカメラ上で見えないようにしたいのだけど、 UI Layer に変えても写ってしまって困ってる……(どうしたらいいのか??)
詳しく
virtual lens等にaim constraintをくっつけてaim先をワールド固定とか…?
— まめ(mamemoyasys) (@mmmysys_vr) 2022年4月30日
ツイートしていたら教えてもらったので、その通り VirtualLens2 と Aim Constraint でやってみることにした。
Aim Constraint は、指定したものを向くための Component で、単純にアバターの顔を指定すれば Look at me 相当のことができる。今回は、それを任意の場所にしたいので、ワールド固定した Object を向くようにする。なので作戦としては、ワールド固定を習得してから、 Aim Constraint を習得する二段階。
ワールド固定
見たのはこのあたり。後半に仕組みも書かれていて面白かった。 Position Constraint でアバターと逆の移動をさせることでその場に置き去りにする、という手法で、ものすごいなるほど感がある。 Rotation Constraint は単に無効化するために負数を指定するというのも面白い。
今回は、ワールド固定する白い Cube は (手ではなく) カメラのレンズの前にくっつけるということにしたので、↑の記事での手順のうち、手に持たせる代わりにカメラのレンズに入れた。この後やる Aim Constraint で指定するので、 Aim Constraint をつける Object の子には入れないように気をつける (子に入れてしまうと、 Aim Constraint で向く→ Cube の位置も変わる→向く……という無限ループになって高速に回転してしまう)。
Aim Constraint
上でやったワールド固定できる Cube を向くように、カメラのレンズに Aim Constraint を追加する。 Unity の様子はこういう感じ。
しかし Aim Constraint を指定すると、 (Z軸が Cube を向いてくれるのはいいとして) レンズの向き (Z軸回転) が必ず初期位置から 90度ずれてしまって困った。色々試すと、 Up Vector という設定を変えると挙動が変わることがわかった。これは本当に理解が難しかった…… (Unity のドキュメントには「アップ軸を指定」とか「上方向を指定」とか書かれていてよくわからない (英語でも「specify the up axis」とか「to specify the upward direction」とかなので翻訳のせいではなさそう))。
Aim Constraint の Up Vector
Up Vector というのは、「Aim Constraint で向く際にどこを常に上とするか」という設定である、と言えそう。
まず Aim Constraint というのは、「指定した Object に向かって、 (Transform を基準とした) あるベクトルを向ける Component」である。今回はカメラのレンズの真正面が Z軸そのものだったので、 (0, 0, 1) というベクトルを指定した。例えばカメラの斜め上のほうを何かに向けたいときは、 (1, 2, 3) みたいなベクトルも指定できるということになる。
ここで、そのベクトルを向けた後の状態を考えると、そのベクトルを軸にいくら回転してもよいことがわかる。例えば今回の場合、 Z軸回転をどれだけやっても、 Z軸が Cube に向いているというのは変わりがない。なので向きが一意にならない (多分)。
Up Vector を使うと、その向きを一意に決めることができる。例えばレンズの Y軸 (0, 1, 0) を Up Vector にして、 World Up Type を Scene Up にすると、レンズ Y軸が Scene の上 (Scene の Y軸) に常に向く。公式カメラの Auto level (常に水平になるモード) と同じような動きになる。デフォルトは Scene Up なので、上で書いていた「レンズの向き (Z軸回転) が必ず初期位置から 90度ずれてしまって困った」というのはこれだった。
ドキュメントにはこういう表がある。
https://docs.unity3d.com/ja/2019.4/Manual/class-AimConstraint.html
Up Vector このゲームオブジェクトのアップ軸を指定します。例えば、ゲームオブジェクトが常に正の Y 軸が上を指すように指定するには、X、Y、Z 軸にそれぞれ 0、1、0 の__Up Vector__ を入力します。 World Up Type 上方向の軸を指定します。エイムコンストレイントは、このベクトルを使用してゲームオブジェクトのアップ軸をこの上方向に整列させます。 Scene Up シーンの Y 軸。 Object Up World Up Object が参照するゲームオブジェクトの Y 軸。 Object Up Rotation World Up Object が参照するゲームオブジェクトの World Up Vector によって指定される軸。 Vector ワールドアップベクトル。 None ワールドアップベクトルを使用しません。 World Up Vector World Up Type で Object Up Rotation と Vector の選択時に使用するベクトルを指定します。 World Up Object World Up Type で Object Up と Object Up Rotation の選択時に使用するゲームオブジェクトを指定します。
今回は、レンズの Y軸をカメラの Y軸に合わせたかったので、 Object Up を使うことにした。小さな Cube をもうひとつ足し、カメラの Y軸側にちょっとだけずらしておいたものを、 World Up Object に指定する。 Up Vector を Y軸にすることで、小さな Cube に常にレンズの Y軸が向くので、カメラを縦にしたり横にしたりするとそのように画面が変わるようになった (一番上の動画でもわかる)。
Aim Constraintを理解してきて、手に持ったカメラのレンズを白いCubeに向けつつ、ピンクのCubeをUp Objectにして画面の向きをカメラの向きでなんとなく操作できるような感じにできてきた、レンズのZ軸がUp ObjectをまたぐとY軸が逆に向く(裏から撮ってることになる)のがめちゃめちゃおもしろい pic.twitter.com/3tJ0l3fTzH
— あずま (@hogextenv) 2022年5月2日
ちなみに None にしたときにどういう挙動になっているのかはよくわからなかった。回転が一意に定まらないはずだけど、大体決まった場所で決まった向きになっているので、何かの基準にフォールバックしているような気もする。
あと World Up Type の Vector に今気づいた。これを使うと、レンズの Y軸を常に Scene の X軸に向けて必ず縦の写真を撮れるようにする、みたいなことができそう。