RenderTextureで実装する簡易鏡
概要
鏡もどきを簡単に実装する知見です。現実の鏡とは違い、どちらかと言えばカメラの映像を映した液晶ディスプレイ。RenderTextureを用いる事で、そのまま写真撮影にも応用できるので、そういった使い方をするのであれば、この手法がおすすめです。カメラのFOVも好きに調整できるので、平面的な写真が撮りやすい鏡も作れます。(FOVが低いほど平面的に写せてよい感じになる。)
実装方法
1.RenderTexture
を作成。サイズは任意に変更。
2.Material
を作成。Albedo
に1で作成したRenderTexture
を設定し。Shader
をStandard
からLegacy Shaders/Self-Illumin/VertexLit
に変更することで、明るく良い感じに表示される。
※StandardとLitの違い
3.鏡に映すためのカメラを作成。TargetTexture
に1で作成したRenderTexture
をアタッチする。
4.Quad
を作成。Size
やPosition
を調整する。2で作成したMaterial
を設定。拡大/縮小
のX
がマイナスになっているのは、鏡のように反転させるため。
5.Quad
の子オブジェクトにCamera
を設定、Position
やfieldOfView
、nearClipPlane
を調整する。Quadにカメラの映像が写っていれば完成。
カメラのPosition
を計算する方法についてはこちらを参照
完成イメージ
以下はFOV30のカメラを使用してRenderTextureをPNG保存したものです。PNG化についてはRenderTexture
png
保存
とかでぐぐれば出てくるので割愛します。この鏡の実装について書いたのは、RenderTextureを適用したMaterialのShader設定について記述している記事が殆ど無かった為です。暗くて泣いていたUnity初心者はきっと私だけなのでしょう。
VRMのBlendShapeを良い感じに遷移させてみる
表情を変えるだけなら公式を参照すれば解決なのですが、作成中のアダルトアプリにおいて、表情の遷移が重要なエッセンスであると感じ、実装してみました。背景としてBlendShapeをAnimationに組み込むと、スクリプトからの変更が効かなくなるのに加え、同じ体位中(Animation)でも表情だけを変更したく、コードから変更したほうが良さそうだった為です。Mecanimのアバターマスクでの遷移は地獄なので。
前提として、またたきの処理についてはVRM標準のもの(Blinker.cs)をモデルにアタッチしています。
※C#の理解度が低いので、かなりゴリ押しです。
KeepChangeBlendshapeEmote
でBlendShapeを5分割して遷移させてあげるだけの、シンプルな実装ですが、引数で細かい制御を可能にしています。emote
がBlendShapeの名前、ratio
が表情の遷移分パラメータ、sec
が表情のキープ時間、late
が遅延時間、transition
が表情の遷移にかける時間、start
が表情の初期パラメータです。transition
は実際には最低0.02secほどかかってしまうので、この時間を10倍した値を考慮して、遷移間隔を算出してあげる必要があります。
以下が、実際に動作させたイメージです。かわいいですね。
ManageFacialExpressions.cs
VRoidで作成したVRMモデルにBlinker.csと、ManageFacialExpressions.csをアタッチすれば動きます。C
V
B
で遷移、N
で中断。
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using VRM; public class ManageFacialExpressions : MonoBehaviour { private Blinker blink; private VRMBlendShapeProxy proxy; private bool endFlag = true; private bool startFlag = true; private int newFace = 100; void Start() { proxy = this.GetComponent<VRMBlendShapeProxy>(); blink = this.GetComponent<Blinker>(); } void Update() { if(Input.GetKeyDown(KeyCode.C)){ startFlag = false; newFace = 0; } if(Input.GetKeyDown(KeyCode.V)){ startFlag = false; newFace = 1; } if(Input.GetKeyDown(KeyCode.B)){ startFlag = false; newFace = 2; } if(Input.GetKeyDown(KeyCode.N)){ blink.enabled = true; startFlag = false; newFace = 100; } if(endFlag && newFace != 100){ blink.enabled = false; startFlag = true; endFlag = false; StartCoroutine(FaceAnimationStart(newFace)); } } //表情を初期化 public void ResetBlendShape(){ proxy.SetValue("NEWTRAL",0.0f); proxy.SetValue("A",0.0f); proxy.SetValue("I",0.0f); proxy.SetValue("U",0.0f); proxy.SetValue("E",0.0f); proxy.SetValue("O",0.0f); proxy.SetValue("BLINK",0.0f); proxy.SetValue("BLINK_L",0.0f); proxy.SetValue("BLINK_R",0.0f); proxy.SetValue("JOY",0.0f); proxy.SetValue("ANGRY",0.0f); proxy.SetValue("SORROW",0.0f); proxy.SetValue("FUN",0.0f); proxy.SetValue("SURPRISED",0.0f); proxy.SetValue("EXTRA",0.0f); } //表情制御(個別) public void KeepChangeBlendshapeEmote(string emote, float ratio, float sec, float late = 0.0f, float transition = 0.01f, float start = 0.0f){ StartCoroutine(ChangingBlendshape(emote, ratio, sec, late, transition, start)); } private IEnumerator ChangingBlendshape(string emote, float ratio, float sec, float late ,float transition, float start){ yield return new WaitForSeconds(late); for(int i=5; i>=1; i--){ proxy.SetValue(emote,start + ratio / i); yield return new WaitForSeconds(transition); } yield return new WaitForSeconds(sec); for(int i=1; i<= 5; i++){ proxy.SetValue(emote,start + ratio / i); yield return new WaitForSeconds(transition); } proxy.SetValue(emote,start); } //表情制御(指示) private IEnumerator FaceAnimationStart(int faceNo){ int blinkCount = 0; if(faceNo == 0){ KeepChangeBlendshapeEmote("BLINK", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); while(startFlag){ KeepChangeBlendshapeEmote("FUN", 0.15f, 1.2f, 0.0f, 0.1f, 0.15f); KeepChangeBlendshapeEmote("JOY", -0.15f, 1.2f, 0.0f, 0.1f, 0.3f); yield return new WaitForSeconds(2.5f); blinkCount++; if(blinkCount == 3){ blinkCount = 0; KeepChangeBlendshapeEmote("BLINK", 0.6f, 0.0f, 0.0f, 0.01f, 0.0f); } } } if(faceNo == 1){ KeepChangeBlendshapeEmote("BLINK", 0.0f, 0.0f, 0.0f, 0.0f, 0.55f); while(startFlag){ KeepChangeBlendshapeEmote("FUN", 0.15f, 1.2f, 0.0f, 0.1f, 0.15f); KeepChangeBlendshapeEmote("JOY", -0.15f, 1.2f, 0.0f, 0.1f, 0.3f); KeepChangeBlendshapeEmote("BLINK", 0.0f, 2.5f, 0.0f, 0.0f, 0.55f); yield return new WaitForSeconds(2.5f); } } if(faceNo == 2){ KeepChangeBlendshapeEmote("BLINK", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); while(startFlag){ KeepChangeBlendshapeEmote("A", 1.0f, 0.0f, 0.0f, 0.02f, 0.0f); KeepChangeBlendshapeEmote("I", 1.0f, 0.0f, 0.3f, 0.02f, 0.0f); KeepChangeBlendshapeEmote("U", 1.0f, 0.0f, 0.6f, 0.02f, 0.0f); KeepChangeBlendshapeEmote("E", 1.0f, 0.0f, 0.9f, 0.02f, 0.0f); KeepChangeBlendshapeEmote("O", 1.0f, 0.0f, 1.2f, 0.02f, 0.0f); yield return new WaitForSeconds(1.7f); } } endFlag = true; ResetBlendShape(); } }
課題 : 元のBlendShapeの値をキープしつつ、変更先までのパラメータを加算なり減算してあげれば、一度リセットしてやらなくても済むのかもしれない。
作業進捗風景
えっち時のVRMの表情を細かく制御したく、テコ入れした結果。表情制御大変すぎでは。 #VRM憑依ちゃん pic.twitter.com/Vw5tNCI1hI
— せすれりあ (@sesleria) 2019年4月27日
表情は5フレームくらいで遷移させるのが妥当なとこかしら。いかんせんUnity経験が足りなさすぎるゾ。 pic.twitter.com/E5IQzwtCSy
— せすれりあ (@sesleria) 2019年4月28日
VRoidの場合、AIUEOが高めのパラメータで被ると歯が飛び出ちゃう事故が起きる事があるので、そこさえ気を付ければ多少重ねたほうが自然に遷移しますな。 pic.twitter.com/gAh7F31hMP
— せすれりあ (@sesleria) 2019年4月28日
へんたい。 pic.twitter.com/wRCcDyw9VX
— せすれりあ (@sesleria) 2019年4月28日
えっち。 pic.twitter.com/Vo3tWlXVVS
— せすれりあ (@sesleria) 2019年4月28日
BLINKにマイナス入れたらどうなるんだろうと思ってやってみたら、壊れたアンドロイドみたいになってしまった。 pic.twitter.com/MYyVwRTiCM
— せすれりあ (@sesleria) 2019年4月28日
シェイプの初期値も考慮するようにしたけど、引数多すぎて地獄絵図だなぁ。 pic.twitter.com/3nYCdrOvQP
— せすれりあ (@sesleria) 2019年4月28日
VRMモデルになってVRMモデルに憑依するVRアプリを作ったお話
配布先
紹介動画
Unity歴について
7年ほど前にアカウントは作成してあった。本格的に触り始めたのは二か月ほど前。最初にやったのはUdemyの講座。非常にわかりやすい。
ユニティちゃんが教える!初心者向けUnity講座 | Udemy
経緯
VRアプリの作成技術を高めたく、一人称視点のゾンビ討伐系VRアプリを作成しようと思っていたが、VR+TSF系のアプリが少なすぎることに気づいてしまい、方針転換。異性のアバターを使うだけならば、他のVRアプリでも同様の体験が可能だが、感情移入するためには元のアバターを表示し続けておくようなギミックが重要なのではないかと感じた(変身と精神の転移は異なるという解釈) 。
VRMの良いところ
VRoid Studioで高品質なモデルを作成可能。VRoid Hubにおいても第三者が使用可能のモデルがユーザーによって提供され続けている。また、ライセンスが明確な為、様々なゲームに採用しやすい。今回のアプリでVRMロード時に注意したポイントは[このアバターを用いて性的表現を演じることの許可][アバターに人格を与えることの許諾範囲][再配布・改変に関する許諾範囲]。VRoidについては脱衣機能を付与した為、改変のライセンスを厳密にチェックしている(Hubと自作のものではライセンスの確認箇所が多少異なる)。VRMファイルについてはStreamingAssetsから決め打ちのファイルパスを起動時に読み込み、Disallowのモデルが含まれていた場合、強制的に終了するようにしている。
VR開発について
タッチコントローラの互換性を持たせたかったので、OpenVRで作成。InputManagerは鬼畜。VR開発は一見ハードルが高そうに見えるが、MainCameraをGameObjectの子に変えて、以下のようにProjectSettingsを変更すれば良い。
一人称視点のこだわり
憑依という設定を大事にしたく、カメラのクリッピング平面パラメータをギリギリのラインで調整している。VRMには頭を非表示にする機能がついているが、あえてそれは使っていない(頭の影が見えなくなる、髪の毛が見えなくなる為)。IKの処理については、Unity標準のものを使用している。失業中なのでFinalIKを買いにくかった・・。コントローラと乳首の位置が大幅にずれる現象の対処等、HMDやコントローラーの位置についての知見は深まったと思う。現実の体と仮想の体についてのサイズ差異については、あくまで自分自身で調整しているので、腕の長さや、首の長さによってはIKの処理がダメだったりするかもしれない。また、モデルの設定次第ではあるが、胸については手で触った時、必ず揺れるように調整している。こちらについてはMtoFにおけるTSFのお約束事項なので、重要視した。自撮り用カメラはトリガーで背面に切り替わるようにしてあり、倒れている自分自身を撮影出来る。
憑依体験について
精神体になり、体を重ねることで憑依できるギミックも考慮していたが、スピード感を大事にしたく、今回のように相手の体に手を入れる、コントローラーが振動する、そこでトリガーを引くと相手に体に入り込めるという体験にした。TSFシチュエーションとしては微妙だが、VR化すると相手の体に入った直後、元の体の意識が抜けるという非現実的な体験が可能になった。また、大型で高画質の鏡を置くことにより、憑依した自分をすぐに確認できるというTSFが好きな人には刺さるアプリになったのではないかと思う。憑依先のモデルについてはブレンドシェイプでまばたきをする程度のアニメーションしかさせていないが、相手が生きているという感じが出てくる。
言いたかったこと
使用アセット
Very Animation - Asset Store
Abandoned Asylum - Asset Store トイレを借りました
PackageはProBuilderとPost Processing。建物はProBuilderで作成したメッシュにMaterialを貼り付けただけ。
大変参考になったサイト
OpenVR コントローラーの入力 - Unity マニュアル
102 2 レーザーコントローラー · yumemi-inc/vr-studies Wiki · GitHub
Unity標準のVR機能(UnityEngine.XR)メモ - フレームシンセシス
Portfolio
writing now....
・Being interested
VR application. VRM format file.
・IT experience
System Engineering Service : 5 years.
(Windows native application develop)
Information system department : 3 years.
(PC kitting etc)
・Skill
C(Win32API),C#,PHP,HTML5,JavaScript,CSS3,Git,Office365(Sharepoint,PowerBI,Flow,Teams etc)
(But wide and shallow....)
・Certification
情報セキュリティスペシャリスト(2012)
・Creation
-2019
Hyoui Chan : 憑依ちゃん - イネ科 - BOOTH
It's original application working for Oculus Rift CV1 and S.
You can experience sex in 6DoF view and read VRM format files.
You can create an video from a third person viewpoint.
Used: Unity(OpenVR,SteamVR Plugin)
Oculus Quest Apps: イネ科 Questアプリ 配布所 - イネ科 - BOOTH
It's original mini application working for Oculus Quest.
Android App: https://play.google.com/store/apps/details?id=com.Sesleria.VMascot
Simple VRM viewer.Made with Unity.
-2013
Hatena Blog's Theme. platismのテーマ一覧 - テーマ ストア - はてなブログ
About 10,000 user.
Tumblr's Theme. Tumblr-Theme/FLATISM at master · sesleria/Tumblr-Theme · GitHub
Used: CSS3