
영상 링크: https://developer.apple.com/videos/play/wwdc2025/287/
2019년에 RealityKit을 출시하며 앱에 3D 콘텐츠를 통합하고 사실적인 렌더링을 제공하여 몰입형 경험을 향상시켰다. RealityKit은 3D 콘텐츠가 실제 환경과 완벽하게 조화를 이룰 수 있는 다양한 기능을 제공하여 visionOS에서 몰입형 앱과 게임을 만들 수 있다.

얘네들에 다 기능을 제공한다. RealityKit의 cross-platform 기능을 통해 최소한의 코드 변경으로 다양한 플랫폼 사용이 가능하다. 올해부턴 tvOS도 지원한다.

이 새로운 것들 중 일부를 이 세션을 통해 알아볼 예정. 이 기능 중 일부를 이용하여 공간 퍼즐 게임을 만들어보겠다.

게임은 당신 앞의 표면에 놓인 잠긴 상자로 부터 시작한다.

상자 주변에는 상호작용 할 수 있는 여러 물체가 있고, 여기서 상자를 여는 열쇠를 물체의 바닥에서 찾아야 한다.

RealityKit AnchorEntity를 사용하여 테이블 위에 게임을 배치할 것이다. Anchor entity는 가상 콘텐츠를 실제 표면에 연결하는데(attach) 사용된다. 올해는 ARKit 앵커링 데이터를 직접 공개하여 Anchor Entity를 더욱 강력하게 만들고 있다.

RealityKit을 통해 ARKit 데이터에 직접 액세스하려면 SpatialTrackingSession을 만들어야한다.
Session의 Configuration에서는 Anchor Entities에 대한 상태가 변경되면 RealityKit이 앱에 새로운 AnchorStateEvents를 보내도록 지시한다.
그런 다음 Anchor Entity를 설정하여 찾고 있는 앵커의 속성을 필터링 할 수 있다. 내가 찾고자 하는 최적의 앵커를 찾으면 AnchorStateEvent를 실행한다. AnchorStateEvent 인스턴스에는 앵커의 변형 및 범위와 같은 ARKit 데이터가 포함되어 있어 이를 사용하여 게임의 위치를 지정할 수 있다.

코드로 살펴보기!
세션을 설정한다.

이러한 AnchorEntity를 생성한다. AnchorEntity는 고정되지 않은 상태로 시작하지만 일치하는 테이블 평면을 감지하면 고정된다.

앵커 상태가 변경될 때 업데이트를 받으려면 AnchorStateEvent API를 사용해야한다. 이를 통해 엔터티가 앵커링 된 경우, 앵커링이 해제되려는 경우, 앵커링이 실패한 경우에 대한 이벤트를 구독할 수 있다.

앱에서 DidAnchor 이벤트를 사용하여 게임 엔터티를 테이블 표면의 경계 내에 배치한다.
DidAnchor 이벤트에 subscription을 추가하여 anchor가 environment에 성공적으로 앵커링 되었는지 알 수 있게 한다.
이벤트 구조는 새로운 ARKitAnchorComponent를 포함하도록 업데이트 된 앵커 엔티티를 제공한다.
이 component는 game entity를 고정된 표면에 배치하는 데 사용할 수 있는 범위 및 변환과 같은 ARKit 데이터를 보관한다. ARKitAnchorComponent의 앵커 속성을 활용하여 이 데이터에 접근할 수 있다.

anchor property를 사용하려면 ARKit anchor type으로 변환해야 한다. 이 경우 AnchorEntity가 원하는 대상인 비행기를 찾도록 되어 있으니 PlaneAnchor로 캐스팅하겠다.

그렇게 하면 이제 raw ARKit extents와 transform에 접근할 수 있다.

ARKit transform, 특히 originFromAnchorTransform과 anchorFromExtentTransfrom을 사용하여 게임의 위치를 정하고 고정된 표면의 중앙에 게임이 위치하도록 할 것이다.
이제 이렇게 게임이 생성되는 것을 볼 수 있다.

ManipulationComponent을 통해 게임에 상호작용을 추가해보겠다.
ManipulationComponent는 3D entity를 선택하고 회전하는 과정을 단순화한다. 손을 바꾸는 것과 같은 고급 제스처도 지원한다.

ManipulationComponent에 대해 더 알고 싶다면 "Better together: SwiftUI and RealityKit" 세션을 시청해라.
Entity를 선택하고 상호작용하려면 ManipulationComponent configureEntity 함수만 호출하면 된다.
이 기능은 엔터티에 필요한 InputTarget, Collision, HoverEffect, Manipulation 구성 요소를 자동으로 추가한다. (와 이거 정말 좋아졌다...)

객체를 놓으면 원래 위치로 자연스럽게 돌아가는 것을 볼 수 있다.

이는 releaseBehavior의 값을 조정하면 된다. stay를 사용하면 release 해도 그대로 유지가 된다.

다음으로는 바닥에 떨어뜨리려고 한다. 이를 위해 PhysicsBodyComponent를 엔터티에 추가한다. 객체가 pinch나 pick up 되지 않을때만 중력이 적용되게 만들고 싶다. 이는 새로운 ManipulationEvents API를 사용하면 쉽게 수행할 수 있다.
ManipulationEvents는 RealityKit에서 발생하는 이벤트로 엔티티가 상호 작용할 때 겪는 다양한 상호작용 상태를 설명한다.

WillRelease event는 플레이어가 엔티티를 release 할 때 트리거 된다. WillBegin, WillEnd, DidUpdateTransform, DidHandOff도 있다.

WillBegin, WillEnd 이벤트를 사용하여 게임 엔터티가 상호 작용하지 않을 때만 중력에 반응하도록 만들겠다.

일단 WillBegin에 subscription을 추가하여 PhysicsBodyComponent 모드를 kinetic mode로 변경하여 객체가 이동하는 동안 물리 시스템이 방해받지 않도록 한다. 이렇게 하면 중력이 해당 entity에 영향을 미치는 것도 방지할 수 있다.

그리고 엔터티와 상호작용하지 않을 때 PhysicsBodyComponen 모드를 다시 동적으로 변경하기 위해 WillEnd에 subscription을 추가한다. 이렇게 하면 entity가 중력을 포함한 물리적 객체에 반응 할 수 있다.
객체가 주변 환경과 충돌하도록 만들어보자. 새로운 Scene Understanding API를 사용하면 앱의 물리 시뮬레이션에 내 방의 mesh를 추가할 수 있다. RealityKit은 SpatialTrackingSession API를 통해 주변 환경의 메시를 생성할 수 있다. 이 메시는 Scene Understanding 메시라고 불리며 방 안의 실제 물체에 충돌과 물리 효과를 추가하는데 사용될 수 있다.
SpatialTrackingSession에서 SceneUnderstandingFlags를 설정하여 실제 세계 주변의 장면 이해 메시를 활용할 수 있다.
visionOS는 현재 collision과 physics flag를 지원한다. 이 두가지를 이용하여 게임 객체가 scene understanding 메시와 충돌하도록 할 것이다. 실행하기 전에 SpatialTrackingSession configuration에서 이 플래그를 설정한다.

일단 이전에 설정한 SpatialTrackingSession을 업데이트 해야 한다. 세션을 시작하기 전에 SpatialTrackingSession configuration에서 collision과 physics 플래그를 추가하면 된다.

이렇게 바닥과 충돌 할 때 환경과 충돌하는 것을 볼 수 있다. (11:17)

주변 환경과 상호작용하게 만들었으니 시각적으로도 주변에 반응하게 만들어보자. EnviornmentBlendingComponent를 여기에 사용할 수 있다.
EnviornmentBlendingComponent는 몰입형 공간 용으로 설계된 새로운 component이다. 이 컴포넌트로 entity를 정적인 현실 객체로 숨길 수 있다.
이 컴포넌트가 있는 엔티티는 정적 현실 객체에 가려진 정도에 따라 부분 또는 전체가 현실감 있게 가려진다. 사람, 동물 처럼 동적으로 움직이는 객체는 이 컴포넌트가 있는 객체를 가리지 않는다.

이 기능을 사용하려면 EnviornmentBlendingComponent를 추가하고 선호하는 융합모드를 설정하면 된다.

이렇게 실제로 가려지는 것을 확인 할 수 있다.

EnviornmentBlendingComponent를 사용하는 엔터티는 배경 환경의 일부로 처리되며 항상 scene의 다른 가상 객체 뒤에 그려진다.
MeshInstancesComponent를 통해 주변 게임 영역에 장식을 추가할 수 있다. 작년에 LowLevelMesh 및 LowLevelTexture API가 RealityKit에 추가되어 랜더링 데이터를 훨씬 더 효과적으로 제어할 수 있게 되었다. 올해의 RealityKit 업데이트에서는 이러한 저수준 액세스가 렌더링의 또 다른 측면인 Instancing으로 확장되었다.
surrounding space를 장식하고 playable area도 정의하고 싶다.

이를 위해 여러 개의 복제된 엔티티를 생성할 수 있다. 그러나 이렇게 하려면 엔터티를 여러 번 복제해야 하므로 ModelComponent의 복사본이 많이 생성된다. 이로 인해 메모리와 처리 공간이 크게 필요할 수 있다. 이를 위해 더 효율적이고 편리한 방법은 new MeshInstancesComponent를 사용하는 것이다.

MeshInstancesComponent를 사용하면 단일 엔티티로 여러번 메시를 그릴 수 있다. mesh를 그리는 데 필요한 변환 목록만 제공하면 된다. iOS, iPadOS, macOS 및 tvOS에서는 LowLevelBuffer를 사용하여 렌더 데이터를 CustomMaterial에 전달하여 각 mesh instance가 unique하게 보이도록 할 수 있다. MeshInstancesComponent는 편리함 외에도 GPU로 전송해야 하는 데이터의 양을 줄여 성능을 향상시킬 수도 있다. 복제된 Mesh를 그릴 때 model과 material의 여러 사본을 GPU로 보내는 대신 MeshInstancesComponent는 해당 데이터를 한 번만 보낸다. single MeshInstancesComponent로 그려진 model은 여전히 단일 entity의 일부로 간주된다는 것에 유의하는 것이 중요하다.

이 component를 사용하여 넓은 영역을 다루는 경우 culling이 일어나도록 여러 개의 작은 entity로 나누는 것이 나을 수 있다.
코드에서 사용하려면 instance화 할 mesh가 일단 필요하다. 앱의 콘텐츠 번들에서 entity를 로드하여 이를 얻을 것이다.

그리고 MeshInstancesComponent와 LowLevelInstanceData object를 초기화 할 수 있다.
LowLevelInstanceData 객체는 각 개별 mesh instance에 대한 데이터를 보관한다.
LowLevelInstanceData 객체를 생성할 때 앱에 필요한 인스턴스 수를 제공해야 한다. 너무 혼잡하지 않은 play 영역 근사치를 나타내고자 20을 사용했다.

그리고 인스턴스화하려는 mesh 부분의 index로 구독된 MeshInstancesComponent에 LowLevelInstanceData 객체를 할당 할 수 있다.
이번 경우에는 인스턴싱하려는 mesh가 간단하고 mesh 부분이 하나뿐이므로 LowLevelDataObject를 partIndex: 0에 할당한다.

이제 각 mesh 인스턴스에 대한 transform으로 LowLevelInstanceData 객체를 채울 수 있다.
다양한 decoration을 위해 각 인스턴스의 크기, 각도, 위치를 무작위로 지정한다.

이러한 값을 사용하여 변환 행렬을 만들고 인스턴스에 할당 할 수 있다.

이제 엔티티에 meshInstancesComponent를 추가할 수 있고 엔티티가 그려질 때마다 MeshInstancesComponent의 데이터를 사용하려 그려진다.

새로운 AnchorStateEvent API를 사용하여 콘텐츠를 고정했다.
그리고 ManipulationComponent를 사용하여 객체와 상호 작용을 허용하고 Scene understanding flags를 사용하여 게임 엔티티가 scene understanding mesh와 충돌할 수 있도록 만들었다.
마지막으로 EnvironmentBlendingComponent와 MeshInstancesComponent를 사용하여 게임이 현실 세계와 어울리도록 했다.

RealityKit에서 이미지를 표현하는 데 사용되는 ImagePresentationComponent라는 새로운 component가 추가되었다.
이 기능은 세 가지 종류의 이미지를 지원한다.
기존의 2D 이미지와 사진, 아이폰이나 비전 프로의 공간 사진, 기존 2D 이미지나 사진에서 생성된 새로운 종류의 3D 이미지인 spatial scene이다.
Spatial Scene은 2D 이미지에서 생성된 실제 깊이를 가진 3D 이미지이다. (17:13)
이는 vieweer가 scene을 기준으로 머리를 움직일 때 공간적 장면의 깊이를 강조하기 위해 동작 시차를 적용한 사진의 diorama 버전과 같다.
Spatial Scene은 visionOS의 사진 앱과 RealityKit을 사용한 자체 앱에서 기존 2D 사진에 생동감을 불어넣는 좋은 방법이다.

앱에 세 가지 종류의 이미지를 추가하는 코드를 살펴보겠다.
2D 사진의 URL을 찾고, 해당 URL을 사용하여 새로운 이미지 표현 구성 요소를 만든다.
이미지를 메모리에 로드하는 데 시간이 좀 걸릴 수 있으므로 component의 초기화 프로그램은 비동기적이다.

Component가 초기화 되면 이를 엔티티에 할당하여 RealityKit scene에 표시할 수 있다.

spatial photo를 표현하려면 한 가지 단계가 더 필요한데, 엔터티에 구성 요소를 설정하기 전에 해당 component에 대해 원하는 viewing mode를 지정해야 한다.
먼저 이미지가 해당 모드를 지원하는지 확인한 후 원하는 viewing mode를 지정할 수 있습니다. 원하는 viewing mode를 지정하지 않거나 이미지가 해당 모드를 지원하지 않는 경우 ImagePresentationComponent는 공간 사진인 경우에도 이미지를 2D 또는 monoscopic 보기 모드로 표현한다.

spatial photo presentation을 선택하려면 spatialStereoImmersvie를 사용해라

spatial photo에서 image presentation component를 만들 때마다 두 가지 spatial stereo mode를 모두 사용할 수 있다.
2D 이미지와 spatial photo는 모두 디스크의 파일에서 로드된다. 이 이미지를 spatial scene으로 표시하려면 몇 가지 추가 단계가 필요하다. 왜냐하면 이미지를 표현하기 전에 spatial scene을 생성해야 하기 때문이다.
코드로 spatial scene을 생성하고 표현하는 방법을 보여주겠다.
2D 이미지나 spatial photo로 spatial scene을 생성할 수 있다. spatial photo에서 spatial scene을 생성하는 경우 spatial photo 채널 중 하나만 2D 이미지 변환에 사용된다.

이미지 URL에서 ImagePresentationComponent를 직접 초기화하지 말고, URL에서 Spatial3DImage를 만들고 spatial 3D image를 사용하여 ImagePresentationComponent를 초기화할 수 있다.
하지만 component는 아직 spatial scene으로 표현될 준비가 덜 되었는데 이를 위해서는 먼저 scene을 생성해야 한다.
이를 위해 spatial 3D image의 generate method를 호출한다. 이렇게 하면 몇 초 안에 spatial scene이 생성된다. 성공적으로 생성된 후에는 ImagePresentationComponent의 availableViewingModes가 업데이트되어서 spatial3D 및 spatial3DImmersive 모드가 포함된다. 그런 다음 원하는 viewing mode를 선택하여 spatial scene을 window 모드로 표시하거나 immersive로 표시할 수 있다.

spatial scene을 미리 생성할 필요는 없고 사진 앱처럼 앱을 사용하는 사람이 버튼을 누를 때까지 기다리는 것이 좋다.
.generate를 호출하기 전에 component의 원하는 viewing mode를 .spatial3D로 설정하면 component에 spatial scene이 준비되는 즉시 표시되도록 알릴 수 있다.
이렇게 하면 생성 중에 component가 진행 애니메이션을 표시하고 생성이 완료되면 즉시 spatial scene을 표시한다.
비전 프로의 예시 (20:30)


VideoPlayerComponent의 업데이트로 다양한 몰입형 비디오 포맷의 재생을 지원한다.
포털 모드와 immersive mode 모두에서 전체 공간 스타일을 적용한 공간 비디오 재생을 지원한다.

180도, 360도, 광시야각 비디오 등 Apple Projected Media Profile 비디오도 지원된다. 사용자는 APMP 비디오에 대한 편안함 설정을 구성할 수 있고, RealityKit은 이에 맞게 재생을 자동으로 조절한다.

이러한 비디오 포맷은 Apple Immersive Video 외에도 다양한 viewing mode로 재생되도록 구성할 수 있다.

RealityKit은 shared space와 full space 모두에서 앱과 상호 작용할 수 있는 공간 액서서리에 대한 추적을 추가적으로 지원한다. 6 자유도로 공간 액세서리를 tracking 할 수 있으며, 앱과 게임의 상호 작용을 강화하는 햅틱 기능도 지원한다.

ViewAttachmentComponent를 통해 SwiftUI 뷰를 엔터티에 직접 추가하는 것이 매우 간편해졌다.
그리고 PresentationComponent를 사용하면 popover와 같은 모달 프레젠테이션을 엔터티에 추가할 수 있다.
또한, 새로운 GestureComponent는 SwiftUI 제스처를 엔터티에 추가하는 프로세스를 간소화한다.

한 엔터티를 다른 엔터티의 핀에 연결할 수 있는 새로운 엔터티 attach 메서드도 있다. 이 API는 에니메이션 뼈대의 관절에 mesh를 부착하는 작업을 대폭 간소화한다. 이런 방식으로 mesh를 첨부하면 mesh를 수동으로 정렬할 필요가 없고 비용이 많이 드는 계층적 변환 업데이트를 방지할 수 있다.

또한, 메모리 데이터 객체에서 엔터티를 로드할 수 있는 새로운 엔터티 initializer도 있다. 이 새로운 initializer를 사용하면 온라인 소스에서 전체 RealityKit 장면이나 USD를 로드하거나 네트워를 통해 스트리밍 할 수 있다.
새로운 initializer는 기존 entity initializer와 동일한 파일 형식을 지원한다.

AVIF 인코딩된 텍스처에 대한 지원을 추가하는데, 이는 10 비트 색상을 지원하면서도 크기는 훨씬 작으면서도 JPEG와 비슷한 품질을 제공한다. Mac의 Preview 앱이나 터미널의 usdcrush를 사용하여 이 압축을 활성화한 상태로 USD를 내보낼 수 있다.

그리고 HoverEffectComponent에는 GroupID라는 새로운 기능이 추가되었다. GroupID가 같으면 activation을 공유한다. 호버 효과에 GroupID를 할당하면 각 효과의 상대적 계층 구조에 관계없이 호버 효과가 활성화 되는 방식을 완벽하게 제어할 수 있다.
일반적으로 호버 효과는 왼쪽의 예처럼 계층적으로 작용하여 자식 엔터티는 부모 엔터티의 효과를 상속 받는다. 하지만 오른쪽 예처럼 A와 B가 같은 그룹 ID의 엔터티에 있는 경우 해당 엔티티는 자닛ㄴ의 효과를 자식 엔티티에 전파하지 않는다.

customPostProcessing API를 사용하면 Metal Performance Shaders, CIFilter, 또는 사용자 정의 셰이더를 사용하여 앱에 bloom과 같은 사용자 정의 효과를 추가할 수 있다. 이는 iOS, iPadOS, macOS, tvOS에서 지원된다.
올해의 RealityKit은 RealityKit을 이용한 3D 경험을 어느 때보다 쉽게 만드는 것에 중점을 두었다.
