사용 방법
  1. 빈 GameObject에 DynamicDataSetLoader.cs(아래 스크립트)를 추가한다.
  2. Inspector에 멤버 변수를 채운다
    Augmentation Object - 마커 하위 오브젝트로 추가될 오브젝트를 넣는 곳
    Data Set Name - DataSet의 이름을 적는 곳
사용 결과
씬이 프로그램이 실행될 때 활성화 되지 않은 DataSet을 로드 시키고, 그안에 있는 마커를 하나씩 로딩한다.
단, 1000개 이상의 마커는 1000개만 생성됨)

응용 방법
마커가 많을 경우 기능별 분류를 통해 DataSet을 분류 해놓고 특정 상황마다 DataSet을 로딩하면 된다.
DynamicDataSetLoader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
using UnityEngine;
using System.Collections;
 
using Vuforia;
using System.Collections.Generic;
 
 
public class DynamicDataSetLoader : MonoBehaviour
{
    // specify these in Unity Inspector
    public GameObject augmentationObject = null;  // you can use teapot or other object
    public string dataSetName = "";  //  Assets/StreamingAssets/QCAR/DataSetName
 
    // Use this for initialization
    void Start()
    {
        // Vuforia 6.2+
        VuforiaARController vac = VuforiaARController.Instance;
        vac.RegisterVuforiaStartedCallback(LoadDataSet);
    }
 
    void LoadDataSet()
    {
 
        ObjectTracker objectTracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
 
        DataSet dataSet = objectTracker.CreateDataSet();
 
        if (dataSet.Load(dataSetName))
        {
 
            objectTracker.Stop();  // stop tracker so that we can add new dataset
 
            if (!objectTracker.ActivateDataSet(dataSet))
            {
                // Note: ImageTracker cannot have more than 100 total targets activated
                Debug.Log("<color=yellow>Failed to Activate DataSet: " + dataSetName + "</color>");
            }
 
            if (!objectTracker.Start())
            {
                Debug.Log("<color=yellow>Tracker Failed to Start.</color>");
            }
 
            int counter = 0;
 
            IEnumerable<TrackableBehaviour> tbs = TrackerManager.Instance.GetStateManager().GetTrackableBehaviours();
            foreach (TrackableBehaviour tb in tbs)
            {
                if (tb.name == "New Game Object")
                {
 
                    // change generic name to include trackable name
                    tb.gameObject.name = ++counter + ":DynamicImageTarget-" + tb.TrackableName;
 
                    // add additional script components for trackable
                    tb.gameObject.AddComponent<DefaultTrackableEventHandler>();
                    tb.gameObject.AddComponent<TurnOffBehaviour>();
 
                    if (augmentationObject != null)
                    {
                        // instantiate augmentation object and parent to trackable
                        GameObject augmentation = (GameObject)GameObject.Instantiate(augmentationObject);
                        augmentation.transform.parent = tb.gameObject.transform;
                        augmentation.transform.localPosition = new Vector3(0f, 0f, 0f);
                        augmentation.transform.localRotation = Quaternion.identity;
                        augmentation.transform.localScale = new Vector3(0.005f, 0.005f, 0.005f);
                        augmentation.gameObject.SetActive(true);
                    }
                    else
                    {
                        Debug.Log("<color=yellow>Warning: No augmentation object specified for: " + tb.TrackableName + "</color>");
                    }
                }
            }
        }
        else
        {
            Debug.LogError("<color=yellow>Failed to load dataset: '" + dataSetName + "'</color>");
        }
    }
}
cs




지페가 구겨져있어서 그런지 몰라도 간혈적으로 내부 라이브러리가 마커가 잡힌 이벤트를 연속으로 보내서 오브젝트가 번갈아 보이는 현상이 발생함

테스트를 다른것으로 해봐야 될듯

이벤트 처리를 위해 ITrackableEventHandler를 추가한다.
1
public class TargetTracker : MonoBehaviour, ITrackableEventHandler
cs


해당 타겟의 TrackableBehaviour 컴포넌트를 받아 해당 스크립트에서 이벤트를 구독할 수 있도록 등록한다.
1
2
3
4
5
mTrackableBehaviour = GetComponent<TrackableBehaviour>();
            if (mTrackableBehaviour)
            {
                mTrackableBehaviour.RegisterTrackableEventHandler(this);
            }
cs


ITrackableEventHandler의 OnTrackableStateChanged 메소드를 구현해야 한다.(이부분에서 마커 트렉킹에 대한 정보가 나온다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void OnTrackableStateChanged(TrackableBehaviour.Status previousStatus, TrackableBehaviour.Status newStatus)
        {
            print("Test");
            if (newStatus == TrackableBehaviour.Status.DETECTED || newStatus == TrackableBehaviour.Status.TRACKED ||
                       newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
            {
                print("TargetTracker : onTrackingFound");
                OnTrackingFound();
            }
            else
            {
                print("TargetTracker : onTrackingLost");
                OnTrackingLost();
            }
        }
cs



전체소스

TargetTracker.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using UnityEngine;
using System;
using System.Collections;
namespace Vuforia
{
    public class TargetTracker : MonoBehaviour, ITrackableEventHandler
    {
        private TrackableBehaviour mTrackableBehaviour;
        public event Action OnTrackingFound = () => { };
        public event Action OnTrackingLost = () => { };
 
        void Start()
        {
            mTrackableBehaviour = GetComponent<TrackableBehaviour>();
            if (mTrackableBehaviour)
            {
                mTrackableBehaviour.RegisterTrackableEventHandler(this);
            }
        }
 
        public void OnTrackableStateChanged(TrackableBehaviour.Status previousStatus, TrackableBehaviour.Status newStatus)
        {
            print("Test");
            if (newStatus == TrackableBehaviour.Status.DETECTED || newStatus == TrackableBehaviour.Status.TRACKED ||
                       newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
            {
                print("TargetTracker : onTrackingFound");
                OnTrackingFound();
            }
            else
            {
                print("TargetTracker : onTrackingLost");
                OnTrackingLost();
            }
        }
        
 
    }
}
cs








마커가 인식되면 생성될 오브젝트안에 들어갈 스크립트

targetTrackListener.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
 
namespace Vuforia
{
 
    public class targetTrackListener : MonoBehaviour
    {
 
        public bool visiToggle;
 
        private TargetTracker targetObject = null;
 
        void Start()
        {
            targetObject = GameObject.Find("ImageTarget (1)").GetComponent<TargetTracker>();
            targetObject.OnTrackingFound += trackingFound;
            //targetObject.OnTrackingLost+= trackingLost;
        }
 
 
        void onDestroy()
        {
            targetObject.OnTrackingFound -= trackingFound;
            //targetObject.OnTrackingLost-= trackingLost;
        }
 
 
        public void trackingFound()
        {
            if (visiToggle)
            {
                gameObject.SetActive(false);
                visiToggle = false;
            }
            else
            {
                gameObject.SetActive(true);
                visiToggle = true;
            }
        }
 
 
    }
 
}
cs


뷰포리아(Vuforia)

개발 가능 툴 : Android, IOS, Unity (유니티로 개발이 가능하기 때문에 PC에서도 사용가능)

특징

Extended Tracking : 카메라로 타겟 마커를 놓쳐도 Tracking을 유지시켜 줌.

Smart Terrain : Depth sensing을 통해 공간을 인식하여 증강현실 지형을 연출함.

Background Effects : 여러가지 비주얼 이펙트를 제공.

Video Playback : 비디오 플레이어를 제공.

Virtual Buttons : 사용자가 타겟의 일부를 가리면 인식하는 방식으로 증강현실과 인터랙션 할 수 있음.

Occlusion Management : 타겟의 일부만 보여도 마커로 인식하는 기능.

마커 저장방식 : Device, Cloud
Device : 기기에 오브젝트(이미지 or 모델링) 데이터를 담아두고, 데이터를 읽는 방식
장점 : 마커 저장 횟수에 제한이 없음.
단점 : 마커 수에 따른 앱 용량 증가. 마커를 수정, 추가 시 새로 빌드해서 프로젝트에 포함시켜야함
Cloud : 마커 이미지를 서버에 저장하여 서버에서 읽어오는 방식
장점 : 마커 수에 상관없는 앱 크기. 마커 수정, 추가 시 새로 빌드 할 필요가 없음.
단점 : 인식 횟수 제한이 있음.

) --> 

가격정책

무료 : 한 달 1000개의 클라우드 타겟 이용가능, 워터마크, 기능사용에 문제없음.

유료 : 클래식 - 앱당 499달러(클라우드 타겟 값 제외), 클라우드 매달 99달러(매달 10,000 번 인식, 이미지 100,000개 등록)


사용 방법 License 등록

뷰포리아 사이트에 접속한다 (https://developer.vuforia.com/)

Develop -> License Manager에서 Add License Key 버튼 클릭

Project Type, App Name과 결제 형식을 설정하고 Next 버튼 클릭

입력한 정보가 맞는지 확인 후 Confirm 버튼 클릭

생성된 라이센스에 들어가면 License Key가 발급되어 있음(유니티 프로젝트에서 사용해야함)





사용 방법 – 마커 등록

※ 마커 등록 시 주의사항 

(https://library.vuforia.com//articles/Solution/Natural-Features-and-Ratings)

곡선보다는 직선이 좀 더 인식이 잘 됨.

지나치게 단순한 형태보단 적당히 복잡한 형태가 인식이 잘 됨.

이미지의 Contrast(대비)가 커야 인식이 잘 됨.

규칙적인 무늬가 반복되는 이미지는 인식율이 떨어짐.

뷰포리아 사이트에 접속한다 (https://developer.vuforia.com/)

Develop -> Target Manager에서 Add Database 버튼 클릭

Name과 Database Type을 지정후 Create 버튼 클릭

생성된 Database를 클릭 후 Add Target 버튼 클릭

Target Type을 선택 후 그 타입에 맞는 파일과 이름크기 관련 정보를 입력 후 Add 버튼 클릭(이때 등록된 이미지의 Rating이 낮으면 인식률이 낮아짐)

마커를 등록한 후 Download Database(All)을 눌러 유니티 패키지를 생성(Device type)




 

사용 방법 유니티에 적용

https://developer.vuforia.com/downloads/sdk 에서 Unity 관련 패키지 다운로드 후 Target Database와 함께 패키지 임포트

Vuforia Prefabs 폴더에서 ARCamera와 저장한 마커에 따른 Target 프리팹을 Hierarchy에 드래그 앤 드롭

Target 프리팹에 Database를 선택 후 Target을 선택하여 이미지 불러오기

Target 프리팹 하위에 올리고싶은 오브젝트를 삽입

ARCamerainspector에서 Vuforia BehaviourOpen Vuforia configuration 버튼 클릭

App License Key에 발급받은 License Key 입력 후 Datasets Load DB이름 DatabaseActivate 체크박스 클릭

빌드





 



실행결과








) --> 


툴 : Unity 5.4.1f1

SDK : Leapmotion SDK

프로그램 설명 : 큐브에 손이 닿으면 큐브 색이 변하는 프로그램

립모션이 살짝 불안정해서 많이 흔들림

 


립모션을 선물받아서 이것저것 만들어볼 예정 + VR에도 적용해볼 예정


여러 씬을 만들어 놓고 각 씬으로 전환하려고 할 때 다음과 같은 메소드로 씬을 전환할 수 있다. 씬 전환 시 오브젝트들은 초기 상태(위치,스크립트 등)로 돌아가고 Static 변수는 남아있다. 삭제된 오브젝트는 씬 전환을 하여도 돌아오지 않는다. (다시 씬으로 돌아왔을 때 보이고 싶다면 SetActive 메소드를 이용하여 숨김 처리를 하는 것이 좋다)

 

기본적인 씬 로딩 함수 이동하려는 씬이 완전히 로딩된 후에 이동하는 함수. 이 메소드를 사용하면 씬 로딩이 끝날 때 까지 화면이 멈춘 상태에서 대기하게 된다.

SceneManager.LoadScene(씬번호);

사용시 using UnityEngine.SceneManagement를 상단에 입력해야함

 

씬이 전환되는 동안 (로딩화면을 띄우고 싶은 경우 or 안 멈추게 하고 싶을 경우) 로딩 전용 공간을 프리팹으로 만든 뒤 로딩할 때 사용하는 카메라를 바꾸어 로딩 화면을 보여준다.

 

AsyncOperation async = Application.LoadLevelAsync(씬번호);

카메라 전환 코드

while (!async.isDone) //완전히 로딩이 될 때까지 반복문

{

yield return null;

}

 

AsyncOperation 클래스에는 로딩 상태와 관련된 메소드, 변수들이 있다.

AsyncOperation 멤버변수

bool isDone 로딩이 끝났는지 않끝났는지 확인할수 있는 변수(로딩 다되면 true)

float progress 로딩의 진행도를 나타내는 변수(1이 로딩 끝)

 

예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Collections;
 
public class SceneChangeScript : MonoBehaviour {
    public GameObject camera1;    //기존 카메라
    public GameObject camera2;    //로딩 카메라
    public Slider slider;            //로딩 진행도를 알려줄 슬라이더 UI
    public int iTemp = 0;        //이동할 씬번호
    public GameObject fadeoutobj;
 
    void OnTriggerEnter(Collider other)
    {
    //Player 테그를 단 오브젝트와 충돌시 씬전환 코루틴 시작
        if (other.gameObject.tag == "Player")
        {
            StartCoroutine(SceneLoad());
        }
    }
    IEnumerator SceneLoad()
    {
        camera1.SetActive(false); //기존 카메라
        camera2.SetActive(true); //로딩화면에 있는 카메라
        yield return null;
    //씬 로딩 시작
        AsyncOperation async = Application.LoadLevelAsync(iTemp);
        while (!async.isDone)
        {
        //1프레임마다 검사
            yield return null;
        //로딩 진행도를 슬라이더 UI 값에 저장
            slider.value = async.progress;
            print(async.progress);
        }
    }
}
cs
 


스크립트를 하나 만들어서 기능을 여러 가지 넣으려고 할 때 다음과 같은 문제점이 발생한다.

만약 Update() 메소드 안에 제어문을 통한 메소드 호출 식으로 코딩을 할 경우, 이미 사용된 기능들도 제어문을 거치는 작업을 계속 수행하여 연산을 낭비하게 된다.

서버에서 다운이 끝난 후 작업을 하려고 할 때, X초 후 작업을 할 때, 물리연산이 모두 끝난후 작업을 하려고 할 때 Update()이나 다른 메소드를 통해서 작업이 끝났는지 확인하는 작업을 해서 연산량이 늘어난다.

 

코루틴 쓰레드와 비슷한 개념으로 하나의 스크립트에서 동시에 2개 이상의 연산을 하도록 하는 것이다. 쓰레드와 다르게 모든 코루틴은 프레임단위(또는 지정해놓은 대기시간)마다 똑같이 작동된다.

따라서 Update문에 모든 코드를 넣어서 사용하지 않는 코드도 계속 연산시키는 것이 아닌 특정 상황마다 코루틴을 실행시켜서 연산의 낭비를 최소화 할 수 있다.

 

코루틴을 시작하는 함수

StartCoroutine(“코루틴함수이름”)

StartCoroutine(코루틴함수())

 

 

기본 구조 쓰레드와 유사하게 호출시 한번만 작동하기 때문에 반복문으로 감싸줘야 유지할 수 있다. (반복문을 쓰지않아도 되는 상황이면 없애도 된다)

IEnumerator 함수이름()

{

while(true)

{

...

}

}

이 구조에서는 return이 없어도 되지만 yield 라는 것으로 코루틴이 해당 조건이 될 때까지 실행을 일시정지시키는 기능을 사용하여야 한다. 무한반복문 구조안에 yield문이 없거나 1프레임안에 해야할 작업이 많다면 무한루프에 빠지거나 게임이 통째로 멈춰버린다.



<!--[endif]-->

유니티에서 지원하는 yield


다른 곳에서 쉽게 코루틴을 끌 수 있도록 하려면 다음과 같은 코드를 참고하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Coroutine currentRoutine;
 
    void Start ()
    {
        currentRoutine = StartCoroutine (DoSomthingCo());
    }
 
    void StopCurrentRoutine ()
    {
        if (currentRoutine != null) {
            StopCoroutine(currentRoutine);
            currentRoutine = null;
        }
    }
 
    IEnumerator DoSomthingCo ()
    {
        while (true)
        {
            // job
        }
    }
cs


코루틴 예시

1
2
3
4
5
6
7
8
9
10
11
    //소리를 점점 줄이는 코루틴
    IEnumerator bgmFadeout()
    {
        for(float i = m_volume; i>=0;i-=0.01f)      //현재 볼륨에서 0까지 줄이는 반복문
        {
            Mainbgm.volume = i;
            yield return new WaitForSeconds(0.1f);  //0.1초 뒤에 코루틴작동(yield문이 없으
                                                       면 바로 볼륨이 0이됨)
        }
        Mainbgm.Stop();
    }
cs


준비물 : 웹캠

AR 관련 작업을 하기위해 카메라 사용법을 찾던 중에 내장클래스(WebCamTexture)로 웹캠을 사용할 수 있는 것을 찾음.
아래와 같은 스크립트를 생성한 뒤 큐브나 플레인에 스크립트를 삽입하면 끝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;
using System.Collections;
 
public class WebCam : MonoBehaviour {
 
    // Use this for initialization
    void Start () {
        WebCamTexture web = new WebCamTexture(1280,720,60);
        GetComponent<MeshRenderer>().material.mainTexture = web;
        web.Play();
    }
    
    // Update is called once per frame
    void Update () {
    
    }
}
 

cs


▲ WebCam.cs 

 


실행화면

 

▲잘나온다!


다음에는 이걸 휴대폰에 사용해서 되는지, 기어 VR에서 사용했을 때 퍼포먼스가 얼만큼 나오는지, 오큘러스 리프트에선 어떤지를 조사할 예정!

카드보드 SDK 다운로드


https://developers.google.com/vr/unity/get-started-android

 

안드로이드 SDK 설치

https://developer.android.com/studio/index.html


프로젝트 생성후 Import 방법
Assets -> Import Package -> Custom Pakcage 선택
다운받은 카드보드 SDK 내에 있는 GoogleVRForUnity.unitypackage 선택하여 import


프로젝트 빌드 방법
File -> Build Setting 선택
Android 플렛폼 선택후 좌측하단 'Switch Platform' 클릭

좌측 하단 Player Setting 클릭 -> Other Settings 탭에서 패키지 이름 설정 ex) com.example.VRUnityDemo
Minimum API Level은 최소 API19(킷켓) 으로 설정하면 됨(그 이상도 상관없음)
Resolution and Presentation 메뉴에서 Landscape Left 체크 후 Build하면 됨


유니티 공식사이트의 메뉴얼

http://docs.unity3d.com/kr/current/Manual/VRDevices-Oculus.html

 

오큘러스 리프트 프로그램 설치

https://www.oculus.com/en-us/setup/


오큘러스 개발 관련 SDK,패키지






'프로그래밍 > Unity' 카테고리의 다른 글

(유니티)PC - 웹캠 이용하는 심플 코드  (0) 2018.03.21
(Unity) 구글 카드보드 환경구축  (0) 2016.07.09
Mission Demolition Prototype  (0) 2016.07.09
Apple Picker Prototype  (0) 2016.07.09
유니티 VR 관련 책  (0) 2016.07.09

+ Recent posts