장면 빌드 및 상호작용

이 페이지에는 Scene를 빌드하고 상호작용하기 위한 일반적인 팁이 포함되어 있습니다.

AR 없이 장면 렌더링

SceneView 클래스를 사용하면 기기의 카메라나 AR 세션을 사용하지 않고도 3D 장면을 렌더링할 수 있습니다. 이 기능은 AR 없이 앱의 3D 객체를 미리 보거나 AR을 지원하지 않는 기기에서 대체 기능을 제공할 때 유용합니다.

기본적으로 SceneView는 AR 카메라의 이미지를 표시하지 않으며 검은색 배경을 사용합니다. 배경 색상을 변경하려면 아래와 같이 view.setBackgroundColor()을 호출하거나 레이아웃에서 배경 색상을 정의하면 됩니다.

<com.google.ar.sceneform.SceneView
    android:id="@+id/scene_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/deep_teal"/>

장면의 Camera 노드가 원점 (위치 0,0,0)에 있고 앞쪽 (방향 0,0,-1)에 배치됩니다. 카메라의 위치와 회전은 AR 모션 추적에 연결되어 있지 않으므로 다른 노드처럼 위치를 변경하거나 애니메이션을 적용할 수 있습니다.

Camera camera = sceneView.getScene().getCamera();
camera.setLocalRotation(Quaternion.axisAngle(Vector3.right(), -30.0f));

상호작용 수

사용자 터치 처리

사용자가 화면을 터치하면 장면 형태가 노드와 장면에 연결된 이벤트 핸들러와 리스너에 터치 이벤트를 전파합니다. 이 동작은 터치 이벤트가 Android의 뷰 및 뷰 그룹에 전파되는 방식과 비슷합니다. 적용 순서는 다음과 같습니다.

  1. 이벤트는 scene.addOnPeekTouchListener()에 추가된 모든 리스너로 전송됩니다.

    미리보기 터치 리스너의 장면이 이벤트를 사용할 수 없다는 점을 제외하면 viewGroup.intercept()와 유사합니다.

  2. 이 이벤트는 레이가 교차하는 첫 번째 노드에 전달됩니다.

    • 노드는 true를 반환하는 onTouchEvent() 메서드 세트를 정의하여 이벤트를 사용할 수 있습니다.
    • onTouchEvent() 메서드가 false를 반환하거나 리스너가 정의되지 않으면 이벤트가 노드의 상위 요소에 전파됩니다. 이 프로세스는 이벤트가 소비되거나 장면에 도달할 때까지 계속됩니다.
  3. 마지막으로 리스너가 이벤트를 사용하지 않으면 이벤트가 scene.onTouchListener()로 전달됩니다.

동작 감지

ArFragment에서는 탭 (선택), 드래그 (이동), 손가락 모으기 (크기 조정), 트위스트 (회전) 동작을 기본적으로 지원합니다.

예를 들어 Hello sceneform 샘플 앱에서 HelloSceneformActivity.java을 참고하세요.

커스텀 노드 만들기

맞춤 Android 뷰 만들기와 마찬가지로 Node의 서브클래스를 만들어 맞춤 노드를 만들 수 있습니다. 다음과 같은 경우에 커스텀 노드를 만들어야 할 수 있습니다.

  • 노드 수명 주기의 이벤트(예: onUpdate(), onActivate, onDeactivate())에 액세스하려고 합니다.
  • 노드 그룹으로 구성된 노드를 만들려고 합니다.
  • 코드가 많이 복제되며 서브클래스에 분해할 수 있습니다.

예는 태양계 샘플 앱에서 Planet.java을 참고하세요.

노드 애니메이션 처리

노드에 애니메이션을 적용하는 방법에는 두 가지가 있습니다.

ObjectAnimator로 애니메이션 처리

스포트라이트의 강도를 애니메이션으로 보여주는 예는 다음과 같습니다.

final int durationInMilliseconds = 1000;
final float minimumIntensity = 1000.0f;
final float maximumIntensity = 3000.0f;
ValueAnimator intensityAnimator =
    ObjectAnimator.ofFloat(
        spotlightNode.getLight(), "intensity", minimumIntensity, maximumIntensity);
intensityAnimator.setDuration(durationInMilliseconds);
intensityAnimator.setRepeatCount(ValueAnimator.INFINITE);
intensityAnimator.setRepeatMode(ValueAnimator.REVERSE);
intensityAnimator.start();

자세한 내용은 ObjectAnimator로 애니메이션 적용을 참고하세요.

onUpdate에서 애니메이션 처리

노드의 onUpdate()를 재정의하여 프레임에 애니메이션을 적용합니다. 다음 예에서는 태양계 샘플 앱의 Planet.java에서 행성이 회전하더라도 사용자를 향하도록 프레임마다 정보 카드를 조정합니다.

@Override
public void onUpdate(FrameTime frameTime) {
  Vector3 cameraPosition = getScene().getCamera().getWorldPosition();
  Vector3 cardPosition = infoCard.getWorldPosition();
  Vector3 direction = Vector3.subtract(cameraPosition, cardPosition);
  Quaternion lookRotation = Quaternion.lookRotation(direction, Vector3.up());
  infoCard.setWorldRotation(lookRotation);
}

조명 추가

Lights는 장면의 모든 노드에 연결할 수 있습니다. 기본적으로 모든 장면 양식에는 방향 빛이 연결된 Sun 노드가 포함되어 있습니다.

태양을 수정하거나 장면에 나만의 조명을 추가할 수 있습니다. 다음 예시에서는 스포트라이트를 추가합니다.

Light spotLightYellow =
    Light.builder(this, Light.Type.FOCUSED_SPOTLIGHT)
        .setColor(new Color(android.graphics.Color.YELLOW))
        .setShadowCastingEnabled(true)
        .build();

그런 다음 setLight()를 호출하여 노드에 연결합니다.

평면 시각화 맞춤설정

기본적으로 장면에는 ARCore에서 감지되면 Planes를 강조표시하는 PlaneRenderer이 있습니다. 다음 그림과 같습니다.

감지된 평면을 렌더링하는 데 사용되는 기본 머티리얼과 텍스처를 수정할 수 있습니다. 텍스처를 변경하는 방법은 다음과 같습니다.

Texture.Sampler sampler =
        Texture.Sampler.builder()
                .setMinFilter(Texture.Sampler.MinFilter.LINEAR)
                .setWrapMode(Texture.Sampler.WrapMode.REPEAT)
                .build();

// R.drawable.custom_texture is a .png file in src/main/res/drawable
Texture.builder()
        .setSource(this, R.drawable.custom_texture)
        .setSampler(sampler)
        .build()
        .thenAccept(texture -> {
          arSceneView.getPlaneRenderer()
                  .getMaterial().thenAccept(material ->
                  material.setTexture(PlaneRenderer.MATERIAL_TEXTURE, texture));
        });

그림자

그림자는 렌더링 가능한 세계를 기반으로 하고 사용자에게 깊이와 공간에 대한 느낌을 줍니다.

장면 양식에는 그림자를 전송할 수 있는 객체와 그림자를 받을 수 있는 객체가 있습니다.

  • LightsRenderables가 그림자를 드리울 수 있음

    기본적으로 그림자 전송은 햇빛에 사용되지만 조명에는 사용되지 않습니다. setShadowCastingEnabled()를 호출하여 사용 설정합니다.

  • RenderablesPlaneRenderer는 그림자를 수신할 수 있습니다.

    기본적으로 그림자 수신 기능이 사용 설정되어 있습니다. setShadowReceiver()를 호출하여 사용 중지합니다.

렌더링 가능 섀도우와 드리우는 그림자가 있다면 그림자를 드리울 수 있습니다.