没入感ならぬ凹入感？：拡張現実というより現実拡張？：ARで凹んだオブジェクトを作ろう！


## はじめに

Three.jsとAR.jsで、凹んだ3Dオブジェクトでこんなの作りました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/bfda3d71-344b-523d-c936-29b1793a13ec.png" width="400">
※左が凹んだオブジェクト

## デモ映像

凹んだオブジェクトのデモ映像です。向かって左が完成品です。右と比べると凹んでいる感じに見えますよね？

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ARで凹んだオブジェクトを表示するところまでをQiitaにまとめる。<a href="https://twitter.com/hashtag/%E3%82%A6%E3%82%BD%E7%A9%B4?src=hash&amp;ref_src=twsrc%5Etfw">#ウソ穴</a> <a href="https://t.co/hbnjCoeiNM">pic.twitter.com/hbnjCoeiNM</a></p>&mdash; j4amountain (@zsipparu) <a href="https://twitter.com/zsipparu/status/1218845750815125505?ref_src=twsrc%5Etfw">January 19, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## ARとは

ARとは「拡張現実感（Augmented Reality）」の略で、以下のようなARマーカを基準に3Dオブジェクトの情報を、実際の映像に合成する技術のことです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/12622045-2e49-f566-2008-a5d147fb9e79.png" width="400">

※ Thee.jsとAR.jsは、ARをWebサイトとして実現できる凄いやつ！ブラボー

## 凹んだオブジェクトの仕組み

凹んだオブジェクトの仕組みを説明します。

#### 5つのオブジェクト

5つのオブジェクトで構成されています。凹みの外側にあたる面のテクスチャを透過率100%の画像（透明な画像）をマッピングします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/8bb40b7d-aa12-5139-6fe5-55e18b4f2596.png" width="550">

3DオブジェクトとARマーカの座標をこのように合わせます。3Dオブジェクトの上部の中心が、ARマーカの中心に合うようにします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/9da07dcd-54e5-155d-543e-2ba6486b35d6.png" width="350">

透明な画像とオブジェクトの配置を工夫することで凹んだオブジェクトを表現しています。

※参考※ 透明な画像を不透明な画像に置き換えると、右のようになります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/32fe8564-af64-a99f-e335-edf289d4a89e.png" width="400">


## ソース

ソース一式をgithubに格納しました → [02-depressiion](https://github.com/zgw426/Three.js-FirstStep/tree/master/AR/02-depressiion)

githubのソース一式には、画像ファイルがないので、以下3つの画像ファイルを追加してください。

- parts/img/invisible.png ※透明度100%の画像
- parts/img/tex101.jpg ※筒の底の画像
- parts/img/tex105.jpg ※筒の側面の画像


## Webサーバーを作ろう

凹んだオブジェクトを表示するにはWebサーバーが必要です。

#### 1.NodejsでWebサーバーを作ろう

Webで閲覧するコンテンツなのでWebサーバーが必要です。私はNodejsを使いましたが、他のWebサーバーでも動作すると思います。

#### 1-1.構成図

WindowsパソコンをWebサーバーにします。パソコンにはカメラが付いています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/fb242710-482d-0dd8-ed88-40fadb7b97bd.png" width="300">

ブラウザで、サイト`http://localhost:xxx/depression-01.html`を開き、パソコン内蔵のカメラでARマーカを映すと、凹んだオブジェクトが表示されます。

#### 1-2.Nodejsの設定概要

詳細は省きますが、フレームワーク`Express`を入れました。サーバ起動のスクリプトは以下です。サーバを起動する（`$ node app.js`）と、`wwwroot`配下にhtmlファイルを格納すると、ブラウザからアクセスできます（`http://localhost:3000/xxx.html`）

```javascript:app.js

var express = require('express');
var app = express();
app.use(express.static('./wwwroot'));
var port = 3000;
app.listen(port,function(){
	console.log("Expressサーバーがポート%dで起動しました。モード:%s",port,app.settings.env)
});
```

凹んだオブジェクトのソース一式を`wwwroot`配下に格納しブラウザでアクセスします。ブラウザでサイトを開く際、カメラの使用許可が表示される場合は許可してください。

## 2.スマホでも見れるようにしよう

WebサーバにしたWindowsとは別の端末からWebサイトを開くにはSSL通信が必須です。要するに『http://・・・`はダメ、https://・・・はOK』ということです。

#### 2-1.構成図

SSL通信に必要な証明書を用意します。今回は自己証明書を使います。また、スマホをWebサーバー(Windows10)と通信可能にするため、同じネットワークにします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/d0f1f215-2b67-551d-f48f-a75f5683ae28.png" width="450">

#### 2-2.自己証明書を作ろう

Windwos環境では、OpenSSLで自己証明書が作れます。私はこちらのサイトを参考に作成しました。 → [Node.js v12でTLSv1.3のテストサーバを手軽に構築](https://qiita.com/marumaru/items/0c73eee045183a440152)

以下のコマンドで証明書を作ります。`xxx.xxx.xxx.xxx`には、Webサーバとなる環境のIPアドレスを設定します。
※注意※ IPアドレスを`127.0.0.1`とすると他の端末ではSSL通信できないかも（←ためしてない）

```console:openssl.exe

# 秘密鍵とサーバ証明書を作成するコマンド
OpenSSL> req -subj '/C=JP/ST=TestState/O=TestCompany/CN=xxx.xxx.xxx.xxx' \
 -x509 -nodes -days 3650 -newkey rsa:2048 \
 -keyout server-key.pem \
 -out server-cert.pem
```

#### 2-3.Webサーバーに証明書を適用しよう

作成した自己証明書を使い、SSL通信のWebサイトを作ります。1-2で紹介した `app.js` を以下のように編集し、app.jsと同じ階層に自己証明書（`server-key.pem`, `server-key.pem`）を格納します。これでSSL通信のWebサーバの準備完了です。

```javascript:app.js

"use strict";

// express を使う
var express = require("express");
var app = express();
app.use(express.static("wwwroot"));
app.get("/", function(req, res) {
  res.sendFile("wwwroot/index.html");
});

// http サーバ
var http = require("http").Server(app);
http.listen(3000);

// https サーバ
var fs = require("fs");
var opt = {
  key:  fs.readFileSync("server-key.pem"),
  cert: fs.readFileSync("server-cert.pem"),
};
var https = require("https").Server(opt, app);  // https サーバを立てる
https.listen(3001);
```

上記は、このようにポート番号で非暗号暗号(`https://・・`)/(`http://・・`)となります。スマホで開くアドレスは、暗号のURLになります。

- 非暗号化
    - `http://{WindowsのローカルIPアドレス}:3000/depression-01.html`
- 暗号化(SSL通信)
    - `https://{WindowsのローカルIPアドレス}:3001/depression-01.html`

暗号化(SSL通信)のサイトをスマホで開くとスマホ上で、凹んだオブジェクトが表示されます（iOS の Safari では動作を確認しました）

このようにスマホでARマーカを映し、凹んだオブジェクトが表示されれば成功です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/636bf18b-8a68-6f4a-3bd6-c950dacc917e.png" width="350">



## 3.インターネット公開しよう

多くの人に利用してもらうためインターネット公開します。ネット公開は[heroku](https://jp.heroku.com/)を使うなどありますが、私はAWSのS3を使用しました。

#### 3-1.構成図

こちらが構成図です、S3だけにしました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/35ce77f7-9a0a-d649-d4d0-13afc1db55cd.png" width="200">

S3に凹んだオブジェクトのソース一式を格納しインターネット公開すれば準備完了です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/8ad3a740-4361-0d42-7d3e-472160dc7a8e.png" width="400">

※S3上のファイルをインターネット公開すると、証明書を用意しなくてもSSL通信になります。
※Window10+Chrome, iOS+Safariの2環境で動作することを確認しました。


## 課題

一応作れたけど、課題はたくさんありそう

- 透明な画像を使用することで、凹んだオブジェクトを表現しましたが、正しい方法は別にあると思います（どーやるんだろ？）情報お持ちの方、教えて頂けると幸いです。
- 画像の張り方やオブジェクトの数はもっとシンプルにできそう
- スクリプトが汚い（いつか整頓します、、たぶん）


## さいごに

凹むオブジェクトを応用すると、何か面白いものが作れそうです。挑戦してみます、乞うご期待

ー追記ー

作ってみた

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">凹むARマーカ、からのリアルとデジタルを行き交う謎物体できた。<a href="https://t.co/3JXAdQHgGk">https://t.co/3JXAdQHgGk</a><a href="https://twitter.com/hashtag/webgl?src=hash&amp;ref_src=twsrc%5Etfw">#webgl</a> <a href="https://twitter.com/hashtag/threejs?src=hash&amp;ref_src=twsrc%5Etfw">#threejs</a> <a href="https://twitter.com/hashtag/AR?src=hash&amp;ref_src=twsrc%5Etfw">#AR</a> <a href="https://t.co/4eDjFrFxJb">pic.twitter.com/4eDjFrFxJb</a></p>&mdash; j4amountain (@zsipparu) <a href="https://twitter.com/zsipparu/status/1219771170863513600?ref_src=twsrc%5Etfw">January 21, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>