AR模型脫卡unity端實現具體步驟
AR模型脫卡的原理
利用一些unity端AR插件做AR應用。通常會有一個需求,當識別物消失的時候,將3D模型從識別物這個父物體上移除,顯示在屏幕中央。那么原理就顯而易見了,就是在識別物追蹤方法中,寫一些模型的操作(判定當前模型顯示、隱藏非當前模型)
實現方式
- 兩個攝像機,丟失追蹤后。移除父物體關聯,用另一個相機進行渲染。其實就是一個相機坐標系的轉換。(稍顯復雜)
- 丟失追蹤后,在主相機中創建一個空物體放置模型。(比較簡單)
核心文件的編輯(簡單點的)
NotFound.cs
文件的編輯
using UnityEngine;
using System.Collections;
using Vuforia;
using System;
//拿到當前追蹤識別
using UnityEngine;
using System.Collections;
using Vuforia;
using System;
using UnityEngine.SceneManagement;
public class NotFound : MonoBehaviour , ITrackableEventHandler
{
#region PRIVATE_MEMBER_VARIABLES
private TrackableBehaviour mTrackableBehaviour;
//判斷是否是第一次識別是否完成,防止開啟程序未放入識別圖也在屏幕中央出現模型
bool firstfound = false;
public bool imageIsOut = false;//識別成功的圖片是否已經出現過
//模型起始位置,值為起始模型組件中Transform.Position
//Vector3 origposition = new Vector3 (0, 0.25f, 0);
public Vector3 originPosition;
public Vector3 rotation;
public Vector3 it_position;
public Vector3 it_rotation;
//當前imagetarget對應的模型等target
public Transform[] localTargets;
//其他非當前imagetarget對應的模型等targets
public Transform[] otherTargets;
//Camera Object
GameObject gObject;
//Camera Object的Creat Empty腳本
CreatEmpty cempty;
//表示ImageTarget父物體那個組件
public GameObject[] otherImageTargets;
#region 自定義的協程延時函數
//定義一個延時函數
public static IEnumerator DelayToInvokeDo(Action action, float delaySeconds)
{
yield return new WaitForSeconds(delaySeconds);
action();
}
#endregion //自定義的協程延時函數
void Start()
{
//相機自動對焦
Vuforia.CameraDevice.Instance.SetFocusMode(Vuforia.CameraDevice.FocusMode.FOCUS_MODE_CONTINUOUSAUTO);
mTrackableBehaviour = GetComponent<TrackableBehaviour>();
if (mTrackableBehaviour)
{
mTrackableBehaviour.RegisterTrackableEventHandler(this);
}
//獲取Camera中組件的CreatEmpty腳本
gObject = GameObject.FindWithTag("MainCamera");
cempty = gObject.GetComponent<CreatEmpty> ();
if(cempty != null)
{
//殺死空物體
cempty.destoryempty ();
}
}
#endregion // UNTIY_MONOBEHAVIOUR_METHODS
#region PUBLIC_METHODS
/// <summary>
/// Implementation of the ITrackableEventHandler function called when the
/// tracking state changes.
/// </summary>
public void OnTrackableStateChanged(
TrackableBehaviour.Status previousStatus,
TrackableBehaviour.Status newStatus)
{
//Vector3 orirotation = new Vector3 (270, 0, 0);
if (newStatus == TrackableBehaviour.Status.DETECTED ||
newStatus == TrackableBehaviour.Status.TRACKED ||
newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
{
//如果識別成功圖片沒有出現過,則執行下面的代碼,顯示並延時0.5秒后消失
if(!imageIsOut)
{
for(int i = 0; i < otherTargets.Length; i++)
{
//但是隱藏其他imagetarget對應的模型,目的是防止在該imagetarget對應的模型出現在屏幕中央的時候不受其他imagetarget對應的模型的影響
//otherTargets[i].gameObject.SetActive (false);
setShow (otherTargets [i], false);
}
for(int i = 0; i < localTargets.Length; i++)
{
//localTargets[i].gameObject.SetActive (false);
setShow (localTargets [i], false);
}
//需要延時0.3s,讓圖片多顯示0.3s后消失(執行outImage函數)
//Invoke ("outImage", 0.5f);//如果這樣,並不影響下面代碼的執行。
StartCoroutine(DelayToInvokeDo(() =>
{
outImage(newStatus);
//識別成功圖片已經顯示過了
imageIsOut = true;
}, 0.4f));
}
else
{
OnTrackingFound ();
}
}
else
{
OnTrackingLost ();
}
}
#endregion // PUBLIC_METHODS
//識別成功圖片顯示並消失之后
private void outImage(TrackableBehaviour.Status newStatus)
{
for(int i = 0; i < otherTargets.Length; i++)
{
//但是隱藏其他imagetarget對應的模型,目的是防止在該imagetarget對應的模型出現在屏幕中央的時候不受其他imagetarget對應的模型的影響
//otherTargets[i].gameObject.SetActive (false);
setShow (otherTargets [i], false);
}
for(int i = 0; i < localTargets.Length; i++)
{
//localTargets[i].gameObject.SetActive (false);
setShow (localTargets [i], false);
}
//顯示並消失之后在一次判斷圖片是否處於追蹤狀態
if (newStatus == TrackableBehaviour.Status.TRACKED ||
newStatus == TrackableBehaviour.Status.DETECTED ||
newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
{
OnTrackingFound ();
firstfound = true;
}
else
{
//OnTrackingLost ();
for(int i = 0; i < otherTargets.Length; i++)
{
//但是隱藏其他imagetarget對應的模型,目的是防止在該imagetarget對應的模型出現在屏幕中央的時候不受其他imagetarget對應的模型的影響
//otherTargets[i].gameObject.SetActive (false);
setShow (otherTargets [i], false);
}
for(int i = 0; i < localTargets.Length; i++)
{
//localTargets[i].gameObject.SetActive (false);
setShow (localTargets [i], false);
}
}
}
private void OnTrackingFound()
{
Debug.Log ("it is found");
for(int i = 0; i < otherImageTargets.Length; i++)
{
//讓其他的識別圖在執行一次狀態識別成功的代碼,使得模型歸位到imagetarget子物體
otherImageTargets[i].GetComponent<NotFound> ().homing ();
//當前圖片識別成功時,關閉其他圖片的識別
// otherImageTargets[i].SetActive (false);
//將其他識別圖中的識別成功圖片設置成未顯示過
otherImageTargets [i].GetComponent<NotFound> ().imageIsOut = false;
}
for(int i = 0; i < localTargets.Length; i++)
{
//setShow (localTargets [i], true);
//othertargetmodel.gameObject.SetActive (false);
localTargets[i].parent = this.transform;
localTargets[i].localPosition = it_position;
localTargets[i].rotation = Quaternion.Euler (it_rotation);
//開啟當前圖片對應的模型,因為在識別其他圖片的時候有可能關閉了這個圖片對應的模型
//localTargets[i].gameObject.SetActive (true);
setShow (localTargets [i], true);
}
//殺死空物體
cempty.destoryempty ();
//因為殺死了空物體,所以一旦丟失跟蹤,模型就沒地方放了,所以就加一個判斷,丟失的話就不顯示模型
if(mTrackableBehaviour.CurrentStatus ==TrackableBehaviour.Status.NOT_FOUND)
{
for(int i = 0;i < localTargets.Length; i++)
{
setShow (localTargets [i], false);
}
}
//target.rotation = Quaternion.Euler(orirotation);
//firstfound = true;
}
private void OnTrackingLost()
{
Debug.Log ("it is lost");
for(int i = 0; i < localTargets.Length; i++)
{
//localTargets[i].gameObject.SetActive (false);
setShow (localTargets [i], false);
}
if (firstfound == true)
{
//創建空物體
cempty.creatempty ();
for(int i = 0; i < otherTargets.Length; i++)
{
//但是隱藏其他imagetarget對應的模型,目的是防止在該imagetarget對應的模型出現在屏幕中央的時候不受其他imagetarget對應的模型的影響
//otherTargets[i].gameObject.SetActive (false);
setShow (otherTargets [i], false);
}
GameObject emptyobject = GameObject.Find ("empty");
Transform cameraPos = emptyobject.transform;
for(int i = 0; i < localTargets.Length; i++)
{
//將target作為cameraPos的子物體,cameraPos就是emptyobject(即空物體)的Transform表現形式
localTargets[i].parent = cameraPos;
localTargets[i].localPosition = originPosition;
//target.localRotation = Quaternion.Euler (orirotation);
// localTargets[i].localRotation = Quaternion.Euler (Vector3.zero);
// localTargets[i].localRotation = Quaternion.Euler(rotation);
//localTargets[i].gameObject.SetActive (true);
setShow (localTargets [i], true);
}
}
else
{
for(int i = 0; i < otherTargets.Length; i++)
{
//但是隱藏其他imagetarget對應的模型,目的是防止在該imagetarget對應的模型出現在屏幕中央的時候不受其他imagetarget對應的模型的影響
//otherTargets[i].gameObject.SetActive (false);
setShow (otherTargets [i], false);
}
for(int i = 0; i < localTargets.Length; i++)
{
print ("local Targets set active false");
//localTargets[i].gameObject.SetActive (false);
setShow (localTargets [i], false);
}
}
}
//方便其他腳本調用的接口函數,使this.中模型歸位
public void homing()
{
for(int i = 0; i < localTargets.Length; i++)
{
localTargets[i].position = new Vector3(0,0,0);
localTargets[i].rotation = Quaternion.Euler (Vector3.zero);
localTargets[i].parent = this.transform;
}
}
//自己寫的隱藏模型代碼,僅模型可用
void setShow(Transform target,bool IsShow)
{
Renderer[] targetrendererComponents = target.GetComponentsInChildren<Renderer>(true);
Collider[] targetcolliderComponents = target.GetComponentsInChildren<Collider>(true);
if(IsShow)
{
// enable rendering:
foreach (Renderer component in targetrendererComponents)
{
component.enabled = true;
}
// enable colliders:
foreach (Collider component in targetcolliderComponents)
{
component.enabled = true;
}
}
else
{
// Disable rendering:
foreach (Renderer component in targetrendererComponents)
{
component.enabled = false;
}
// Disable colliders:
foreach (Collider component in targetcolliderComponents)
{
component.enabled = false;
}
}
}
}
CreatEmpty.cs
文件的編寫
using UnityEngine;
using System.Collections;
using Vuforia;
public class CreatEmpty : MonoBehaviour {
Vector3 emptyposition = new Vector3 (16, -62, 120);
public void creatempty()
{
//定義一個空物體,並將他作為Camera的子物體,並設置其坐標和旋轉角度
GameObject emptyObject = new GameObject ();
emptyObject.transform.parent = GameObject.FindWithTag ("MainCamera").transform;
emptyObject.name = "empty";
emptyObject.transform.localPosition = emptyposition;
emptyObject.transform.localRotation = Quaternion.Euler (Vector3.zero);
}
//殺死camera組件中名字為“empty”的物體
public void destoryempty()
{
if (GameObject.Find ("empty"))
GameObject.Destroy (GameObject.Find ("empty"));
else
print ("沒有empty!");
}
}
實現步驟(復雜點的)
-
添加新的Camera,並將Camera配置如下:
這里有個坑就是,圖層的depth
屬性的設置。值越高圖層顯示優先級越高。 -
對於預制體添加標志Compent
這里添加兩個組件Rotate
是控制模型旋轉的,另外一個是一個標志。方便我們通過組件查找到我們要訪問的Object。 -
核心追蹤的方法實現,掛靠到imageTarget上
public string ObjectName;
private void OnTrackingFound()
{
//初始化模型
TrackObjectFree[] objs = FindObjectsOfType<TrackObjectFree> ();
foreach (TrackObjectFree to in objs) {
Destroy (to.gameObject);
}
Resources.UnloadUnusedAssets ();
//創建模型
GameObject o = GameObject.Instantiate (Resources.Load (ObjectName)) as GameObject;
o.transform.parent = this.transform;
o.transform.position = this.transform.position;
}
private void OnTrackingLost()
{
TrackObjectFree to = GetComponentInChildren<TrackObjectFree> ();
if (to != null)
{
to.gameObject.transform.parent = this.transform.parent;
to.gameObject.layer = 10;
}
}