관리 메뉴

나만을 위한 블로그

[Android] AR core 공식 홈페이지 예제 빌드하는 법 본문

Android

[Android] AR core 공식 홈페이지 예제 빌드하는 법

참깨빵위에참깨빵 2020. 11. 15. 00:17
728x90
반응형

AR core가 뭔지, 공식 홈페이지의 예제는 어떻게 빌드하는지 간략하게 적었던 적이 있다.

onlyfor-me-blog.tistory.com/57

 

[Android] AR core란?

나만을 위한 블로그 [Android] AR core란? 본문 안드로이드 개발 [Android] AR core란? 끌어주면좀죽여라 2019. 12. 2. 14:34 Prev 1 ··· 177 178 179 180 181 182 183 184 185 ··· 204 Next

onlyfor-me-blog.tistory.com

이번 포스팅에선 공식 홈페이지의 예제를 스크린샷을 통해 어떻게 내 핸드폰에 빌드하는지 알아보려고 한다.

걸어둔 링크를 확인하면 알겠지만 확인 안 하는 사람도 있을테니 먼저 말하자면, AR core를 빌드하려면 아래 조건을 만족해야 한다.

※ 무슨 에러인지 모르겠지만, 안드로이드 3.6.0 버전에선 정상적으로 빌드가 성공하나 그 이상의 버전에선 빌드가 되지 않는다. 안드로이드 스튜디오 자체가 문제인지 sceneform 라이브러리의 문제인지는 정확히 모르겠지만 다 수정될 때까진 3.6.0 버전을 사용하자.

 

안드로이드 스튜디오 버전 3.1 이상 / 안드로이드 SDK 플랫폼 버전 7.0(API 레벨 24, 누가 버전) 이상

 

이것 중 하나라도 만족하지 못하면 AR core를 빌드할 수 없으니 이 조건들만큼은 필수로 맞춰줘야 한다.

 

 

먼저 프로젝트를 하나 생성하자. 프로젝트 생성 화면에서 API 레벨을 24로 설정한 다음 확인을 눌러주면 된다.

앱 수준 gradle 파일을 열었을 때 아래와 같은 문자열이 보여야 한다.

 

 

24 이상이기만 하면 되니 난 24로 설정해서 만들었다.

이 앱 수준 gradle 파일은 어차피 의존성을 넣어야 하기 때문에 닫지 말고 잠시 열어두자.

다음은 퍼미션이다. 매니페스트로 이동해서 아래의 권한들을 매니페스트에 추가한다.

 

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.ar" />
<meta-data android:name="com.google.ar.core" android:value="required" />

 

meta-data 태그는 application 태그 아무데나 넣으면 된다. 난 마지막 부분에 넣었다.

매니페스트 최종 형태는 아래와 같다.

 

여기서 meta-data 태그에 보면 android:value 속성의 값이 required로 되어있는 걸 볼 수 있다.

이 속성의 값은 required, optional 2가지가 있는데 둘의 차이는 아래와 같다.

 

  • required로 설정한 경우 AR 없이 이 앱을 쓸 수 없다는 의미다. 즉, AR core를 지원하는 기기에서만 사용할 수 있다는 의미다.
  • optional로 설정한 경우 기기가 AR core를 지원하는 경우에만 활성화되는 1개 이상의 AR 기능이 앱에 포함되어 있더라도, AR core를 지원하지 않는 기기에도 앱을 설치하고 실행할 수 있다는 의미다.

즉, 내가 만드는 앱이 AR core가 메인이라 관련 기능들로 범벅이 되 있는 경우 required로 설정하고 플레이 스토어에 올리면, 아무도 내 앱을 쓸 수 없다.

반면 AR core는 부가적인 기능이고, 메인 기능들이 따로 있다면 플레이 스토어에서 내 앱을 다운받아 사용할 수 있다는 의미다. 물론 핸드폰이 AR core를 지원하지 않는다면 AR 기능은 사용할 수 없다.

 

  • required로 설정한 경우, 플레이 스토어에서 이 앱을 다운받아 설치하는 핸드폰이 AR core를 지원하는 경우에만 앱을 설치할 수 있다. 그래서 설치하는 핸드폰에 플레이 스토어에서 AR용 Google Play Service를 자동으로 설치한다. 이 Google Play Service를 업데이트 해야 하거나 삭제한 경우, 앱에서 추가 런타임 확인을 수행해야 한다.
  • optional로 설정한 경우, AR용 Google Play Service가 설치된 핸드폰에서만 활성화되는 선택적 AR 기능이 있다. 플레이 스토어에서 앱을 설치하는 경우 핸드폰에 AR용 Google Play Service가 깔려있지 않더라도 자동으로 설치하지 않는다.

이것에 주의해서 자신의 상황을 파악하고 저 값을 required로 줄지, optional로 줄지 잘 선택해야 한다.

이 포스팅은 예제니까 required로 하고 넘어간다.

 

다음은 의존성이다. 이제 앞서 열어뒀던 앱 수준 gradle 파일에 아래 문장들을 추가한다.

 

implementation 'com.google.ar:core:1.21.0'
implementation "com.google.ar.sceneform.ux:sceneform-ux:1.15.0"

 

그리고 안드로이드에서 AR core를 다루기 위해선 Sceneform이라는 라이브러리가 필수적이다.

이 라이브러리는 자바8의 언어 구성을 사용하기 때문에, minSdkVersion이 26 미만인 경우 앱 수준 gradle에 아래 블럭을 추가해야 한다.

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

 

 

이렇게 설정하면 람다식을 사용할 수 있게 된다.

람다식을 주제로 하는 포스팅이 아니기 때문에 간단하게 설명하자면, 메서드를 하나의 식(Expression)으로 표현한 것이 람다식이다. 객체 지향 언어보다 함수 지향 언어에 가까운 것으로, 메서드를 람다식으로 표현하면 메서드명, 리턴값이 없어져서 익명 함수라고도 부른다.

 

위의 옵션을 적용해서 버튼 클릭 리스너를 적용할 경우, 원래라면 버튼 클릭 리스너는 아래와 같은 형태다.

 

button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                //
            }
        });

이것이 람다식을 적용하면 아래와 같은 형태로 변한다.

 

button.setOnClickListener(v -> {
            //
        });

매개변수인 View 객체와 실행코드인 중괄호 블럭만 남은 걸 볼 수 있다. 코드 수가 엄청나게 줄어든 걸 볼 수 있다.

자세한 내용은 람다식을 따로 검색해서 확인하자.

 

다음은 설정 창을 열어서 plugin을 눌러 플러그인을 설치한다. 검색창에 sceneform이라고 치면 나오는 이것을 설치한다.

설치가 끝났으면 안드로이드 스튜디오를 재시작해주자.

 

다음은 예제 코드를 내 앱으로 가져와야 한다. 아래 링크에서 다운받면 된다.

github.com/google-ar/sceneform-android-sdk/tree/v1.15.0

 

google-ar/sceneform-android-sdk

Sceneform SDK for Android. Contribute to google-ar/sceneform-android-sdk development by creating an account on GitHub.

github.com

파일을 압축해제하고 samples > hellosceneform > app > main > 경로에 있는 java, xml 파일들을 만든 프로젝트에 붙여넣기 해준다.

이름이 다르기 때문에 자신의 프로젝트에 맞게 바꿔줘야 한다.

 

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment android:name="com.google.ar.sceneform.ux.ArFragment"
        android:id="@+id/ux_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>
public class MainActivity extends AppCompatActivity
{
    private static final String TAG = MainActivity.class.getSimpleName();
    private static final double MIN_OPENGL_VERSION = 3.0;

    private ArFragment arFragment;
    private ModelRenderable andyRenderable;

    @Override
    @SuppressWarnings({"AndroidApiChecker", "FutureReturnValueIgnored"})
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        if (!checkIsSupportedDeviceOrFinish(this)) {
            return;
        }
        setContentView(R.layout.activity_main);

        arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);

        ModelRenderable.builder()
                .setSource(this, R.raw.andy)
                .build()
                .thenAccept(renderable -> andyRenderable = renderable)
                .exceptionally(
                        throwable -> {
                            Toast toast =
                                    Toast.makeText(this, "Unable to load andy renderable", Toast.LENGTH_LONG);
                            toast.setGravity(Gravity.CENTER, 0, 0);
                            toast.show();
                            return null;
                        });

        arFragment.setOnTapArPlaneListener(
                (HitResult hitResult, Plane plane, MotionEvent motionEvent) -> {
                    if (andyRenderable == null) {
                        return;
                    }

                    // Create the Anchor.
                    Anchor anchor = hitResult.createAnchor();
                    AnchorNode anchorNode = new AnchorNode(anchor);
                    anchorNode.setParent(arFragment.getArSceneView().getScene());

                    // Create the transformable andy and add it to the anchor.
                    TransformableNode andy = new TransformableNode(arFragment.getTransformationSystem());
                    andy.setParent(anchorNode);
                    andy.setRenderable(andyRenderable);
                    andy.select();
                });
    }

    public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            Log.e(TAG, "Sceneform requires Android N or later");
            Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show();
            activity.finish();
            return false;
        }
        String openGlVersionString =
                ((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE))
                        .getDeviceConfigurationInfo()
                        .getGlEsVersion();
        if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) {
            Log.e(TAG, "Sceneform requires OpenGL ES 3.0 later");
            Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG)
                    .show();
            activity.finish();
            return false;
        }
        return true;
    }

}

 

이런 형태로 만들었다면 클래스 파일의 setSource() 메서드 안 2번 인자에 빨간 줄이 생긴 걸 볼 수 있다.

당연히 아직 raw 폴더가 없고 그 안에 아무것도 없으니 빨간 줄이 생긴 것이다.

하지만 빨간 줄이 생긴 부분을 아래처럼 수정해줄 수도 있다.

 

ModelRenderable.builder()
                .setSource(this, Uri.parse("andy.sfb")) // <- Uri.parse()를 사용하는 코드로 변경
                .build()
                .thenAccept(renderable -> andyRenderable = renderable)
                .exceptionally(
                        throwable -> {
                            Toast toast =
                                    Toast.makeText(this, "Unable to load andy renderable", Toast.LENGTH_LONG);
                            toast.setGravity(Gravity.CENTER, 0, 0);
                            toast.show();
                            return null;
                        });

 

이렇게 설정한 후, 압축해제한 폴더를 보면 app 폴더 안에 sampledata 폴더가 있다. 들어가면 models 폴더가 있고 그 안에 4개의 파일들이 있다.

sampledata 폴더를 드래그해서 안드로이드 스튜디오 프로젝트의 app 밑으로 가져오자. 아래 화면처럼 보이도록 하면 된다.

 

 

이제 raw 폴더를 만들어줘야 한다. res 폴더를 우클릭한 후 아래 그림대로 순서대로 누르면 된다.

 

 

만약 저 탭을 누른 후 아래와 같은 화면이 나오더라도 FINISH를 누르면 된다.

나오지 않더라도 FINISH를 누르면 raw 폴더 생성이 완료된다.

 

 

이제 AR core로 띄울 3D 객체를 만들어보자.

안드로이드 스튜디오 왼쪽의 폴더 트리에서 app > sampledata > models 폴더 안의 andy.obj 파일을 우클릭하면 맨 위에 보라색 3D 사각형과 함께 이 탭이 보일 것이다.

 

 

이것을 눌러주자. 그러면 경로 설정하는 화면이 나오는데 따로 건드릴 건 없다. 우측 밑의 FINISH를 누르자.

그러면 로그창에서 뭔가가 돌아가다가 없던 assets 폴더가 생기고 그 안에 andy.sfb 파일이 생긴 걸 볼 수 있다.

이후 앱을 빌드하면 바닥에 하얀 점들이 보이고, 그 화면을 누르면 안드로이드 마스코트가 생겨날 것이다.

여기까지 했다면 첫 AR core 앱을 만든 데 성공한 것이다.

반응형
Comments