SceneView 2.0.2 업데이트 공부하기

·2024년 1월 20일
0

오랜만에 돌아왔습니다... 사실 AR은 AR 프로젝트가 끝난 뒤로 공부하지 않았는데 이것과 관련해서 질문이 몇 개 들어왔고 Sceneview 라이브러리가 업데이트를 거치면서 완전히 사용법이 달라졌기에 다른 이들에게 알려주기 전에 정리하고자 이 글을 작성합니다. SceneView 0.10.0 버전에서 SceneView 2.0.2의 차이점은 굉장히 많지만, 그래도 정리해보고자 합니다. 오늘 살펴본 코드는 이곳에 SceneView/sceneviewTest 있습니다. ARCore의 API 사용 설정을 Google Could에서 진행하시길 바랍니다.(파일의 API키는 제 google colud에서 삭제되었습니다.)

SceneView의 업데이트

공식 GitHub

잠시 안 본 사이에 정말 많은 업데이트가 이루어졌습니다. engine요소가 추가된 것을 제외하고도 많은 Node 클래스가 사라지고 대부분 AnchorNode, ARCameraNode, HitResultNode등 기존의 Node 생성과 anchor 생성을 한꺼번에 하는 느낌이 되어버렸다. 물론, 아직 자세하게 읽어보지 않아 두 개를 한꺼번에 한다는 건 틀릴 수도 있다.

대표적인 업데이트는 아래와 같고 생각한다.

1. Compose추가

Compose가 추가됨에 따라 공식 GitHub의 예제의 방식이 layout 방식과 compose 방식으로 나뉘어져 있다.

하지만, 현재 안드로이드 내에서는 Compose를 통한 안드로이드 코딩 과정의 대부분을 코틀린으로 작성하는 정형화? 통일화? 라고 하는 것을 하고 싶어하기 때문에 공식 깃허브의 README.md는 Compose를 기준으로 작성되었다.

2. ARCore의 Session

ARCore의 Session 기능에 접근이 가능하게 되었습니다.
ARCore Session 구성 문서
ARCore Session 문서
ARCore의 Session이 무엇인가는 위의 공식 문서를 통해서 확인할 수 있습니다.
쉽게 정리하자면, ARCore의 Session은 ARCore의 모든 활동을 관리하고 ARCore 작동에 필요한 생명주기를 관리하는 객체라고 합니다.

다음으로 layout 방식의 ArSceneView(2.0.2 버전) 공식 예시 중 ArSceneView를 설정하는 코드입니다.

sceneView = findViewById<ARSceneView?>(R.id.sceneView).apply {
            planeRenderer.isEnabled = true
            //해당 ARSceneView의 Sesstion을 설정합니다. 해당 코드에서는 ARCore의 Depth API를 사용할 수 있는지 확인한 다음에 해당 API를 활성화하거나 비활성화하는 코드입니다. Depth API는 특정 센서가 존재하지 않으면 사용할 수 없습니다.
            configureSession { session, config ->
            //해당 기기에서 Depth API를 사용할 수 있는 확인하는 부분
                config.depthMode = when (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {
                    true -> Config.DepthMode.AUTOMATIC
                    else -> Config.DepthMode.DISABLED
                }
                config.instantPlacementMode = Config.InstantPlacementMode.DISABLED
                config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR
            }
            // 해당 ARSceneView의 1 프레임당의 시스템 업데이트 방식을 정하는 코드입니다.
            onSessionUpdated = { _, frame ->
                if (anchorNode == null) {
                    frame.getUpdatedPlanes()
                        .firstOrNull { it.type == Plane.Type.HORIZONTAL_UPWARD_FACING }
                        ?.let { plane ->
                            addAnchorNode(plane.createAnchor(plane.centerPose))
                        }
                }
            }

각각 Session이 들어간 두 개의 configureSession 메소드와 onSessionUpdated 변수를 코드에서 발견할 수 있습니다.

각각의 공식 설명은 AndroidStudio의 디컴파일(Ctrl + 클릭)을 통해 볼 수 있습니다.
먼저 configureSession 메소드에 대한 공식의 설명과 코드는 이러합니다.

    /**
     * Define the session config used by ARCore.
     *
     * Prefer calling this method before the global (Activity or Fragment) onResume() cause the
     * session base configuration in made there.
     * Any later calls (after onSessionResumed()) to this function are not completely sure be taken
     * in account by ARCore (even if most of them will work)
     *
     * Please check that all your Session Config parameters are taken in account by ARCore at
     * runtime.
     *
     * @param applyConfig the apply block for the new config
     */
    fun configureSession(applyConfig: (Session, Config) -> Unit) {
        _onSessionCreated += object : (Session) -> Unit {
            override fun invoke(session: Session) {
                _onSessionCreated -= this
                session.configure { config ->
                    applyConfig.invoke(session, config)
                }
            }
        }
    }

위의 내용은 ARCore의 Session 관리에 대해서 배경 지식이 없으면 이해하기 어려울 수도 있습니다. 먼저, ARCore의 모든 활동을 접근할 수 있는 Session의 경우 onResume() 때 설정이 완료된다는 설명을 발견할 수 있습니다. 따라서, 해당 메소드를 onResume() 전에 사용할 것을 권장하고 있습니다.
또한, 해당 메소드의 config 매개 변수에는 ARCore에서 지원하는 기능에 대한 설정을 할 수 있도록 매개변수가 존재합니다.
만약, 프로젝트에서 ARCore에서 AR만 띄우는 게 아니라 CloudAnchor나, streetscapeGeometry, geospatial 같은 기능을 사용하고 싶다면 아래와 같이 설정해야 한다.

configureSession { session, config ->
				//추가된 부분
                config.streetscapeGeometryMode  = Config.StreetscapeGeometryMode.ENABLED
                config.geospatialMode = Config.GeospatialMode.ENABLED
                //추가된 부분
                
                config.depthMode = when (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {
                    true -> Config.DepthMode.AUTOMATIC
                    else -> Config.DepthMode.DISABLED
                }
                config.instantPlacementMode = Config.InstantPlacementMode.DISABLED
                config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR
            }

아직 직접적으로 해당 기능을 활용한 것은 아니고 해당 기능을 사용할 것이며 해당 내용을 매 프레임마다 반영할 필요가 있다고 ARCore에게 알리기만 하였다.

이제는 sessionUpdated 필드에 대한 공식 설명이다.

Updates of the state of the ARCore system.
This includes: receiving a new camera frame, updating the location of the device, updating the location of tracking anchors, updating detected planes, etc.
This call may update the pose of all created anchors and detected planes. The set of updated objects is accessible through Frame.getUpdatedTrackables.
Invoked once per Frame immediately before the Scene is updated.

왜 코드가 없는가는 직접 확인해보시면 압니다. 코드가 엄청 깁니다. 매 프레임마다 해당 무엇을 업데이트해야 하는가를 명시하는 코드라고 합니다.

Node의 생성 방식

이전의 방식은 Node를 생성할 때 해당 Node를 보여줄 sceneview 나 arsceneview의 엔진만 설정해주는 등의 방식으로 연결되고 공식 예제에서 보여주는 방식도 매우 간단한 코드처럼 보였다.
하지만, 지금의 node를 생성하는 공식 예제 코드는 이러하다.

    fun addAnchorNode(anchor: Anchor) {
        sceneView.addChildNode(
            AnchorNode(sceneView.engine, anchor)
                .apply {
                    isEditable = true
                    lifecycleScope.launch {
                        isLoading = true
                        sceneView.modelLoader.loadModelInstance(
                            "https://sceneview.github.io/assets/models/DamagedHelmet.glb"
                        )?.let { modelInstance ->
                            addChildNode(
                                ModelNode(
                                    modelInstance = modelInstance,
                                    // Scale to fit in a 0.5 meters cube
                                    scaleToUnits = 0.5f,
                                    // Bottom origin instead of center so the model base is on floor
                                    centerOrigin = Position(y = -0.5f)
                                ).apply {
                                    isEditable = true
                                }
                            )
                        }
                        isLoading = false
                    }
                    anchorNode = this
                }
        )
    }

이전 포스트에서 많이 본 형태들보다 복잡하다. 그러나, 생각보다 쉽다. 잘 살펴보면 apply, let, 코루틴 때문에 복잡해졌을 뿐 사실 이전의 그 코드들과 이름만 다르다는 걸 알 수 있다.
이 코드는 sceneView라는 ARSceneView에 자식 노드를 추가하는 코드이다.
여기서 AnchorNode는 현실 공간에서의 위치가 고정된 Node이다. anchor이라는 단어가 들어간 것들은 대부분 위치가 고정되었다는 것을 내포한다.

차례대로 apply의 내용은 이러하다.

  • isEditable : 해당 Node는 나중에 수정될 수 있는가에 대한 변수
  • isLoading : 이 예제에서 만든 변수로 glb 파일을 로딩 중이라면 로딩 화면을 띄우는 상태를 결정하는 변수
  • sceneView.modelLoader.loadModelInstance : glb, gltf 파일을 가져와서 모델 인스턴스 생성, url, uri, file 위치등등 여러가지 형태로 glb, gltf 파일을 가져올 수 있으며 잠시 실행을 중지시킬 수 있는 suspend 함수이다.
  • anchorNode : 이 예제에서 만든 변수로 ARCore가 평면이나 빈 공간 인식에 실패했을 때 천천히 빈공간에 옮기라고 하는 등의 안내를 하기 위해 만든 변수

다음으로 let의 내용은 이러하다.
일단, 이 내용은 loadModelInstance로 만들어진 모델 인스턴스가 null이 아니면 실행된다.
먼저 AnchorNode의 자식 node를 생성하는데 여기서는 ModelNode를 생성하여 모델 인스턴스를 볼 수있도록 한다.

  • modelInstance: loadModelInstance에서 생성된 모델 인스턴스를 설정하는 변수. 3D로 렌더링된 모델을 주로 사용한다고 설명되어 있다.
  • scaleToUnits : 모델의 사이즈를 설정하는 변수. Float 타입이다.
  • centerOrigin : 옛날의 screenPosition과 동일하게 모델의 핸드폰 카메라에 대한 상대적인 위치를 결정하는 변수이다. 다만, 모델의 중심을 기준으로 설정되는 것같다.

한 마디로 sceneView 아래에 AnchorNode를 생성하고, AnchorNode 아래에 ModelNode를 생성한다. 옛날에 node를 만들고 해당 node 안에서 anchor를 설정 했다면 이번에는 AnchorNode를 만들어 그곳에 ModelNode를 매달리게 만들었다는 느낌이 든다. (아직 다 안 읽어봐서 아닐 수도 있습니다.)

여담 SceneView 1.1.0

왜 이런 대격변을 겪었는가에 대한 이제부터는 추측글이다. 릴리즈 노트를 읽던 중 이런 글을 보고 말았다.

아무래도 구현 과정에서 SceneView -> ARCore -> Filament 라는 흐름에서 ARCore를 빼고 SceneView -> Filament 방식으로 대체된 부분이 많은 거 같다.
실제로 많은 부분을 그러한 방식으로 바꾼 것인지 아예 SceneView 이전 버전과 현재 버전을 구분한 폴더로 GitHub에 올라와있었다.
이미 1.1.0 버전부터 많은 Node가 삭제된 흔적을 발견했지만, 문제는 내부의 코드가 거의 다르다는 거다. 이러니, 옛날 예제들이 전부 안 됐던 거 같다.
자세한 건 커뮤니티를 좀 더 들여다 봐야 알 수 있겠지만, 2.0.0 버전이 시작되고서 꽤 많은 issue와 dicuss가 있는 것처럼 보인다.

profile
안드로이드 네이티브 앱 개발자를 지망하는 대학생입니다.

0개의 댓글