UE4のマテリアルでピッチとヨー（ロール成分無し）でカメラに追随させる

以下のようにピッチとヨーで常にカメラ方向を向くようなオブジェクトをTickを使わずにマテリアルで実現してみました。
これでSceneCaptureComponentを複数使ってもちゃんとそれぞれの方向にオブジェクトが向いてくれます。
UE4.25.4で試してみています。

![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16768/e521e829-70dd-dd12-b0c1-fcd070011a2b.gif)
ロール成分を使わなかったのはVRで首を傾けた時に画面に対して追随されると酔ってしまうからです。
~~ただし、VRの場合はオブジェクトに近づくと両目それぞれの画像に対して文字が向いてしまうのでぼやけて表示されてしまうという問題があるみたいです。~~
マテリアル中でHMDの位置を取得することで解決できました。下記の「HMDの位置をマテリアルで取得する」を参照して下さい。

# 作成したマテリアル

TextRenderComponentのTextMaterialにデフォルトで設定されているDefaultTextMaterialOpaqueをベースにWorldPositionOffsetを以下のようにしています。

![capRotateMaterial.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16768/18469524-9086-6a7b-f236-264dfc755719.png)

atan2とasinはそれぞれcustomノードで以下のような設定になっています。

atan2
![capatan2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16768/19875d34-946f-507c-61a8-0b2986129adf.png)

asin
![capasin.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16768/79c9d03d-9e92-00f8-0663-2c88e443e5dc.png)


# 参考サイト
## HMDの位置をマテリアルで取得する

[HMD camera location in material - UE4 AnswerHub](https://answers.unrealengine.com/questions/412168/hmd-camera-location-in-material.html)

上記の参照サイトにあるようにcustomノードで以下のように設定すればHMDの位置を取得できるみたいです。
CameraPositionをこのcustomノードに置き換えればHMDの位置に対しての回転となるため、近づくと左右のカメラ方向に向いてボケるということがなくなります。

```hlsl
(GetInstancedView().WorldCameraOrigin + GetPrimaryView().WorldCameraOrigin) * 0.5f
```

2022/05/12追記
UE5.0.1だとWorldCameraOriginがfloat3からFLWCVector3に代わって以下のようにする必要があるみたいです。
参照：[Unreal Engine 5 のラージ ワールド座標レンダリング | Unreal Engine ドキュメント](https://docs.unrealengine.com/5.0/ja/large-world-coordinates-rendering-in-unreal-engine-5/)

```hlsl
(LWCHackToFloat(GetInstancedView().WorldCameraOrigin) + LWCHackToFloat(PrimaryView.WorldCameraOrigin)) * 0.5f
```

## ピッチとヨーとロールを追随させる場合

以下の記事にあるようにSpriteノードで実現できます。
[[UE4]謎のマテリアルノード「Sprite」｜株式会社ヒストリア](https://historia.co.jp/archives/5154/)

AlignMeshToTheCameraというノードもあるようです。
[Help with camera facing material - Unreal Engine Forums](https://forums.unrealengine.com/development-discussion/rendering/118396-help-with-camera-facing-material)

今回はロールを追随させたくなかったので利用しませんでした。

## ヨーを追随させる場合

公式サイトにサンプルがあります。
[スタイライズド レンダリング マテリアル | Unreal Engine Documentation](https://docs.unrealengine.com/ja/Resources/Showcases/Stylized/Materials/index.html)

これをマテリアルをベースにしています。

## ベクトルからのピッチとヨーの求め方

以下の記事を参照しました。

[math - How to get Yaw, Pitch and Roll from a 3D vector - Stack Overflow](https://stackoverflow.com/questions/2782647/how-to-get-yaw-pitch-and-roll-from-a-3d-vector)

> Given unit (normalized) direction vector d
> 
>> pitch = asin(-d.Y);
>> yaw = atan2(d.X, d.Z)

## RotateAboutAxisノードについて

以下の記事で詳しく解説されています。
[[UE4] RotateAboutAxisでオブジェクトを回転する｜株式会社ヒストリア](https://historia.co.jp/archives/6261/)


### 出力値について

> RotateAboutAxisは出力する値は元の座標からの差分なので、絶対座標ではありません。

上記の記事にもあるとおりRotateAboutAxisの出力する値はもとの座標からの差分ということに注意が必要です。
絶対値にするにはPositionに渡した値にAddする必要があります。
また、複数のRotateAboutAxisを利用する場合は以前のRotateAboutAxisの結果を絶対値にして次のPositionに設定する、最終的に用いる差分はすべてのRotateAboutAxisの結果の差分をAddする必要があります。

### Angleに指定する値について

[[UE4]マテリアルブループリント内でクォータニオンでオブジェクトを回す - Qiita](https://qiita.com/selflash/items/1d58a9fc905f8ae3c860)にあるようにRotateAboutAxisのAngleに渡すのは0-1の値ということに注意が必要です。

> 注意点としてはRotateAboutAxisのAngleに指定する値はDegreesでもRadiansでもなく、0~1の値です。
> 90度なら0.25, 180度なら0.5, 360度回したいなら1.0を指定するといった感じで、僕はここで挙動が読めなくてかなり躓きました、、