いつでもパンツを覗きたかった話

# はじめにとお詫び
この記事は、自分で作ったVRMモデルのパンツをただただ覗きたいがために20日近くを費やした人の話です。
今後不快に思われる方がいると思いますが、申し訳ございませんとしか言いようがありません。
それでも読み進めていただけると幸いです。

# 作るにあたった経緯
ある日、いつも通りtwitterを見たりネットサーフィンをしていると、とあるサイトを見つけました。

[ARマーカーが外れてもトラッキングする機能で、スカートの中を覗く事が出来るのか？](http://tsubakit1.hateblo.jp/entry/2014/12/03/213743)

投稿されたのは2014/12/03
自分でもよく見つけたと思います。
この記事では、Unityを用いてARマーカー外でも追跡するARを作成するといったものです。
確かに、ARではマーカーがないと座標がとれないですが、ここでは立体物を用いることで側面からでもスキャンできるので、下からスカートを覗くことができるといったものです。
それでもARはARです。そのマーカーがないと実行できません。

それならVRですれば...?
いっそのことwebで見れるようにすればいつでも覗けてハッピーじゃないか...?
自分のキャラのパンツが見たい...!

しかし、それに行き着くまでは問題ばかりだったのである。

# 作成する前に
作る前に基盤をしっかり作ります。
## 対象端末問題
そもそもwebでVRをするにはどうすれば...といった問題があります。
VR専用の機器を用いるものは好まれません。所持してる人が限られるので。
というわけで使用するものは**スマホのみ**に至りました。
これには[Google Cardboard](https://arvr.google.com/intl/ja_jp/cardboard/)も含まれません。
あくまで**いつでも覗けるようにする**ものなので。

## 使用ライブラリ問題
webだからjavascript使うでしょ!
ってなるんですけどなんのライブラリを使うかという問題があります。
一番に出てきたのが[A-FRAME](https://aframe.io/)です。
Hello WebVRというだけありまして、WebVRを作るのに特化したライブラリになります。
それで、次に出てくるのはthree.jsです。
まあ[前](https://qiita.com/Akatsuki1910/items/8c18858d5a8807660008)からpixivさんの[three-vrm](https://github.com/pixiv/three-vrm)を使用してたんでこっち使うんですけど。
あと操作です。本来なら歩いたりした時に前後左右に動きたかったんですけど...。
あいにくスマホにはそんなセンサーがなく、最終的にGPSから無理やりしてやろうかと思ったら、高さを取得できないのでしゃがんだりがなんともできない...。

昔に試そうとしていた人がいました

[杏inほぼ現実世界その3【ジャイロ・加速度・GPSセンサー】](https://www.theuri.net/%e6%9d%8fin%e3%81%bb%e3%81%bc%e7%8f%be%e5%ae%9f%e4%b8%96%e7%95%8c%e3%81%9d%e3%81%ae3%e3%80%90%e3%82%b8%e3%83%a3%e3%82%a4%e3%83%ad%e3%83%bb%e5%8a%a0%e9%80%9f%e5%ba%a6%e3%83%bbgps%e3%82%bb%e3%83%b3/)

仕方がないので画面で操作できるようにします。
この話はのちほど。

# いざ開発
いざ開発します。
ここでも問題が発生したので紹介します
## iosジャイロセンサー問題
カメラはthree.jsの[DeviceOrientationControls](https://threejs.org/docs/#examples/en/controls/DeviceOrientationControls)を使います。
ここで出てきました難関「**Andoroid端末しか持ってないからios端末の問題がでてくる**」が出てきました。
ここでは、ios13ではジャイロ機能を使用する前に許可を取らないといけないといったものでした。
これは[この記事](https://tknc.jp/tp_detail.php?id=1116)で対応しました。
以下コード抜粋↓

```javascript
//ジャイロセンサー確認
var isGyro=false;
if((window.DeviceOrientationEvent)&&('ontouchstart' in window)){
	isGyro=true;
}

//PCなど非ジャイロ
if(!isGyro){
	setCanvas();

//一応ジャイロ持ちデバイス
}else{
	//ジャイロ動作確認
	var resGyro=false;
	window.addEventListener("deviceorientation",doGyro,false);
	function doGyro(){
		resGyro=true;
		window.removeEventListener("deviceorientation",doGyro,false);
	}

	//数秒後に判定
	setTimeout(function(){
		//ジャイロが動いた
		if(resGyro){
			setCanvas();

		//ジャイロ持ってるくせに動かなかった
		}else{
			//iOS13+方式ならクリックイベントを要求
			if(typeof DeviceOrientationEvent.requestPermission==="function"){
				//ユーザアクションを得るための要素を表示
				cv._start.show();
				cv._start.on("click",function(){
					cv._start.hide();
					DeviceOrientationEvent.requestPermission().then(res => {
						//「動作と方向」が許可された
						if(res==="granted"){
							setCanvas();
						//「動作と方向」が許可されなかった
						}else{
							isGyro=false;
							setCanvas();
						}
					});
				});

			//iOS13+じゃない
			}else{
				//早くアップデートしてもらうのを祈りながら諦める
				isGyro=false;
				setCanvas();
			}
		}
	},300);
}
```
やめてくれよ...よくわからんシステム...
今回では読み込み時にAndroidかiosかを確認し、ボタンを押したときにこの処理を行うようにしました。
ちなみに許可したあとにthree.jsのrendererを作ったりしてください。**潔白の大地**が広がります。

## pixi.jsきちんと表示されない問題
実は操作系のボタンをpixi.jsで実装する予定でした。
ここでpixi.jsのバージョンアップ(4→5)しました。
するとなんということでしょう!canvasがきちんと描画されない!!
こういう時は[github](https://github.com/pixijs/pixi.js/wiki/v5-Migration-Guide)を確認!
以下抜粋↓
>Specifying options as a third parameter in Renderer constructor is officially dropped (same with PIXI.Application, PIXI.autoDetectRenderer & PIXI.CanvasRenderer). In v4 we supported two function signatures, but in v5 we dropped width, height, options signature. Please add width and height to options.

```javascript
const renderer = new PIXI.Renderer(800, 600, { transparent: true }); // bad
const renderer = new PIXI.Renderer({ width: 800, height: 600, transparent: true }); // good
```
ふ～ん:upside_down:(よくわからない顔)
つまり書き方が変わったということですね。
これによって描画されました。
まあ最終的に使わないんですけどね!!

## カメラ正面向いてくれよ問題
Androidでは、基準になる向きが前じゃないんですよね。
なのでVRMモデルを描画しても最初から前にいるわけではありませんでした。

昔に(ry

[杏inほぼ現実世界その5【MMD＋ジャイロセンサー】](https://www.theuri.net/%e6%9d%8fin%e3%81%bb%e3%81%bc%e7%8f%be%e5%ae%9f%e4%b8%96%e7%95%8c%e3%81%9d%e3%81%ae5%e3%80%90mmd%ef%bc%8b%e3%82%b8%e3%83%a3%e3%82%a4%e3%83%ad%e3%82%bb%e3%83%b3%e3%82%b5%e3%83%bc%e3%80%91/)

でも1つわかったことがあります。起動した時に向いている向きは前じゃないけど**一定である**と。
ならばその角度を取得し、補正してあげればいいじゃないかとなりました。

前向きからのずれは```camera.rotation.y```で取得できました。
あとは修正するだけです。
どうにか修正できないものかとcameraの角度を動かしまくりました。
しかし直らず挫折して調べると[alphaOffset](https://threejs.org/docs/#examples/en/controls/DeviceOrientationControls#alphaOffset)を見つけました。
以下抜粋
>The alpha offset in radians. Default is 0.

ほ～ん。いいもん持ってんじゃんthree.jsさんよぉ!
もちろん[DeviceOrientationControls](https://threejs.org/docs/#examples/en/controls/DeviceOrientationControls)にもありました。
これを使ってセットすることで補正できます!!やったね!!

```javascript
controls.alphaOffset = camera.rotation.y * -1;
```
この記事書いてた人に届くといいな。

## TypeScriptの型問題
空間を把握するために、最初色々ものを置いていました。
自分はTypeScriptめったに触らないので型でつまづきました。

```javascript
let material = new THREE.MeshBasicMaterial({
    vertexColors: THREE.FaceColors//エラー
});
```

これ、左はbooleanで右はColorなんですよ。
これだけどうしようもなくて、```as any```つけてました。
仕様なのかよくわかりませんでした。

## 操作問題
ついにきました操作の問題です。
先ほどでも申し上げた通り、端末を動かしても移動できないので操作できるようにします。
操作にはジョイスティックを用いることで滑らかな移動を可能しようと思いました。
これには[**nipple.js**](https://yoannmoi.net/nipplejs/)というライブラリを用います。
しかし、最新バージョンが**v0.8.0で2018年**...
よくわからないなりにコード読んで使いました。
左スティックに平面移動、右スティックで上下移動を割り振りました。これによってぬるぬる動くことができます。

本当はpixi.jsでボタンを作るつもりでしたが、スマホの場合長押し操作の判定をしなければならず、微調整を行いたい場合のために長押し判定の調整をしなければならないため、スティックにすることによって回避しました。

~~しかし名前なんとかならんかったのか...~~

# 完成!!
紆余曲折ありましたが何とか完成しました!!
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">完成!!!! <a href="https://t.co/R7V86Dz121">pic.twitter.com/R7V86Dz121</a></p>&mdash; 暁の流星 (@nomber1910) <a href="https://twitter.com/nomber1910/status/1249718729996296192?ref_src=twsrc%5Etfw">April 13, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

見ていただけるとわかりますが、ちゃんとパンツが見れています!!やったー!!

左上にFPSが出てますが、一度読み込み時に重くなるだけでほぼ60FPS近くを維持しています。
カクつきを気にしなくてすむので万々歳です。

こちら、完成品となります
[ThreeVrmVR](https://github.com/Akatsuki1910/ThreeVrmVR)
ソースコードを解説しようかと思いましたが、そこまで難しくないので頑張って読んでください。


# おわりに
今日(2020/4/14)ではコロナウイルスが蔓延しており、高専生がサイトを作って新聞に取り上げられたり~~twitterで炎上したり~~して活躍しております。
それでなんだって話ですが、自分、高専生なんですよ。
発想の差ってすごいなって思いました(社会の貢献度的に)。

でも、この機にJavaScriptを学ぼうとしたりしてたりする人もいるかもしれません。
そんな人たちがこの記事を見て学習意欲湧いてくれたらなと思います。