- 投稿日:2020-11-04T19:40:18+09:00
HoloLens2×WebRTC×Raspberry Pi Zero Wを使ってRaspberry Pi Zero Wでのカメラ映像をHoloLens2に表示する
概要
WebRTCを使ってRaspberry Pi Zero Wからのカメラ映像をHoloLens2に投影することにより、別の視点からの状況を同時に見れたりすることが出来たりする。今回記事にしたのは環境構築に非常に手間取ったためである。
結論としては、dllをビルドした時の対象アーキテクチャを合わせてあげる必要があった。
使ったハードと技術並べてタイトルにしただけなのになろう系みたいな長文タイトルになってしまった。はじめに
HoloLens2の背面にカメラを載せて、車のバックモニターみたいな機構を作れないか調べてたらWebRTCと呼ばれる技術が使えそうだということで、実際に簡単なプロジェクトを作ってみることにした。
環境構築
以下の環境、ライブラリを使用した。
- Windows 10 Home
- Unity 2019.4.13f1
- Visual Studio 2019
- Raspbian GNU/Linux 10
- Microsoft Mixed Reality Toolkit 2.5.0
- MixedReality-WebRTC 2.0.0-preview.1
- MixedReality-WebRTC Samples 2.0.0-preview.1
- MixedReality-WebRTC-ayame
- WebRTC シグナリングサービス Ayame Lite
- WebRTC Native Client Momo
- Newtonsoft.Json 12.0.3
- SuperSocket.ClientEngine
- WebSocket4Net v0.15
プロジェクト作成
Raspberry Pi Zero W編
- OSはデフォルトのRaspberry Pi OSを使用。スイッチサイエンス社からOSがプリインストールされているキットが販売されていたため、OSのインストールは割愛する。
- WebRTCクライアントのWebRTC Native Client Momoを配置する。Raspberry Pi Zero W用のバイナリが提供されているのでそれをダウンロードする。
- 実行方法はSETUP_RASPBERRY_PI.mdを参照。 シグナリングキーとルームIDはHoloLens2側と合わせること。
HoloLens2編
ビルドサポート追加
HoloLens2はUWP環境らしいので以下のようにUWPのビルドサポートを追加する。
ライブラリ追加
MixedReality-WebRTC
プロジェクトフォルダ/Packages/manifest.jsonファイルに以下のように追記をする。
"scopedRegistries": [ { "name": "Microsoft Mixed Reality", "url": "https://pkgs.dev.azure.com/aipmr/MixedReality-Unity-Packages/_packaging/Unity-packages/npm/registry/", "scopes": [ "com.microsoft.mixedreality" ] } ], "dependencies": { "com.microsoft.mixedreality.webrtc": "2.0.0-preview.1", "com.microsoft.mixedreality.webrtc.samples": "2.0.0-preview.1",ライブラリがインポートされるとWindowタブ > Package Managerに追加される。
MixedReality-WebRTC-ayame
GithubのReleasesからMixedReality-WebRTC-ayama-x.x.x.unitypackageをダウンロードし、インポートする。
README.mdにはWebSocket4Net、SuperSocket.ClientEngine、Newtonsoft.Jsonのdllをインポートすると書いてあるので、それらもインポートする。WebSocket4Net
v0.15ブランチを使用する。
WebSocket4Net.slnを開き、ビルドタブからWebSocket4Net.Net45のビルドを選択しdllファイルを作成する。作成されたWebSocket4Net.dllをプロジェクト内に配置してInspectorからPlatform settingsでUWPでの設定を変更する。
SDKをUWPに、ScriptingBackendをIl2Cppに変更している。SuperSocket.ClientEngine
WebSocket4Netのビルド時にWebSocket4Net.dllと同じディレクトリにSuperSocket.ClientEngine.dllが作成されているので、それをWebSocket4Net.dllと同じようにプロジェクト内に配置してInspectorからPlatform settingsでUWPでの設定を変更する。
Newtonsoft.Json
Newtonsoft.Jsonのサイトからビルド済みのものをダウンロードできる。
netstandard2.0ディレクトリ内にあるNewtonsoft.Json.dllファイルを同様に、プロジェクト内に配置してInspectorからPlatform settingsでUWPでの設定を変更する。Microsoft Mixed Reality Toolkit
GithubのReleasesからMicrosoft.MixedReality.Toolkit.Unity.Foundation.x.x.x.unitypackageをダウンロードし、インポートする。
シーン作成
MixedReality-WebRTC-ayameにシグナリングサーバーにayameを用いたサンプルシーンがあるのでそれを流用する。今回はRaspberry Pi Zero WからHoloLens2に一方的に映像を送るのでHoloLens2から送る処理は取り除く。
AyameSignalerのシグナリングキーなども設定する。この時、Raspberry Pi Zero Wと同じシグナリングキーとルームIDを設定する。後々使うかもしれないのでMicrosoft Mixed Reality Toolkitもシーンに追加する。
Mixed Reality ToolkitタブからAdd to Scene and Configure...を選択してオブジェクトを追加する。ビルド
Build SettingsとPlayer Settingsを設定しビルドを行う。
実機確認
— 夏野すぐる (μ兵装) (@fuyunosuguru) November 4, 2020
- 投稿日:2020-11-04T18:47:01+09:00
UnityでSliderを使ってCubeを回したい
やること
タイトル通り、UnityのSliderを使ってCubeを回します。
オブジェクト間の値わたしについて、Sliderを例に理解していきます。
自分が全忘却した時の備えとして、工程を全メモします。
たぶんUnityをインストールしただけでほとんど使い方がわからないという人でもできます。素材の準備
キャンバス、スライダー、キューブ、キューブ用スクリプトの4つを準備します。すべてデフォルト機能のものです。
① Canvasを準備
スライダーを設置するためのCanvasを準備します。Canvasはカメラの前に置くレイヤーのような画面で、ゲームのスコアなどを表示する面です。
Hierarchyウィンドウの何もないところで「右クリック」→「UI」→「Canvas」
Canvasを設定すると、EventSystemというオブジェクトも同時に生成されます。
② Sliderを準備
Sliderはドラックで数値を入力できる長いスイッチです。
これをCanvasの上に設置します。Hierarchyウィンドウの「Canvas」の上で「右クリック」→「UI」→「Slider」
設置が終わったらシーンビュー(メインウィンドウ)の上のタブを「Scene」ビューから「Game」ビューに切り替えると、画面にスライダーが設置されていることがわかります。
(GameビューはUnityの再生時に見えるカメラ目線で、Sceneビューは作業用の目線になります。)③ Cubeを準備
スライダーを使って回転させるCubeを置きます。
Hierarchyウィンドウの何もないところで「右クリック」→「3D Object」→「Cube」
④ Scriptを準備
ProjectウィンドウのAssetsの中の何もないところで「右クリック」→「Create」→「C# Script」を選択し、ファイル名を「SliderDemo」とします。
これで今回使う素材の準備が整いました。
UnityのPublicを理解する
Unityは各オブジェクトに紐づく変数や関数を他のオブジェクトとやりとりしたり、右のInspectorウィンドウに項目としてその値を表示し、書き換えもできるようになっています。
そこがUnityの便利なところなのですが、変数にその機能を持たせるにはPublicというアクセス権を設定する必要があります。という話。大事なのはそこだけです。仕込み
① CubeにアタッチするC#スクリプトを書く
SliderDemoのファイルを開くと、スクリプトをUnityで使えるようにするための雛形がすでに記述されています。
MonoBehaviourというUnityでしか見ない単語があり気になりますが、Unityで扱えるようにするための標準セットを継承するためのもの、ぐらいに考えておけばよさそうです。
また、Unityのスクリプトには「ファイル名とスクリプト内のclass名を必ず同じにする」という大事なルールがあるので注意が必要です。内容を追記し、下記のように改めます。
SliderDemousing UnityEngine; public class SliderDemo : MonoBehaviour //Unity標準クラスを継承 { //スライダーの値を「取得する」ためのパブリック変数を定義する関数 //取得した値のCubeの回転への反映もここで行います。 public void SliderDemo_CubeRot(float newValue) { // キューブを回転させるための変数を宣言 // Vector3変数は3つの値を格納できる型 // その変数に現在のオイラー角xyzを代入 // thisはこのスクリプトをアタッチしたGameObjectを表す Vector3 rot = this.transform.localEulerAngles; // 縦軸のy成分を代入 rot.y = newValue; // 新しい数値をキューブの角度に代入 this.transform.localEulerAngles = rot; } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } }Cubeを回転させるためのTransformのところは今回はあまり気にしないでください。
void Start()やvoid Update()はカラのままで大丈夫です。② スライダーの設定をする
HierarchyウィンドウのSliderを選択すると、右のInspectorに設定画面が出ます。
一番下の方にMin Value、Max Valueという項目があるので、ここにスライダで操作したい回転度数の範囲として-180と180を入力しておきます。
ちなみにその下にあるWholo Numbersというチェックボックスですが、これをチェックするとValueの値を整数に制限できるそうです。仕上げ
オブジェクトやコンポーネントをアタッチして関連づける
オブジェクト(GameObject)というのはUnityの要素の単位で、キャラクターはもちろん、マシンガンの球、光源、ビュー用のカメラまでゲームを構成するあらゆる物体の基本となるものです。またコンポーネントというのはオブジェクトに内包される構成要素で、形、色、衝突判定やスクリプトなどが定義されます。
衝突判定はUnityの物理世界の中で最初から相互に関連するようになっていますが、独自に設定した変数や関数についてはオブジェクト同士で紐付けないと作用がしません。
ということで、① スクリプトをCubeにアタッチする
Assets内のSliderDemoのスクリプトファイルを、HierarchyウィンドウのCubeにドラッグ&
ドロップでアタッチします。しっかり重ねるとブルーにハイライトするので、その時にドラッグします。(ちゃんと重ねないとグレーのハイライトとなり、その状態ではアタッチされません)
Cubeのインスペクターウィンドウに「SliderDemo(Script)」という項目が追加されていれば成功です。② スライダーにCubeをアタッチする
ここがミソです。Sliderの値(Value)をどう取り出すかという話。
SliderのInspectorの下にある「On Value Changed(Single)」という項目を使います。ここでプラスを押すと、Valueの渡し先を設定できるようになります。項目の説明です。
まず左上のプルダウンメニューの「OFF/Editor and Runtime/Runtime Only」の選択ですが、Runtime Onlyを選んでおけば大丈夫です。(Editor and Runtimeを選べば、再生中以外でもスライダーの値が反映されるようになるようです。)次に、その下のボックスにオブジェクトをアタッチします。
今回はCube(Hierarchyウィンドウ内)をこのボックスにドラック&ドロップします。すると右側のプルダウンメニューからSliderDemoの項目を選べるようになります。いま関連づけたCubeにはSliderDemoのスクリプトがアタッチされており、その内容である関数が選べるようになっているというわけです。
さっそく「SliderDemo」→「SliderDemo_CubeRot」を選択します。この設定をすることでスライダーで設定したValueの値(-180~180)が独自に設定した関数「SliderDemo_CubeRot」に渡されるようになりました。
これで準備は完了です。
完成・実行
画面上部の三角ボタンからプログラムを実行します。
スライダーの操作に連携して、キューブが横に回転してくれます。
- 投稿日:2020-11-04T14:04:59+09:00
全集中・Magic Leapの呼吸 肆ノ型 手入力 "かすたむ じぇすちゃ"
なにをするの?
今回作るものとしてはMagicLeapのジェスチャ( 8種類 )を組み合わせてカスタムのジェスチャを作成するというもの
— 松本隆介 (@matsumotokaka11) December 3, 2020
サンプル動画( twitterがおしゃかポンになったときの予備 )プロジェクトはこちらのリポジトリにあります
下準備
本記事は基本的なビルドまでの手順はなされていることを前提として記事を書いています
ビルド時に利用する認証ファイルとかZeroIterationの設定の仕方とか開発環境
- Lumin SDK : 0.24.1
- MagicLeap Unity Package : 0.24.1 ( 少し古いけど Lumin SDKと合わせました )
- MagicLeap Tool-Kit : MagicLeapToolKitのリポジトリ最新版
- UniRx : 7.1.0
- UniTask : 2.0.26
ProjectSettingsのXR Plug-in Management の MagicLeapの項目にチェックを入れる
今回はハンドジェスチャを利用するので
ProjectSettings > MagicLeap > ManifestSettings
より GestureConfig, GestureSubscribe を有効化
シーンの構築
CameraRigの作成
MagicLeap > Core > Assets > Prefabs
からMain Cameraをシーンに配置し、一旦そのMain Cameraオブジェクトを任意のフォルダにプレハブとして保存する( 名前を CameraRig に変更 )
HandControllerの作成
CameraRigオブジェクトの下にHandControllerオブジェクトを作成し、MagicLeap-Tools > Code > Input > Hands > HandInput
をアタッチします
このままだと手が描画されておらず確認しづらいのでサンプルにあるHandVisualizerを利用してテストします
HandControllerオブジェクトの下に
MagicLeap > Examples > Assets > Prefabs > HandVisualizer
オブジェクトを配置し、HandVisualizerコンポーネントのCenterの項目にはnullを指定します( Centerが設定されている場合は球で手の各関節を表示する機能がONになり見た目上邪魔になるため )
HandVisualizserオブジェクトの子オブジェクトにLHand, RHand オブジェクトを作成し、HandInputシーンから [VISUALIZERS]オブジェクトの中のKeyPointVisualizers 以下のオブジェクトを複製、Right~ はRHandオブジェクトの子オブジェクトに Left~はLHandオブジェクトの子オブジェクトにします
上の手順を終えたものがこちら、なおCenterオブジェクトはHandVisualizerオブジェクトを複製する際についてきたものなので削除しても構いません
RHand, LHand それぞれにHandSkeletonVisualizerコンポーネント、AxisVisualizerコンポーネントをアタッチ、BoneColorはお好きな色を設定
ここまでの動作確認
なぜかGameウィンドウに描画されていなかったのでSceneViewの方のスクショ
スクリプトの作成
MagicLeapが提供しているデフォルトのジェスチャ( 8種類 )を組み合わせてカスタムジェスチャを作成するスクリプトを作ります
スクリプト全容( せっかちな人はこれをコピペ )
using System; using MagicLeapTools; using UnityEngine; using UnityEngine.Events; using UnityEngine.XR.MagicLeap; using Debug = UnityEngine.Debug; using Cysharp.Threading.Tasks; using UniRx; namespace AdventCalendar { /// <summary> /// ハンドコントローラ. /// </summary> public class HandController : MonoBehaviour { struct KeyInfo { public HandPose pose; public float time; } public enum HandPose { LFinger, RFinger, LFist, RFist, LPinch, RPinch, LThumb, RThumb, LL, RL, LOpenHand, ROpenHand, LOk, ROk, LC, RC, LNoPose, RNoPose, LNoHand, RNoHand } private HandPose handPose; private ManagedHand rHand; private ManagedHand lHand; [SerializeField] private GameObject handVisualizer; [field: SerializeField] public bool IsGestureLogOutput { get; set; } = false; private async void Start() { await Setup(); SwitchHandVisualize(); } private async UniTask Setup() { await UniTask.WaitUntil(() => HandInput.Ready); rHand = HandInput.Right; lHand = HandInput.Left; rHand.Gesture.OnKeyPoseChanged += OnHandGesturePoseChanged; lHand.Gesture.OnKeyPoseChanged += OnHandGesturePoseChanged; // 確認用にジェスチャをDebug.Logに出力. this.ObserveEveryValueChanged(_ => handPose).Subscribe(e => { if (IsGestureLogOutput) Debug.Log($"Key {e}"); }); } private void SwitchHandVisualize() { #if UNITY_EDITOR handVisualizer.SetActive(true); #elif !UNITY_EDITOR || UNITY_LUMIN handVisualizer.SetActive(false); #endif } private void OnHandGesturePoseChanged( ManagedHand hand, MLHandTracking.HandKeyPose pose) { bool isLeft = hand.Hand.Type == MLHandTracking.HandType.Left; switch (pose) { case MLHandTracking.HandKeyPose.C: handPose = isLeft ? HandPose.LC : HandPose.RC; break; case MLHandTracking.HandKeyPose.Finger: handPose = isLeft ? HandPose.LFinger : HandPose.RFinger; break; case MLHandTracking.HandKeyPose.Fist: handPose = isLeft ? HandPose.LFist : HandPose.RFist; break; case MLHandTracking.HandKeyPose.L: handPose = isLeft ? HandPose.LL : HandPose.RL; break; case MLHandTracking.HandKeyPose.Ok: handPose = isLeft ? HandPose.LOk : HandPose.ROk; break; case MLHandTracking.HandKeyPose.Pinch: handPose = isLeft ? HandPose.LPinch : HandPose.RPinch; break; case MLHandTracking.HandKeyPose.Thumb: handPose = isLeft ? HandPose.LThumb : HandPose.RThumb; break; case MLHandTracking.HandKeyPose.NoHand: handPose = isLeft ? HandPose.LNoHand : HandPose.RNoHand; break; case MLHandTracking.HandKeyPose.NoPose: handPose = isLeft ? HandPose.LNoPose : HandPose.RNoPose; break; case MLHandTracking.HandKeyPose.OpenHand: handPose = isLeft ? HandPose.LOpenHand : HandPose.ROpenHand; break; } } /// <summary> /// ジェスチャコマンドのオブザーバー作成. /// </summary> /// <param name="time"></param> /// <param name="poseA"></param> /// <param name="poseB"></param> /// <returns></returns> private IObservable<KeyInfo> CreateGestureCommandObserver( float time, HandPose poseA, HandPose poseB) { // 指定したキーの判定を通知するObserverを返す. IObservable<KeyInfo> GetInputObserver(HandPose pose) { return this.ObserveEveryValueChanged(_ => handPose) .Where(k => k == pose) .Select(k => new KeyInfo{pose = k, time = Time.realtimeSinceStartup}); } var observer = GetInputObserver(poseA); observer = observer.Merge(GetInputObserver(poseB)) .Buffer(2, 1) .Where(b => b[1].time - b[0].time < time) .Where(b => b[0].pose == poseA && b[1].pose == poseB) .Select(b => b[1]); return observer; } /// <summary> /// カスタムジェスチャを登録し、登録したカスタムジェスチャが発火されたら実行する. /// </summary> /// <param name="time"></param> /// <param name="poseA"></param> /// <param name="poseB"></param> /// <param name="callback"></param> public void RegisterCustomGesture( float time, HandPose poseA, HandPose poseB, UnityAction callback, Func<bool> option = null) { CreateGestureCommandObserver(time, poseA, poseB) .Subscribe(e => callback?.Invoke()) .AddTo(this); } } }スクリプトの解説
先ずカスタムのジェスチャを作成するためにハンドポーズを宣言します、MagicLeapのデフォルトのHandPoseは
MLHandTracking.cs
内に定義されています、このままでも使えますが使い勝手が悪い為一旦カスタムのジェスチャを定義してそちらに変換するようにします。public enum HandPose { LFinger, RFinger, LFist, RFist, LPinch, RPinch, LThumb, RThumb, LL, RL, LOpenHand, ROpenHand, LOk, ROk, LC, RC, LNoPose, RNoPose, LNoHand, RNoHand }
取得したハンドジェスチャをカスタムのジェスチャに変換する処理
private void OnHandGesturePoseChanged( ManagedHand hand, MLHandTracking.HandKeyPose pose) { bool isLeft = hand.Hand.Type == MLHandTracking.HandType.Left; switch (pose) { case MLHandTracking.HandKeyPose.C: handPose = isLeft ? HandPose.LC : HandPose.RC; break; case MLHandTracking.HandKeyPose.Finger: handPose = isLeft ? HandPose.LFinger : HandPose.RFinger; break; case MLHandTracking.HandKeyPose.Fist: handPose = isLeft ? HandPose.LFist : HandPose.RFist; break; case MLHandTracking.HandKeyPose.L: handPose = isLeft ? HandPose.LL : HandPose.RL; break; case MLHandTracking.HandKeyPose.Ok: handPose = isLeft ? HandPose.LOk : HandPose.ROk; break; case MLHandTracking.HandKeyPose.Pinch: handPose = isLeft ? HandPose.LPinch : HandPose.RPinch; break; case MLHandTracking.HandKeyPose.Thumb: handPose = isLeft ? HandPose.LThumb : HandPose.RThumb; break; case MLHandTracking.HandKeyPose.NoHand: handPose = isLeft ? HandPose.LNoHand : HandPose.RNoHand; break; case MLHandTracking.HandKeyPose.NoPose: handPose = isLeft ? HandPose.LNoPose : HandPose.RNoPose; break; case MLHandTracking.HandKeyPose.OpenHand: handPose = isLeft ? HandPose.LOpenHand : HandPose.ROpenHand; break; } }
カスタムジェスチャ購読用のObserverを作成する処理
指定時間以内に1番目のジェスチャ
->2番目のジェスチャ
と連続で呼ばれたイベントを購読するオブザーバーを作成し、返り値で返しますprivate IObservable<KeyInfo> CreateGestureCommandObserver( float time, HandPose poseA, HandPose poseB) { // 指定したキーの判定を通知するObserverを返す. IObservable<KeyInfo> GetInputObserver(HandPose pose) { return this.ObserveEveryValueChanged(_ => handPose) .Where(k => k == pose) .Select(k => new KeyInfo{pose = k, time = Time.realtimeSinceStartup}); } var observer = GetInputObserver(poseA); observer = observer.Merge(GetInputObserver(poseB)) .Buffer(2, 1) .Where(b => b[1].time - b[0].time < time) .Where(b => b[0].pose == poseA && b[1].pose == poseB) .Select(b => b[1]); return observer; }動作確認
サンプル用スクリプト
Sample.cs
using UnityEngine; namespace AdventCalendar { public class Sample : MonoBehaviour { [SerializeField] private Transform camera; [SerializeField] private HandController handController; [SerializeField] private GameObject objA; [SerializeField] private GameObject objB; [SerializeField] private GameObject objC; private void Start() { // 1秒以内に右手グー( RFist ) -> 右手パー( ROpenHand )で発火 SpawnObjA()を処理する. handController.RegisterCustomGesture(1f, HandController.HandPose.RFist, HandController.HandPose.ROpenHand, SpawnObjA); // 1秒以内に左手グー( LFist ) -> 左手パー( LOpenHand )で発火 SpawnObjB()を処理する. handController.RegisterCustomGesture(1f, HandController.HandPose.LFist, HandController.HandPose.LOpenHand, SpawnObjB); // 1秒以内に左手OK ( LOk ) -> 右手OK ( ROk )で発火 SpawnObjC()を発火する. handController.RegisterCustomGesture(1f, HandController.HandPose.LOk, HandController.HandPose.ROk, SpawnObjC); } private void SpawnObjA() { GameObject obj = Instantiate(objA); obj.transform.position = camera.position + (camera.forward * 0.5f); } private void SpawnObjB() { GameObject obj = Instantiate(objB); obj.transform.position = camera.position + (camera.forward * 0.5f); } private void SpawnObjC() { GameObject obj = Instantiate(objC); obj.transform.position = camera.position + (camera.forward * 0.5f); } } }SpawnObj.cs
テスト用のオブジェクト、生成されてから5秒で破棄するだけの処理using System.Collections; using UnityEngine; namespace AdventCalendar { /// <summary> /// スポーンしたオブジェクト用スクリプト、5秒で破棄する. /// </summary> public class SpawnObj : MonoBehaviour { private void Start() { StartCoroutine(AutoDeath()); } private IEnumerator AutoDeath() { yield return new WaitForSeconds(5f); Destroy(gameObject); } } }シーン上に適当なオブジェクト ObjA ~ ObjC を作成しプレハブにし、それぞれにSpawnObjコンポーネントをアタッチ
動作確認用に Sampleオブジェクトを作成し Sampleコンポーネントをアタッチ、シーン上に配置します、オブジェクトの参照周りは以下の画像の通り
Sample.csのStart()にてカスタムジェスチャを登録し、イベントが発火されたら登録したオブジェクトを生成するようにしています
今回のサンプルでは
- 右Fist -> 右OpenHand で ObjA の生成
- 左Fist -> 左OpenHand で ObjB の生成
- 左OK -> 右OK で ObjC の生成
を行います
private void Start() { handController.RegisterCustomGesture(1f, HandController.HandPose.RFist, HandController.HandPose.ROpenHand, SpawnObjA); handController.RegisterCustomGesture(1f, HandController.HandPose.LFist, HandController.HandPose.LOpenHand, SpawnObjB); handController.RegisterCustomGesture(1f, HandController.HandPose.LOk, HandController.HandPose.ROk, SpawnObjC); }
- 投稿日:2020-11-04T00:52:45+09:00
[Unreal Engine4] Material Nodeに対応するHLSL, GLSL
はじめに
北千住さんの記事がとても見やすかったのでその形式を踏襲させていただきました.また,そちらの記事に関数の説明は載っているので参考にしてもらえればと思います.
また,Unity, openFrameworks, TouchDesignerなどでShader言語を書いていて,UnrealEngineも触り始めたという方の作業効率の向上に繋がれば幸いです.Type
UnrealEngine HLSL GLSL Constant float float Constant2Vector float2 vec2 Constant3Vector float3 vec3 Constant4Vector float4 vec4 Texture Sample sampler2D sampler2D Function
UnrealEngine HLSL GLSL Floor floor floor Frac frac fract Fmod fmod mod atan2 atan2 atan lerp lerp mix Power lerp mix Abs abs abs ValueStep step step SmoothStep smoothstep smoothstep Clamp clamp clamp VectorLength length length Distance distance distance Normalize normalize normalize HLSLのみ
UnrealEngine HLSL Saturate saturate Sample NodeRecipe
これらの関数の特徴を捉えることが出来るノードレシピの1例です.(参考までに)
注意
Floor
Frac
Fmod
Lerp
Power
Abs
ValueStep
1th inputによって1.0からoffsetを作って黒の開始させる.つまり,offset = 0.2だと0.8まで黒.
//glslとかに書き換えるとこんな感じ //1th inputの値をoffsetとすると v = step(1.0 - offset, x);SmoothStep
Sign
Saturate