스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
RealityKit Trace 알아보기
Reality Trace를 사용해 공간형 컴퓨팅 앱의 성능을 어떻게 개선할 수 있는지 발견하세요. 이 플랫폼을 위한 성능 프로파일링 가이드라인을 탐색하고, RealityKit Trace 템플릿이 앱의 렌더링 최적화를 어떻게 도와줄 수 있는지 알아보세요. 앱에서 다양한 유형의 콘텐츠를 프로파일링하여 성능 문제를 정확히 찾아내기 위한 지침을 제공합니다.
챕터
- 0:44 - Rendering
- 2:17 - Profiling spatial apps
- 3:34 - Introduction to RealityKit Trace
- 7:38 - Optimizing offscreen passes
- 11:30 - Optimizing asset rendering
- 14:02 - Optimizing system power impact
- 17:55 - Overview of optimized World app
- 19:01 - Recommendations
리소스
관련 비디오
WWDC23
WWDC21
Tech Talks
-
다운로드
♪ 멜로우 인스트루멘탈 힙합 ♪ ♪ 안녕하세요! 저는 Sarina이고 RealityKit Tools 팀의 소프트웨어 엔지니어입니다 저는 프로파일링 툴 엔지니어 Harjas입니다 오늘 저희 둘은 Instruments에서의 RealityKit Trace 템플릿을 소개해드릴 건데요 이 템플릿을 통해 공간 경험 성능을 최적화할 수 있는 방법을 보여드리겠습니다 성능은 공간 컴퓨팅의 사용자 경험에서 굉장히 중요합니다 공간 경험 최적화 방법을 알아보기 위해 이 플랫폼에서의 렌더링 작동 방식을 간략하게 소개하고 Instruments에서 RealityKit Trace 템플릿을 사용해 프로파일하는 법을 보여드리죠 그 다음 콘텐츠 최적화에 이용 가능한 다른 툴도 간략하게 다뤄볼게요 이 플랫폼에는 특수한 성능 제약 조건이 있습니다 이를 이해하기 위해서는 렌더링 작동 방식을 먼저 이해해야 하는데요 렌더링에는 앱 프로세스 렌더 서버 컴포지터가 포함됩니다 앱이 이 요소들과 상호작용하는 방식은 여러분이 만드는 경험의 유형에 따라 달라지는데요 공간형 앱을 위해 만들 수 있는 경험의 유형과 그들이 렌더링되는 방식을 살펴봅시다 플랫폼에서 앱은 공유 공간이나 전체 공간에 진입할 수 있습니다 여기에는 렌더링 방식을 기반으로 고려해야 하는 성능의 영향력이 다양한데요 여러 앱이 함께 실행될 때 이들은 동일한 공간에 렌더링됩니다 그래서 '공유 공간'이라고 부르는 것이기도 하죠 이건 렌더링 서버가 다른 앱을 렌더링하기 위해 하는 일에 의해서 앱의 성능이 영향을 받을 수 있다는 뜻입니다 렌더링 서버는 컴포지터와 작동해 최종 프레임을 생성하는데요 앱이 전체 공간에 들어가는 경우 눈에 보이는 다른 모든 앱이 숨겨집니다 이건 앱의 성능이 숨겨져 있는 앱에 대한 렌더링 작업에 의해 더 이상 영향을 받지 않음을 뜻하죠 전체 공간 진입 방식을 더 알아보고 싶으시다면 다음 세션을 확인해 주세요 방금 다룬 내용을 기반으로 앱 프로파일 방식 두 가지를 추천합니다 성능 문제를 조사하거나 시스템 전력 영향을 분석할 때면 앱을 단독으로 프로파일해야 하는데요 그래야 시스템 성능에 미치는 앱의 영향을 이해할 수 있죠 앱이 다른 앱과 함께 작동할 것으로 예상된다면 여러분의 앱을 그 앱들과 함께 프로파일해야 합니다 그건 사용자가 여러분의 앱을 경험하는 방식에 굉장히 중요한데요 공간형 앱을 프로파일해 앱의 성능만 최적화하는 방법을 알아봅시다 RealityKit Trace 템플릿을 사용해서요 저희는 Hello World에서 작업 중이었고 성능 문제가 없음을 보장하고 싶습니다 이건 앱의 Start 스크린인데요 SwiftUI View죠 이 뷰에는 Objects in Orbit 버튼이 있는데요 이 버튼을 탭해서 지구 궤도를 도는 물체에 대해 더 알아볼 수 있습니다 이 버튼은 새로운 뷰를 열어 지구의 궤도를 돌고 있는 다양한 물체의 예시를 보여줍니다 이 뷰에는 그 물체들의 3D 모델이 있는데요 위성 달 망원경이 여기에 포함되죠 이 뷰에는 View Orbits 버튼도 있습니다 이것도 버튼을 탭해서 탐색할 수 있고요 그럼 지구와 그 궤도를 도는 위성을 보여주는 몰입형 경험을 열어줍니다 이 모델에 대해서는 디테일한 에셋을 사용했고 저는 이들이 이 앱의 성능에 영향을 주고 있다고 생각합니다 이 몰입형 경험에서 우리는 이 위성이 지구를 돌 때 어떤 경로를 지나는지 확인할 수 있는데요 지구를 확대해서 더 자세히 볼 수도 있습니다 근데 이 상호작용이 너무나 끊기네요 여기에 성능 문제가 있는것 같습니다 그래서 이 경험을 프로파일해 봤습니다 RealityKit Trace 템플릿을 사용해서 말이죠 Harjas, 마저 설명해 주시겠어요? 물론이죠 RealityKit Trace에서 이용 가능한 기능을 함께 살펴보겠습니다 RealityKit Trace는 Instruments 15에서 새롭게 이용할 수 있는 템플릿입니다 실제 디바이스와 시뮬레이터의 프로파일에 사용 가능하죠 가장 정확하고 실행 가능한 정보를 얻기 위해 실제 디바이스를 프로파일해야 합니다 시뮬레이터에 대해 프로파일하는 경우 모든 타이밍 정보가 정확하지 않을 수 있습니다 Mac과 온디바이스의 하드웨어, 소프트웨어 차이 때문이죠 하지만 시간을 기반으로 하지 않는 통계 자료 일부를 빠르게 반복하고 개선하기 위해서는 여전히 사용할 수 있습니다 RealityKit Trace 템플릿에는 여러 툴이 포함되는데요 먼저 살펴볼 툴은 RealityKit Frames 툴입니다 이 툴은 디바이스가 렌더링 중인 각 프레임을 추적하죠 각 프레임의 렌더링 시간을 확대해서 볼 수 있습니다 이를 통해 프레임의 각 단계가 렌더링되려면 얼마나 걸리는지 볼 수 있고요 렌더링 파이프라인 중 어떤 부분이 성능 문제를 일으키는지 고급 아이디어를 줄 수 있죠 매끄러운 사용자 경험을 달성하기 위해서 앱은 초당 90프레임을 달성할 수 있어야 합니다 하지만 OS는 항상 90fps를 목표로 할 순 없죠 보여지는 콘텐츠와 디바이스가 있는 환경에 가장 적절한 프레임 속도로 렌더링할 겁니다 프레임 속도는 바뀔 수 있기 때문에 모든 프레임에는 렌더링 완료 데드라인이 있죠 그래야 디바이스가 타겟 프레임 속도가 어떻든 결국 도달할 수가 있으니까요 프레임은 3개 그룹으로 분류됩니다 데드라인 내에 제대로 완료되는 프레임 데드라인 내에 거의 완료되지 않는 프레임 데드라인이 지나 실행되어 프레임 드롭으로 이어지는 프레임 이 분류들은 색깔 코드인데요 각각 초록색, 주황색 빨간색입니다 데드라인이 지나 실행되는 프레임은 사용자 경험에 부정적인 영향을 줄 겁니다 상위 레벨에서 프레임을 확대해 확인하면 컬러 코딩을 통해 트레이스에서 문제 부분을 빠르게 찾을 수가 있죠 그래서 성능 조사 범위를 가장 많은 프레임 드롭이 있는 영역으로 좁히면 됩니다 이 기능은 개별 프레임 외에도 시스템이 각 프레임의 렌더링을 위해서 CPU나 GPU에 사용한 평균 시간도 시각화합니다 다음에 확인하실 툴은 RealityKit Metrics인데요 최상위 레벨에서 이 툴은 감지한 모든 병목 현상을 이끌어냅니다 이 병목 현상은 렌더링 파이프라인 전체에서 종합적인 타이밍 정보를 보아 생성된 것이죠 프레임이 데드라인을 초과하는 시간과 동일하게 발생하는 병목 현상을 우선시하세요 아래 상세 뷰를 보시면 이 RealityKit 병목 현상이 심각성과 유형에 따라 요약된 게 보이실 겁니다 툴이 찾은 병목 현상이 정확히 무엇인지 전반적인 성능에 얼마나 많은 영향을 주는지 더 확인해 볼 수 있죠 확장 상세 뷰에서는 이 병목 현상을 진단하는 법과 그걸 완화하기 위해 취할 수 있는 단계는 무엇인지 권고안을 제공합니다 RealityKit Metrics 트랙을 확장하여 렌더링 파이프라인의 다른 구성 요소들로부터 다양한 유형의 지표가 제시될 수 있습니다 이 통계는 앱이 현재 제시하고 있는 씬의 전체 복잡도를 이해하는 데에 도움이 되죠 주요 지표 중 일부는 관련된 경계를 가져 해당 지표에 대해 합리적인 예측을 알려주죠 이 예측을 사용하면 병목 현상 진단과 프레임이 데드라인을 맞추지 못한 이유를 더 잘 알 수 있을 겁니다 RealityKit Metrics는 앱의 RealityKit Trace 시스템 실행을 위해 각 프레임에 쓰인 시간을 시각화할 겁니다 여기에는 모든 빌트인 시스템과 앱이 구현할 수 있는 모든 커스텀 시스템이 포함되죠 이 정보는 Time Profiler와 가장 잘 결합되어 RealityKit 시스템 코드를 최적화할 수 있습니다 마지막으로 RealityKit Metrics에 제시된 System Power Impact를 검토해 봅시다 일관적이고 멋진 사용자 경험을 제공하기 위해 앱이 작업하는 데에 있어 내부에서 필요한 전력 엔벨롭을 이해하기 위해서요 이제 Hello World가 열릴 때 취한 트레이스를 살펴보도록 합시다 앱의 첫 번째 씬은 Start 스크린이었는데요 이건 SwiftUI에서 구현됐죠 프레임 툴에서 보니 이 트레이스 전반에 걸쳐 드롭 프레임이 꽤 있는데요 이 프레임들은 중요해 보이지 않을지 몰라도 실제로 사용자 경험에 해가 될 수 있습니다 Option-drag를 사용해 더 문제가 되는 영역 중 한 곳을 확대해 보겠습니다 시간 범위를 조정해서 RealityKit Metrics 툴이 어떤 병목 현상을 찾은 건지 확인할 수가 있는데요 이렇게 오랫동안 프레임이 실행되는 동안 말이죠 Instrument에서는 이 때 가장 큰 병목 현상이 Core Animation Encoding에서 일어났다는 걸 찾았습니다 Core Animation 통계 자료를 확인해 볼게요 RealityKit Metrics 툴의 옆에 있는 더보기 삼각형 버튼을 클릭하고 Core Animation이라고 표시된 트랙을 선택해 주세요 이 지표는 무엇이 이 프레임 드롭을 유발했는지 그 정보를 줄 수 있습니다 이 지표를 조사하면서 지표 중 일부가 얼마나 심각한지 맥락을 보여준다는 걸 아실텐데요 이는 타임라인에서 컬러 코딩으로 반영됩니다 그럼 이 주요 지표들에 대해 합당한 경계가 무엇인지 더 잘 알 수가 있죠 타임라인 시각화를 기반으로 보면 오프스크린 프리페어의 수에 대해 추천 경계를 넘고 있다는 게 명확합니다 아래 요약 내용은 오프스크린 프리페어의 평균 횟수가 180이라는 걸 알려주죠 평균치고는 높습니다 Core Animation 통계를 고려할 때 기억하셔야 하는 세 가지 작업이 있는데요 먼저 투명성과 블러 효과가 시스템에서는 비용이 큰 연산이 된다는 겁니다 이 효과들은 사용자에게 가장 큰 영향력을 전달할 때 사용하시고 아니면 사용을 아껴두세요 렌더링 패스의 숫자는 Core Animation이 전체 이미지에 대해 개별 렌더링하기 위해 갖는 레이어의 갯수로 결정됩니다 마지막으로 오프스크린 패스인데요 이름이 의미하듯이 오프스크린 패스는 디스플레이가 아닌 오프스크린에 렌더링되는 렌더링 패스입니다 오프스크린 패스는 현재 하고 있는 것을 정지하고 사용자에게 보이지 않을 일을 하려면 렌더링 패스를 필요로 합니다 하지만 오프스크린 패스 출력은 정규 렌더링 패스 지속을 위해 필요하죠 오프스크린 패스는 특히 공간형 앱에 대해 영향력이 크고요 다른 앱 플랫폼과 다르게 이 플랫폼에서는 공간형 앱을 계속 렌더링하는데 그건 모든 단일 프레임이 환경 요인을 고려해야 하기 때문입니다 사용자의 머리 움직임 같은 것들을요 따라서 정적 UI는 시스템의 목표 프레임 속도로 렌더링될 수 있을 정도로 효율적이어야 합니다 오프스크린 패스를 일으킬 수 있는 주요 작업 유형에는 4가지가 있는데요 그림자 마스킹 둥근 사각형 시각 효과입니다 오프스크린 패스에 대해 더 알아보시려면 아래에 나와 있는 Tech Talks 영상을 확인해 주세요 오프스크린 패스는 굉장히 많기 때문에 이 뷰에 대한 SwiftUI 코드를 확인하여 어떤 것이 원인이 된 것인지 알아보겠습니다 SwiftUI 코드에서 이 뷰는 마스킹이나 시각 효과가 적용되어 있지 않은데요 하지만 그림자를 적용 중인 인스턴스들이 있습니다 이 SwiftUI View 항목에는 여러 버튼에 그림자가 적용되어 있습니다 그림자는 특히 비용이 큰 연산이죠 특히 투명성과 결합되었을 때는요 그림자가 공간형 앱에 유용한 UI 이디엄이기는 하나 그림자는 사용자에게 상당한 영향을 줄 때만 사용해야 합니다 이 그림자를 비활성화하고 새로운 트레이스를 살펴볼게요 그림자를 비활성화하면 RealityKit Frames Instrument에 프레임 문제가 거의 없고 RealityKit Metrics는 오프스크린 패스가 4번 줄었다고 보고합니다 Hello World 앱에서 본 그 다음 씬은 궤도 뷰에 있는 물체들이었는데요 그 씬에서 트레이스를 열어서 최적화 가능한 게 있는지 확인해 보겠습니다 Frames Instrument에서는 병목 현상이 많은 트레이스 전반에 걸쳐 드롭 프레임이 분산되어 있는데요 RealityKit Metrics의 상세 뷰를 보면 병목 현상에 대한 요약 내용이 제공됩니다
이 요약을 보니 병목 현상 대부분이 GPU Work Stalls와 관련이 있네요 가장 자주 보고되는 병목 현상 유형이 GPU 멈춤이기 때문에 다시 한번 RealityKit Metrics를 확장해 보겠습니다 하지만 이번에는 3D Render 트랙을 사용할게요
우선 프레임 드롭 수가 많은 트레이스 영역을 선택할 겁니다 이 시간 선택에서 3D Render 지표는 삼각형과 정점 수가 추천 경계를 훨씬 초과한다고 보고하고 있죠 다음으로는 프레임 드롭이 그렇게 많지 않은 트레이스 영역을 강조할 건데요
렌더링 지표에 따르면 삼각형과 정점 수는 추천 경계 내에 있습니다 이 씬에서 앱이 사용 중인 에셋의 갯수와 품질을 제대로 평가해야 한다는 것을 뜻합니다 에셋 렌더링을 최적화할 때 RealityKit Metrics의 3D Rendering 그룹에서 삼각형, 정점, 드로우콜을 먼저 확인하세요 가능하다면 이들을 최적화하기 위해 단순한 쉐입 메시를 사용하세요 에셋을 동일한 메시로 활용하고자 한다면 인스턴싱을 이용하세요 3D 콘텐츠 조립 편집, 미리보기가 가능한 Reality Composer Pro에서 통계를 사용한 에셋의 복잡도를 확인하세요 해당 콘텐츠를 나중에 Xcode 프로젝트에서 코드를 통해 직접 접근할 수 있습니다 이 툴과 멋진 에셋을 만드는 법에 대해 알아보시려면 다음 세션을 확인하세요 저는 이미 사용 중이던 에셋을 다각형을 덜 사용하고 새로운 트레이스를 포착한 에셋으로 바꿔 놓았는데요 이 트레이스에서 Frames Instrument는 모든 프레임이 데드라인을 맞췄다고 보고하고 있습니다 3D 렌더링 통계를 다시 한번 확인하면
삼각형과 정점 갯수가 상당히 줄어든 것을 확인할 수 있네요 이 에셋은 다각형을 덜 사용하긴 했지만 경험의 품질에는 손상이 없었습니다 다음 트레이스는 Earth 모델과 상호작용할 때를 위한 겁니다 이 씬 동안에는 지구 크기 조절이 사실 꽤 불안정합니다 RealityKit Metrics는 System Power Impact 레인이 상당한 시간 동안 굉장히 높았다고 보고하네요 이건 앱 일부분이 매우 비효율적이고 사용자 경험이 영향을 받을 수도 있다는 것을 나타냅니다 디바이스 시스템 전력 영향이 노미널 상태에서 가능한 오랜 시간 동안 유지되면서 앱 작동도 잘 되도록 해야 합니다 시스템 전력 영향을 줄이려 프로파일링할 때 항상 앱을 단독으로 프로파일하세요 실행 가능한 정보를 최대로 얻을 수 있도록 말이죠 여러 접근법을 사용해 영향을 낮출 수 있습니다 먼저 RealityKit Metrics 통계가 예상 내에 있도록 해주세요 예상을 초과해 버리면 디바이스는 더 높은 전력으로 더 오래 운영되면서 매끄러운 경험을 제공할 수가 없게 됩니다 다음으로 CPU, GPU가 하고 있는 작업을 확인하세요 CPU의 경우 Time Profiler가 고전력 유도 영역에서 높은 CPU 사용률을 보고하고 있는지 확인하세요 만약 그렇다면 Time Profiler를 사용해 CPU 바운드 코드를 최적화하세요 GPU의 경우 성능 상태가 있습니다 GPU가 최대 단계에 있으면 상당한 전력량을 유도하는데요 이런 경우 Metal System Trace 템플릿을 사용해 GPU에서 어떤 작업이 진행 중인지 봐야 합니다 이렇게 어떤 게 최적화될 수 있는지 이해할 수 있죠 트레이스로 다시 돌아갑시다 Time Profiler는 우리에게 CPU 사용이 이 영역에서 평균 100%이고 GPU 성능 상태는 이 시간 동안 대부분 최소라고 알려줍니다 Time Profiler를 사용해서 높은 CPU 사용 원인을 알 수 있죠 가장 무거운 스택 트레이스는 확장 상세 뷰에 있습니다 이건 Time Profiler의 유용한 기능인데요 콜트리에서 코드의 가장 비용이 큰 부분을 빠르게 찾을 수 있도록 해주기 때문이죠 이 프레임들을 보니 Entity.makeModel이 많은 CPU 시간을 차지하네요 아래의 다음 프레임은 Entity.generateCollisionShapes를 호출 중입니다 따라서 성능 문제는 모델과 콜리전 쉐입을 지속적으로 생성함으로써 나타나는 것으로 보입니다 비용이 큰 운영인데 말이죠 이제 Xcode를 열어 뭘 할 수 있는지 살펴봅시다 이건 Entity.makeModel 함수 호출입니다 보여지는 콜트리는 긴 CPU 타임을 차지하죠 이건 makeGlobe 함수 내에서 호출될 겁니다 makeGlobe 함수를 컨트롤 클릭하면 누가 이걸 호출하는지 확인할 수 있는데요 Orbit SwiftUI 뷰 본문에서 호출되고 있네요 이건 피해야 할 안티패턴입니다 뷰 바디가 아주 빠르게 계산되어야 하거든요 SwiftUI 뷰 본문에서는 모델 로딩이나 다른 비용이 큰 운영을 피하셔야 합니다 뷰의 상태가 변할 때마다 그런 비용이 큰 운영들이 다시 계산되어야 하거든요 그럼 이제 뷰 본문에서 이 호출을 삭제해 보겠습니다 다음으로 ViewModel에서 Earth 엔티티의 재사용 가능 버전을 추가하겠습니다 마지막으로 Orbit View에서 재사용 가능한 Earth 엔티티를 사용할 겁니다 뷰 본문이 다시 계산되면 앱은 동일한 모델 재로딩에 시간 낭비를 안 해도 되죠 이렇게 고치고 나서 트레이스를 확인해 보니 전력 영향이 노미널 상태로 낮아졌네요 Time Profiler는 CPU 사용이 100%에서 10%로 떨어졌다고 보고하네요 이 모든 최적화 이후 보고된 병목 현상은 거의 없습니다 거의 대부분의 프레임이 데드라인을 맞추고 있고 전력은 예상 내에 있네요 이제 Hello World 앱은 이 플랫폼에 대해 아주 잘 최적화된 앱입니다 오프스크린 패스의 수를 줄이고 고다각형 에셋을 합당한 에셋으로 대체했으며 CPU와 전력 사용도 줄였죠 이제 이 앱의 최적화 버전을 살펴볼 차례입니다 시작 스크린은 좋아보이고 사용자 경험에 그림자가 많이 추가되지 않은 걸 보니 괜찮은 최적화입니다 다음은 Objects in Orbit을 열어보겠습니다 다각형이 덜 있는 에셋을 사용했는데도 모델이 괜찮아 보이네요 추가 세부 사항은 자원을 낭비하죠 마지막으로 Earth 모델을 다시 열어서 크기를 조절해 보겠습니다
이 상호작용은 버터만큼이나 부드럽네요 이 새로운 플랫폼에 대한 앱 최적화에 있어서 RealityKit Trace를 어떻게 사용할지 살펴봤습니다 Sarina, 개발자들이 또 어떤 툴을 사용할 수 있죠? 공간 컴퓨팅을 위한 앱 최적화를 돕는 툴은 굉장히 다양한데요 SwiftUI 콘텐츠 최적화를 위해 Instruments에는 SWift UI 분석부터 Core Animation, Hangs를 분석하는 툴이 있습니다 Hangs 툴에 대해 더 알아보고 싶으시다면 다음 세션을 확인해 주세요 3D 에셋 기반 콘텐츠를 최적화하기 위해 이용할 수 있는 툴도 다양합니다 Time Profiler는 앱이 최대 시간을 쓰는 영역을 찾을 수 있도록 도와주죠 에셋 로딩에 엄청난 시간이 소요되는 것처럼요 RealityKit Metrics는 씬에 너무나 많은 에셋이 있거나 너무 복잡한 에셋이 있을 때 진단을 도와줍니다 마지막으로 Reality Composer Pro를 사용해 씬을 조립할 때 에셋이 얼마나 복잡한지 확인할 수 있습니다 Reality Composer Pro에 대해 더 알아보시려면 다음 세션을 확인해 주세요 앱에서 Metal을 사용 중이시라면 Instruments에서 가장 유용한 툴은 Metal System Trace 템플릿일 겁니다 이 템플릿에는 GPU 타임라인 CPU 카운터, 성능 상태 등 주요 지표들이 있죠 이 템플릿을 비롯해 Metal 콘텐츠 프로파일링을 위한 다른 툴도 알아보시려면 다음 세션을 확인해 보시기 바랍니다 정리해 보면 성능은 이 플랫폼의 본질입니다 앱은 잘 최적화되어 가능한 최고의 사용자 경험을 전달해야 하죠 그리고 RealityKit Trace를 사용해 앱의 성능 병목 현상을 찾을 수 있습니다 다른 툴과의 적극적인 프로파일링과 Reality Composer Pro에서의 콘텐츠 점검 또한 성능 문제 해결에 도움이 될 수 있습니다 앱 최적화를 위해 RealityKit Trace 템플릿 사용법을 더 알아보시려면 개발자 문서를 확인해 주세요 이 플랫폼에서의 성능에 대해 더 알아보고 싶으시다면 아래 나와 있는 세션을 확인해 주시고요 공간 컴퓨팅 앱 최적화를 즐겨보세요 트레이스 상관 없이 말이죠 다들 시청해 주셔서 감사합니다 ♪
-
-
10:50 - SwiftUI View with High Offscreens
private struct Item: View { var module: Module // The corner radius of the item's hightlight when selected or hovering. let cornerRadius = 20.0 var body: some View { NavigationLink(value: module) { VStack(alignment: .leading, spacing: 3) { Text(module.eyebrow) .font(.titleHeading) .foregroundStyle(.secondary) VStack(alignment: .leading, spacing: 7) { Text(module.heading) .font(.largeTitle) Text(module.abstract) } } .padding(.horizontal, 5) .padding(.vertical, 20) } .buttonStyle(.bordered) .shadow(radius: 10) .buttonBorderShape(.roundedRectangle(radius: cornerRadius)) .frame(minWidth: 150, maxWidth: 280) } }
-
16:33 - EarthEntity Factory
class EarthEntity: Entity { static func makeGlobe() -> EarthEntity { EarthEntity(earthModel: Entity.makeModel( name: "Earth", filename: "Globe", radius: 0.35, color: .blue) ) } static func makeCloudyEarth() -> EarthEntity { let earthModel = Entity() earthModel.name = "Earth" Task { if let scene = await loadFromRealityComposerPro( named: WorldAssets.rootNodeName, fromSceneNamed: WorldAssets.sceneName ) { earthModel.addChild(scene) } else { fatalError("Unable to load earth model") } } return EarthEntity(earthModel: earthModel) } }
-
16:53 - Orbit SwiftUI View Body
struct Orbit: View { @EnvironmentObject private var model: ViewModel var body: some View { Earth( world: EarthEntity.makeGlobe(), earthConfiguration: model.orbitEarth, satelliteConfiguration: [model.orbitSatellite], moonConfiguration: model.orbitMoon, showSun: true, sunAngle: model.orbitSunAngle, animateUpdates: true ) .place( initialPosition: Point3D([475, -1200.0, -1200.0]), useCustomGesture: model.useCustomGesture, handOffset: model.customGestureHandOffset, isCustomGestureAnimated: model.isCustomGestureAnimated, debugCustomGesture: model.debugCustomGesture, scale: $model.orbitEarth.scale) } }
-
17:26 - SwiftUI ViewModel
class ViewModel: ObservableObject { // MARK: - Navigation @Published var navigationPath: [Module] = [] @Published var titleText: String = "" @Published var isTitleFinished: Bool = false var finalTitle: String = "Hello World" // MARK: - Globe @Published var globeEarthEntity: EarthEntity = .makeGlobe() @Published var isShowingGlobe: Bool = false @Published var globeEarth: EarthEntity.Configuration = .globeEarthDefault @Published var globeEarthOffset: SIMD3<Double> = [0, 0, 0] @Published var globePanelOffset: SIMD3<Double> = [0, -50, 30] @Published var showSatelliteButton: Bool = false @Published var isShowingSatellite: Bool = false // MARK: - Orbit @Published var orbitEarthEntity: EarthEntity = .makeGlobe() @Published var useCustomGesture: Bool = true @Published var customGestureHandOffset: SIMD3<Float> = [0, 0.21, -0.07] @Published var isCustomGestureAnimated: Bool = false @Published var debugCustomGesture: Bool = false @Published var orbitSatelliteScale: Float = 0.9 @Published var orbitMoonScale: Float = 0.9 @Published var orbitTelescopeScale: Float = 0.8 @Published var orbitSatelliteZOffset: Double = 100 @Published var orbitMoonZOffset: Double = 100 @Published var orbitTelescopeZOffset: Double = 100 @Published var isShowingOrbit: Bool = false @Published var orbitImmersionStyle: ImmersionStyle = .mixed @Published var orbitEarth: EarthEntity.Configuration = .orbitEarthDefault @Published var orbitSatellite: SatelliteEntity.Configuration = .orbitSatelliteDefault @Published var orbitMoon: SatelliteEntity.Configuration = .orbitMoonDefault @Published var orbitSunAngle: Angle = .degrees(150) var orbitSunAngleBinding: Binding<Float> { Binding<Float>( get: { Float(self.orbitSunAngle.degrees) }, set: { self.orbitSunAngle = .degrees(Double($0)) } ) } // MARK: - Solar System @Published var solarEarthEntity: EarthEntity = .makeCloudyEarth() @Published var isShowingSolar: Bool = false @Published var solarImmersionStyle: ImmersionStyle = .full @Published var solarEarth: EarthEntity.Configuration = .solarEarthDefault @Published var solarSatellite: SatelliteEntity.Configuration = .solarTelescopeDefault @Published var solarMoon: SatelliteEntity.Configuration = .solarMoonDefault @Published var solarSunDistance: Double = 700 @Published var solarSunAngle: Angle = .degrees(280) @Published var solarSunSpotIntensity: Float = 10.5 @Published var solarSunEmissionIntensity: Float = 10.5 var solarSunPosition: SIMD3<Float> { [Float(solarSunDistance * sin(solarSunAngle.radians)), 0, Float(solarSunDistance * cos(solarSunAngle.radians))] } }
-
17:33 - SwiftUI Orbit View Body
struct Orbit: View { @EnvironmentObject private var model: ViewModel var body: some View { Earth( world: model.globeEarthEntity, earthConfiguration: model.orbitEarth, satelliteConfiguration: [model.orbitSatellite], moonConfiguration: model.orbitMoon, showSun: true, sunAngle: model.orbitSunAngle, animateUpdates: true ) .place( initialPosition: Point3D([475, -1200.0, -1200.0]), useCustomGesture: model.useCustomGesture, handOffset: model.customGestureHandOffset, isCustomGestureAnimated: model.isCustomGestureAnimated, debugCustomGesture: model.debugCustomGesture, scale: $model.orbitEarth.scale) } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.