AR Pointer Tracker

관련 문서
Tracker Coordinate System in Unity

1. Definition
2. Video See-Through
2.1. Setting Up an ARPointer Tracker Scene
2.2. Setting Up ARPointerTracker.cs
2.3. Entire Code for ARPointerTracker


1. Definition

You can augment specific locations of objects that the user is viewing through the camera with augmented reality content.


2. Video See-Through

ARPointer Tracker only supports Video See-Through.

2.1. Setting Up an ARPointer Tracker Scene

  1. Install the MAXST AR SDK for Unity

  2. Create the new scene.

  3. Delete the Main Camera(default) at ‘Project Tab > Assets > MaxstAR > Prefabs > ARCamera’ to the Scene. artracker1.png

  4. In the Hierarchy, create an empty GameObject (name it SceneManager) and add a new script as a component. Name the script ARPointerTracker.cs. artracker2.png

  5. Add an empty GameObject and name it 'Tracker'. artracker4.png

  6. Add the object you want to track (ARPointer) to the Tracker. artracker3.png

  7. Complete the ARPointerTracker.cs setup as below, and assign each GameObject to the ARPointerTracker component on the SceneManager GameObject. artracker5.png

2.2. Setting up ARPointerTracker.cs

  1. Adding a Parent Class(ARBehaviour) to ARPointerTracker.cs
 
public class AARPointerTracker : ARBehaviour

  1. You need to add variables to the ARPointerTracker.cs class. Please add the following code as class variables.
 
    private CameraBackgroundBehaviour cameraBackgroundBehaviour = null;
    private int screenWidth;
    private int screenHeight;
    private int cameraWidth = 1280;
    private int cameraHeight = 720;
    private Matrix4x4 currentPose = Matrix4x4.identity;
    private int anchorCount = 0;
    private List<string> anchorList = new List<string>();
    private Dictionary<String, Matrix4x4> anchorDictionary = new Dictionary<String, Matrix4x4>();
    private Dictionary<String, GameObject> anchorGameObjectDictionary = new Dictionary<String, GameObject>();
    public GameObject trackingObject;
    public GameObject arCamera;
    public GameObject pointObject;
    String anchorName = "";
  1. You need to initialize ARPointerTracker.cs. Please add the following code to the Awake function.
 
  void Awake()
    {

        Init();

        AndroidRuntimePermissions.Permission[] result = AndroidRuntimePermissions.RequestPermissions("android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.CAMERA");
        if (result[0] == AndroidRuntimePermissions.Permission.Granted && result[1] == AndroidRuntimePermissions.Permission.Granted)
            Debug.Log("We have all the permissions!");
        else
            Debug.Log("Some permission(s) are not granted...");

        cameraBackgroundBehaviour = FindObjectOfType
  1. You need to add start and stop functionality to the tracker. Please add the following methods to the ARPointerTracker.cs class.
     
    void Start()
    {
        screenWidth = Screen.width;
        screenHeight = Screen.height;

        CameraDevice.GetInstance().Start();
        TrackerManager.GetInstance().StartTracker(0x1000);
        StartCoroutine(FindSurface());
    }

    void OnDestroy()
    {
        CameraDevice.GetInstance().Stop();
        TrackerManager.GetInstance().StopTracker();
        TrackerManager.GetInstance().DestroyTracker();
    }

    void OnApplicationPause(bool pause)
    {
        if (pause)
        {
            TrackerManager.GetInstance().StopTracker();
        }
        else
        {
            CameraDevice.GetInstance().Start();
            TrackerManager.GetInstance().StartTracker(0x1000);
            StartCoroutine(FindSurface());
        }
    }

  1. The code that learns the current real-world view of the ARPointerTracker.
 
    IEnumerator FindSurface()
    {
        yield return new WaitForSeconds(0.2f);
        anchorList.Clear();
        TrackerManager.GetInstance().QuitFindingSurface();
        TrackerManager.GetInstance().FindSurface();
    }
  1. To use the tracking information, add the following code to the Update function. The code below performs computations of the learned anchor position using the tracking position obtained from the camera.
 
    void Update()
    {
        if(screenWidth != Screen.width)
        {
            screenWidth = Screen.width;
            screenHeight = Screen.height;
        }

        TrackingState state = TrackerManager.GetInstance().UpdateTrackingState();

        if (state == null)
        {
            return;
        }

        cameraBackgroundBehaviour.UpdateCameraBackgroundImage(state);
        cameraWidth = state.GetImage().GetWidth();
        cameraHeight = state.GetImage().GetHeight();
        

        TrackingResult trackingResult = state.GetTrackingResult();

        int trackingCount = trackingResult.GetCount();
        if (trackingCount == 0)
        {
            return;
        }

        if (Input.GetMouseButtonUp(0))
        {
            UpdateTouchDelta(Input.mousePosition);
        }

        for (int i = 0; i < trackingCount; i++)
        {
            Trackable trackable = trackingResult.GetTrackable(i);

            if (trackable.GetName() == "status")
            {
                continue;
            }
            else if (trackable.GetName() == "default")
            {
                currentPose = trackable.GetOriginalPose();
                continue;
            }

            if (!anchorDictionary.ContainsKey(trackable.GetName()))
            {
                anchorDictionary.Add(trackable.GetName(), trackable.GetOriginalPose());
                RemoveAnchor(trackable.GetName());
            }
        }

        foreach (string anchorName in anchorDictionary.Keys)
        {
            Matrix4x4 anchorPose = anchorDictionary[anchorName];
            Matrix4x4 currentAnchorPose = anchorPose;
            Matrix4x4 multiplyPose = currentPose * currentAnchorPose;
            Matrix4x4 targetPose = MatrixUtils.GetConvertPose(multiplyPose);

            GameObject anchorGameObject = anchorGameObjectDictionary[anchorName];

            anchorGameObject.transform.position = MatrixUtils.PositionFromMatrix(targetPose);
            anchorGameObject.transform.rotation = MatrixUtils.QuaternionFromMatrix(targetPose);
            anchorGameObject.transform.localScale = MatrixUtils.ScaleFromMatrix(targetPose);
        }
    }

  1. This is the method to acquire the Anchor you want to track using touch coordinates on the screen.
 
    private void UpdateTouchDelta(Vector2 touchPosition)
    {
        if(Input.GetMouseButtonUp(0))
        {
            PointerEventData pointer = new PointerEventData(EventSystem.current);

            float end_x = touchPosition.x;
            float end_y = screenHeight - touchPosition.y;
            Matrix4x4 lastPose = currentPose;

            anchorName = GetAnchorName(true);
            Vector3 end_ncoord = ScreenToLocal(end_x, end_y);
            AddPrevAnchor(anchorName, end_ncoord.x, end_ncoord.y, MatrixToFloat(lastPose));

            GameObject cloneObject = GameObject.Instantiate(pointObject);
            anchorGameObjectDictionary[anchorName] = cloneObject;
            cloneObject.transform.parent = trackingObject.transform;
            cloneObject.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
            cloneObject.SetActive(true);
        }
    }

  1. Here is the code for normalizing the touch position based on the screen size. It is used in the UpdateTouchDelta function above.
 
    private Vector3 ScreenToLocal(float screenX, float screenY)
    {
        float sw = screenWidth;
        float sh = screenHeight;

        float cw = cameraWidth;
        float ch = cameraHeight;

        float nw = (sw > sh) ? sw : sh;
        float nh = (sw > sh) ? sh : sw;

        float px = (sw > sh) ? screenX : screenY;
        float py = (sw > sh) ? screenY : nh - screenX;

        float rw = nw / cw;
        float rh = nh / ch;

        float margin_nh = 0.0f;
        float margin_nw = 0.0f;

        // cut height
        if (rw > rh)
        {
            float vh = rw * ch;
            margin_nh = (vh - nh) * 0.5f;
            py += margin_nh;
        }
        else
        {
            float vw = rh * cw;
            margin_nw = (vw - nw) * 0.5f;
            px += margin_nw;
        }

        float nx = (px - (nw * 0.5f + margin_nw)) / nw;
        float ny = (py - (nh * 0.5f + margin_nh)) / nw;

        return new Vector3(nx, ny, 1.0f);
    }

  1. Code to add an Anchor using touch. It is used in UpdateTouchDelta.
 
    void AddPrevAnchor(String anchorName, float normalizedCenterPosX, float normalizedCenterPosY, float[] prevPose)
    {
        String strPose = "";
        for (int i = 0; i < 15; i++)
            strPose += prevPose[i] + ",";
        strPose += prevPose[15];
        String addCommand = "{\"vivar\":\"add_prev_anchor_normalized\",\"name\":\"" + anchorName + "\",\"x_pos\":" + normalizedCenterPosX + ",\"y_pos\":" + normalizedCenterPosY +
                ",\"search_pose\":\"" + strPose + "\"}";

        TrackerManager.GetInstance().AddTrackerData(addCommand, false);

        anchorList.Add(anchorName);

        Debug.Log(addCommand);
    }

    //Deletes the existing Anchor in use.
    void RemoveAnchor(String anchorName)
    {
        String removeCommand = "{\"vivar\":\"remove_anchor\",\"name\":\"" + anchorName + "\"}";
        TrackerManager.GetInstance().AddTrackerData(removeCommand, false);
    }

2.3. Entire Code for ARPointerTracker

Here is the complete code for the ARPointerTracker.cs file, which is added to the SceneManager Object.

ARPointerTracker.cs.zip