이번 글은 2개의 다른 마커 위에 각각 다른 가상화 객체를 만드는 방법을 다루었다. 기본적으로 nreal glasses에 대한 이해가 조금 필요하다.
1. NRCameraRig, NRInput 을 Hierarchy 창에 드래그&드랍
2. NRInput에서 Override Camera Center 부분을 NRCameraRig의 CenterCamera 드래그&드랍
3. 캔버스 추가 후 이벤트 시스템 제거
그리고 캔버스에서 Graphic Raycaster를 제거하고 Canvas Raycast Target을 추가한다.
캔버스에 버튼 등 인터렉션이 필요없으면 할필요 없다.
캔버스는 월드스페이스로 하고 nreal에서 제공하는 move with camera 스크립트를 컴포넌트로 주면 사용자 화면에 고정되게 된다. 거리 조정은 유니티 내에 실행하면서 해주어야 할 것이다.
4. empty gameobject 추가후 Tracking Image Visualizer 이름의 속성으로 추가
Tracking Image Example Controller를 컴포넌트로 가져온다.
Text : 이미지의 인덱스를 화면에서 확인하기 위한 객체
tracking Image Visualize에서 위에는 엔진 아래는 자동차
Fit To Scan Overlay는 이미지를 탐색할 때 나오는 텍스트이다.
위 컨트롤러는 글쓴이가 수정한 코드이다. 월래라면 등록된 이미지가 탐지되면 그위에 가상화할 객체를 하나만 보여주는 것이지만 글쓴이는 2개의 이미지에 각각 서로 다른 객체를 가상화 하고 싶었다.
아래는 수정된 코드이다.
namespace NRKernal.NRExamples
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Controller for TrackingImage example.
/// </summary>
[HelpURL("https://developer.nreal.ai/develop/unity/image-tracking")]
public class TrackingImageExampleController : MonoBehaviour
{
public Text text;
// A prefab for visualizing an TrackingImage.
public TrackingImageVisualizer TrackingImageVisualizerPrefab_1;
public TrackingImageVisualizer TrackingImageVisualizerPrefab_2;
// The overlay containing the fit to scan user guide.
public GameObject FitToScanOverlay;
private Dictionary<int, TrackingImageVisualizer> m_Visualizers
= new Dictionary<int, TrackingImageVisualizer>();
private List<NRTrackableImage> m_TempTrackingImages = new List<NRTrackableImage>();
public void Update()
{
#if !UNITY_EDITOR
// Check that motion tracking is tracking.
if (NRFrame.SessionStatus != SessionState.Running)
{
return;
}
#endif
// Get updated augmented images for this frame.
NRFrame.GetTrackables<NRTrackableImage>(m_TempTrackingImages, NRTrackableQueryFilter.New);
// Create visualizers and anchors for updated augmented images that are tracking and do not previously
// have a visualizer. Remove visualizers for stopped images.
foreach (var image in m_TempTrackingImages)
{
/* UnityEngine.Debug.Log("image.GetDataBaseIndex() : " + image.GetDataBaseIndex());
UnityEngine.Debug.Log("image.GetTrackableType() : " + image.GetTrackableType());
UnityEngine.Debug.Log("image.GetTrackingState() : " + image.GetTrackingState());*/
TrackingImageVisualizer visualizer = null;
m_Visualizers.TryGetValue(image.GetDataBaseIndex(), out visualizer);
if (image.GetTrackingState() == TrackingState.Tracking && visualizer == null)
{
NRDebugger.Log("Create new TrackingImageVisualizer!");
// Create an anchor to ensure that NRSDK keeps tracking this augmented image.
try
{
text.text = "Image Index : " + image.GetDataBaseIndex();
if (image.GetDataBaseIndex() == 4)
{
visualizer = (TrackingImageVisualizer)Instantiate(TrackingImageVisualizerPrefab_1, image.GetCenterPose().position, image.GetCenterPose().rotation);
}
else
{
visualizer = (TrackingImageVisualizer)Instantiate(TrackingImageVisualizerPrefab_2, image.GetCenterPose().position, image.GetCenterPose().rotation);
}
}
catch(NullReferenceException e) {
}
visualizer.Image = image;
visualizer.transform.parent = transform;
m_Visualizers.Add(image.GetDataBaseIndex(), visualizer);
}
else if (image.GetTrackingState() == TrackingState.Stopped && visualizer != null)
{
m_Visualizers.Remove(image.GetDataBaseIndex());
Destroy(visualizer.gameObject);
}
FitToScanOverlay.SetActive(false);
}
}
public void EnableImageTracking()
{
var config = NRSessionManager.Instance.NRSessionBehaviour.SessionConfig;
config.ImageTrackingMode = TrackableImageFindingMode.ENABLE;
NRSessionManager.Instance.SetConfiguration(config);
}
public void DisableImageTracking()
{
var config = NRSessionManager.Instance.NRSessionBehaviour.SessionConfig;
config.ImageTrackingMode = TrackableImageFindingMode.DISABLE;
NRSessionManager.Instance.SetConfiguration(config);
}
}
}
여기서 추가한 핵심 코드는 image.GetDataBaseIndex() 코드이다.
GetDataBaseIndex() 코드는 데이터베이스에 등록된 이미지의 인덱스 번호를 반환하는 메소드이다.
여기서 주의할 점이 있다. 유니티 내부에서 실행했을 때 이미지의 인덱스 번호와 AR 안경을 착용하고 봤을 때 이미지의 인덱스 번호가 다르다는 점이다 . 따라서 ar 안경을 착용하고 이미지의 인덱스 번호를 확인하기 위해 유니티 캔버스에 Text를 이용하여 직접 인덱스 번호를 GetDataBaseIndex를 사용하여 확인하길 바란다.
5. 트래킹 이미지 벌추얼라이저 만들기
위 이미지 처럼 엠티 오브젝트에 추적된 이미지 위에 가상화할 객체를 자식으로 넣어준다. 그리고 엠티 오브젝트에 컴포넌트로
를 넣어주어야 한다. Axis 값은 자식으로 주었던 객체를 넣어주면 된다.
글쓴이는 예제 코드를 조금 수정하였다.
namespace NRKernal.NRExamples
{
using UnityEngine;
/// <summary>
/// Uses 4 frame corner objects to visualize an TrackingImage.
/// </summary>
public class TrackingImageVisualizer : MonoBehaviour
{
// The TrackingImage to visualize.
public NRTrackableImage Image;
/* // A model for the lower left corner of the frame to place when an image is detected.
public GameObject FrameLowerLeft;
// A model for the lower right corner of the frame to place when an image is detected.
public GameObject FrameLowerRight;
/// A model for the upper left corner of the frame to place when an image is detected.
public GameObject FrameUpperLeft;
// A model for the upper right corner of the frame to place when an image is detected.
public GameObject FrameUpperRight;*/
public GameObject Axis;
public void Update()
{
if (Image == null || Image.GetTrackingState() != TrackingState.Tracking)
{
/* FrameLowerLeft.SetActive(false);
FrameLowerRight.SetActive(false);
FrameUpperLeft.SetActive(false);
FrameUpperRight.SetActive(false);*/
Axis.SetActive(false);
return;
}
/* float halfWidth = Image.ExtentX / 2;
float halfHeight = Image.ExtentZ / 2;*/
/* FrameLowerLeft.transform.localPosition = (halfWidth * Vector3.left) + (halfHeight * Vector3.back);
FrameLowerRight.transform.localPosition = (halfWidth * Vector3.right) + (halfHeight * Vector3.back);
FrameUpperLeft.transform.localPosition = (halfWidth * Vector3.left) + (halfHeight * Vector3.forward);
FrameUpperRight.transform.localPosition = (halfWidth * Vector3.right) + (halfHeight * Vector3.forward);*/
var center = Image.GetCenterPose();
transform.position = center.position;
transform.rotation = center.rotation;
/* FrameLowerLeft.SetActive(true);
FrameLowerRight.SetActive(true);
FrameUpperLeft.SetActive(true);
FrameUpperRight.SetActive(true);*/
Axis.SetActive(true);
}
}
}
글쓴이는 모서리 부분을 알 필요없이 이미지 중앙에다가 객체만 띠우고 싶어서 위와 같이 소스코드를 수정하였다.
그러면 원하는 객체만 이미지 위에 나타나게 된다.
그외 글쓴이는 엔진을 가상화 하였는데 그림자 때문에 엔진이 잘 보이지 않았다 그래서 아래 블로그를 참조 하였다.
위 이미지를 다시 살펴보면 sender IP와 target IP가 같음을 알 수 있다. 따라서 MAC 주소를 확인할 필요가 없는
쓸데없는 ARP 라는 것이다. 그럼에도 이는 의미가 있는데 3가지 용도가 있다.
1) IP 주소 충돌 감지 -> 자신의 IP주소를 타깃으로 하여 ARP 요청을 보냈는데 만약 다른 호스트에서 이에 대한 응답이 있다면 IP 충돌을 감지한다.
2) ARP Table 갱신 -> 자신의 IP를 가지고있는 단말 장치가 있다면 MAC 주소를 갱신한다.
3) VRRP/HSRP -> VRRP/HSRP가 enable된 라우터 중 Master에 해당하는 라우터에 해당하는 자신과 연결된 L2 switch의 Mac address table 정보를 갱신(해당 MAC 주소에 대한 Port 정보 갱신) 하기 위해 GARP를 사용한다.