読者です 読者をやめる 読者になる 読者になる

VR 研究記

3D技術の経験を活かして、VRに挑戦するおっさんの奮闘記

360°パノラマビューをUnityで作ってみた

今流行の360°ビューの仕組みとは

細かく書くと難しくなるので、簡単に書いていきます。
360°撮影できる機材を使ってパノラマ撮影した画像・映像を
擬似空間の球体(正距円筒球)の内側に張り付けて球体の中心に
カメラを設置してユーザーに見せる事であたかも自分の周りに
撮影した景色があるかのように見える技術の一つです。
ポイントは、正距円筒球を使う事です。

正距円筒球とは何か?

擬似空間でのポリゴンの表現がどんな形状で出来ているかご存知でしょうか?
実は、3D空間ではすべて3角形で表現します。

f:id:subrutm:20161109104137p:plain

美術でも遠近法が用いられることにより奥行きを感じるのと同じように
カメラから離れた位置にある三角形メッシュほど、縦長に大きくなることが
分かると思います。

画像を張り付ける手法は、頂点情報に画像の座標を記録し、それを
三角形メッシュで描画しているので画像を加工しないでそのまま
描画すると不自然な形になるのはイメージ出来ますでしょうか?

違和感の無い画像表現をする為には、球の頂点位置と画像座標が適切に
マッチさせてあげる必要があると理解できると思います。

ここからは、テクスチャーマッピングの話です。
下記のように、平面(ABCD)と画像(abcd)があるとします。
法線は、あなたに向かってくる方向が法線ベクトルの方向とします。
f:id:subrutm:20161109131215p:plain

  • ABCD = abcdの場合

元画像の解像度と等倍で正常に表示される。

  • ABCD > abcdの場合

元画像の解像度より拡大して表示される。

  • ABCD < abcdの場合

元画像の解像度より縮小して表示される。

この例から分かるように、3D空間のメッシュの大きさと画像の大きさが
マッチしないと倍率に差が出てしまい、表示が不自然になってしまう。
だから、球面の三角形に合わせた画像を作る必要があり、その手法が
正距円筒図法と呼ばれます。
これを応用すると、パノラマ投影法[プロジェクションマッピング]
になる訳です。

詳細な変形手法としては、詳しいサイトがありますのでそちらを参照下さい。
空雲礼賛

正距円筒球を入手する方法は?

正距円筒球の入手手段は、インターネットでダウンロードか
機材を用いて撮影するかの二択になります。

インターネットでダウンロード

  1. 画像検索で、「360-degree photo」をキーワードに検索する
  2. 写真共有サイト Flickrから写真をダウンロードする。*1

機材を使って撮影

下記は、360°パノラマ撮影するための機材です。

  1. GoPro Kolor*2
  2. Google Jump*3
  3. JauntVR NEO*4

Unityで360°パノラマビューを作ってみる

上記の何れかの方法で入手した画像素材を利用して、360°ビューを表示してみましょう。

まずは、球体を作成します。

f:id:subrutm:20161109120208p:plain

写したいパノラマ画像を作成した球体にドラッグアンドドロップします。
すると、球の外側に画像が表示されると思います。
これを内側に切替える方法は下記の通りです。

手順1. カスタムシェーダーを作成する

①カスタムシェーダーファイルを新規作成する
f:id:subrutm:20161109142152p:plain

②以下のコードに書き換える。

Shader "Custom/InwardShader" {
	Properties {
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200

		Cull Front
		
		CGPROGRAM
		// Physically based Standard lighting model, and enable shadows on all light types
		#pragma surface surf Standard vertex:vert//fullforwardshadows

		// 法線ベクトルを内側に向ける設定
		void vert(inout appdata_full v) {
			v.normal.xyz = v.normal * -1;
		}

		sampler2D _MainTex;

		struct Input {
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutputStandard o) {
			// Albedo comes from a texture tinted by color
			fixed4 c = tex2D(_MainTex, IN.uv_MainTex);// * _Color;
			o.Albedo = c.rgb;
		}
		ENDCG
	}
	FallBack "Diffuse"
}
手順2. Materialの設定を利用する

f:id:subrutm:20161109141441p:plain
①: 影は無用なのでチェックを外す
②: マテリアルのシェーダー設定を作成したシェーダーに変更する

手順3. MainCameraの位置を球体の中心に設定する

f:id:subrutm:20161109144142p:plain

手順4. 環境光を消して、スポットライトで内部を照らす

①環境光を消す
f:id:subrutm:20161109144424p:plain

②スポットライトを新規作成する
f:id:subrutm:20161109144948p:plain

③必要ならパラメータを修正する
f:id:subrutm:20161109145055p:plain

手順5. 球体を回転させるスクリプトを作成
using UnityEngine;
using System.Collections;

public class Rotator : MonoBehaviour {

    public Vector3 rotateRate;

    // Use this for initialization
    void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
        transform.Rotate(rotateRate * Time.deltaTime);
    }
}

作成したスクリプトを球体にアタッチして再生すると、360°パノラマビューの
簡単なテストアプリの完成です。