후기 단계 다시 프로젝션

LSR(후기 단계 재투영)은 사용자가 이동할 때 홀로그램을 안정화하는 데 도움이 되는 하드웨어 기능입니다.

정적 모델 주변을 이동할 경우 해당 모델 위치를 시각적으로 유지해야 합니다. 정적 모델이 불안정한 것으로 표시되는 경우 이 동작은 LSR 문제를 나타낼 수 있습니다. 애니메이션 또는 폭발 뷰와 같은 추가 동적 변환은 이 동작을 마스킹할 수 있습니다.

두 개의 다른 LSR 모드(즉, 평면 LSR 또는 깊이 LSR) 중에서 선택할 수 있습니다. 두 LSR 모드는 별도의 제한이 있기는 하지만 홀로그램의 안정성을 향상시킵니다. 대부분의 경우 더 나은 결과를 제공하는 깊이 LSR을 사용하여 시작합니다.

LSR 모드를 설정하는 방법

사용되는 LSR 모드는 클라이언트 애플리케이션이 깊이 버퍼를 전송하는지 여부에 따라 결정됩니다. 깊이 버퍼가 제출되면 깊이 LSR평면 LSR을 사용합니다.

다음 단락에서는 Unity 및 네이티브 애플리케이션에서 깊이 버퍼를 제출하는 방법을 각각 설명합니다.

Unity

Unity 편집기에서 File > Build Settings로 이동합니다. 왼쪽 아래에서 Player Settings를 선택하고 Player > XR Settings > Virtual Reality SDKs > Windows Mixed Reality에서 Enable Depth Buffer Sharing이 선택되어 있는지 확인합니다.

깊이 버퍼 공유 사용 플래그

해당 항목이 선택된 경우 앱은 깊이 LSR을 사용하고, 그렇지 않은 경우 평면 LSR을 사용합니다.

OpenXR을 사용하는 경우 깊이 버퍼는 항상 제출되어야 합니다. 설정은 XR Plug-in Management > OpenXR에서 확인할 수 있습니다. 그런 다음, OpenXR 플러그 인의 확장을 통해 재투영 모드를 변경할 수 있습니다.

using Microsoft.MixedReality.OpenXR;

public class OverrideReprojection : MonoBehaviour
{
    void OnEnable()
    {
        RenderPipelineManager.endCameraRendering += RenderPipelineManager_endCameraRendering;
    }
    void OnDisable()
    {
        RenderPipelineManager.endCameraRendering -= RenderPipelineManager_endCameraRendering;
    }

    // When using the Universal Render Pipeline, OnPostRender has to be called manually.
    private void RenderPipelineManager_endCameraRendering(ScriptableRenderContext context, Camera camera)
    {
        OnPostRender();
    }

    // Called directly when using Unity's legacy renderer.
    private void OnPostRender()
    {
        ReprojectionSettings reprojectionSettings = default;
        reprojectionSettings.ReprojectionMode = ReprojectionMode.PlanarManual; // Or your favorite reprojection mode.
        
        // In case of PlanarManual you also need to provide a focus point here.
        reprojectionSettings.ReprojectionPlaneOverridePosition = ...;
        reprojectionSettings.ReprojectionPlaneOverrideNormal = ...;
        reprojectionSettings.ReprojectionPlaneOverrideVelocity = ...;

        foreach (ViewConfiguration viewConfiguration in ViewConfiguration.EnabledViewConfigurations)
        {
            if (viewConfiguration.IsActive && viewConfiguration.SupportedReprojectionModes.Contains(reprojectionSettings.ReprojectionMode))
            {
                viewConfiguration.SetReprojectionSettings(reprojectionSettings);
            }
        }
    }
}

네이티브 C++ 애플리케이션

깊이 버퍼 제출은 WMR 또는 OpenXR 버전과 관계없이 네이티브 C++ 바인딩 코드를 완전히 제어합니다. 충족해야 하는 유일한 조건은 GraphicsBinding::BlitRemoteFrame이 호출될 때 깊이 버퍼가 그래픽 API에 바인딩되어야 한다는 것입니다.

깊이 LSR

깊이 LSR이 작동하려면 클라이언트 애플리케이션에서 LSR 중에 고려해야 하는 모든 관련 기하 도형을 비롯한 유효한 깊이 버퍼를 제공해야 합니다.

깊이 LSR은 제공된 깊이 버퍼의 내용에 따라 비디오 프레임을 안정화하려고 시도합니다. 결과적으로 투명 개체와 같이 렌더링되지 않은 콘텐츠는 LSR에서 조정할 수 없으며 불안정성 및 재투영 아티팩트가 표시될 수 있습니다.

투명 개체의 재투영 불안정을 완화하기 위해 깊이 버퍼 쓰기를 강제로 수행할 수 있습니다. PBR 재질의 경우 TransparencyWritesDepth 재질 플래그를 참조하세요. 그러나 이 플래그를 사용하도록 설정하면 투명/불투명 개체 상호 작용의 시각적 품질이 저하될 수 있습니다.

평면 LSR

평면 LSR에는 깊이 LSR에 포함된 픽셀별 깊이 정보가 없습니다. 대신 각 프레임을 제공해야 하는 평면을 기반으로 모든 콘텐츠를 재투영합니다.

평면 LSR은 제공된 평면과 가까이 있는 개체를 가장 잘 재투영합니다. 개체가 멀수록 더 불안정합니다. 깊이 LSR은 서로 다른 수준에서 개체를 재투영할 때 더 적합한 반면 평면에 잘 정렬된 콘텐츠의 경우 평면 LSR이 더 적합할 수 있습니다.

Unity에서 평면 LSR 구성

평면 매개 변수는 이른바 포커스 포인트에서 파생됩니다. WMR을 사용하는 경우 포커스 포인트는 UnityEngine.XR.WSA.HolographicSettings.SetFocusPointForFrame을 통해 모든 프레임을 설정해야 합니다. 자세한 내용은 Unity 포커스 포인트 API를 참조하세요. OpenXR의 경우 이전 섹션에 표시된 ReprojectionSettings를 통해 포커스 포인트를 설정해야 합니다. 포커스 포인트를 설정하지 않으면 대체(fallback)가 선택됩니다. 하지만 자동 대체는 차선의 결과를 초래합니다.

Remote Rendering 호스트에서 계산된 항목을 기반으로 하는 것이 적합할 수 있지만 포커스 포인트를 직접 계산할 수 있습니다. RemoteManagerUnity.CurrentSession.GraphicsBinding.GetRemoteFocusPoint를 호출하면 됩니다.

일반적으로 클라이언트와 호스트는 모두 클라이언트의 UI 요소와 같이 다른 쪽에서 인식되지 않는 콘텐츠를 렌더링합니다. 따라서 원격 포커스 포인트를 로컬로 계산된 것과 결합하는 것이 적합할 수 있습니다.

연속된 두 프레임에서 계산되는 포커스 포인트는 많이 다를 수 있습니다. 이를 그대로 사용하면 홀로그램이 흔들리게 표시될 수 있습니다. 이 동작을 방지하기 위해 이전 포커스 포인트와 현재 포커스 포인트를 보간하는 것이 좋습니다.

재투영 포즈 모드

하이브리드 렌더링의 일반적인 문제 범위는 다음과 같이 명시할 수 있습니다. 원격 및 로컬 콘텐츠는 서버에서 원격 포즈를 예측하는 반면 로컬 포즈는 실제 현재 포즈이므로 고유한 포즈(즉, 좌표 공간) 내에 있습니다. 그러나 렌더링 프레임이 끝나면 원격 및 로컬 콘텐츠를 모두 정렬하고 디스플레이로 가져와야 합니다. 다음 그림에서는 디스플레이 뷰포트에 비해 로컬 및 원격 포즈가 번환되는 예제를 보여 줍니다.

대상 뷰포트와 관련된 원격 및 로컬 포즈를 보여 주는 다이어그램

사용된 GraphicsBinding에 따라 ARR은 위에서 설명한 LSR 모드로 직교적으로 작동하는 최대 3개의 재투영 모드를 제공합니다. 이러한 모드를 Remote pose mode, Local pose mode, Passthrough pose mode라고 합니다. LSR 모드와 달리 포즈 모드는 원격 및 로컬 콘텐츠가 결합되는 방법을 정의합니다. 모드 선택은 런타임 성능을 위해 로컬 콘텐츠의 시각적 품질을 교환하므로 애플리케이션은 적절한 옵션을 신중하게 고려해야 합니다. 아래 고려 사항을 참조하세요.

Remote pose mode

Remote pose mode이 ARR의 기본 모드입니다. 이 모드에서는 로컬 콘텐츠가 원격 프레임의 원격 포즈를 사용하여 들어오는 원격 이미지 스트림 위에 렌더링됩니다. 그런 다음, 최종 재투영을 위해 결합된 결과가 OS로 전달됩니다. 이 방법은 재투영을 하나만 사용하지만 최종 수정은 왕복 간격을 기반으로 하므로 전체 재투영 오류도 로컬 콘텐츠에 적용됩니다. 결과적으로 큰 보정 델타는 UI 요소를 포함하여 로컬 기하 도형을 크게 왜곡할 수 있습니다.

위의 그림을 사용하여 다음 변환이 Remote pose mode에 적용됩니다.

원격 포즈 모드의 재투영 단계입니다.

Local pose mode

이 모드에서 재투영은 두 가지 개별 단계로 분할됩니다. 첫 번째 단계에서 원격 콘텐츠는 로컬 포즈 공간, 즉 로컬 콘텐츠가 기본적으로 VR/AR 디바이스에서 렌더링되는 공간으로 재투영됩니다. 그 후 로컬 콘텐츠는 일반적인 로컬 포즈를 사용하여 미리 변환된 이미지 위에 렌더링됩니다. 두 번째 단계에서는 최종 재투영을 위해 결합된 결과가 OS로 전달됩니다. 이 두 번째 재투영은 ARR이 없는 경우 사용되는 것과 동일한 델타인 작은 델타만 발생하므로 로컬 콘텐츠의 왜곡 아티팩트가 크게 완화됩니다.

따라서 그림은 다음과 같습니다.

로컬 포즈 모드에서 재투영 단계입니다.

Passthrough pose mode

이 포즈 모드는 기본적으로 Remote pose mode와 동일하게 작동합니다. 즉, 로컬 및 원격 콘텐츠가 원격 공간에서 결합됩니다. 그러나 결합된 후에는 콘텐츠가 재투영되지 않지만 원격 포즈 공간에 남아 있습니다. 이 모드의 주요 장점은 결과 이미지가 재투영 아티팩트의 영향을 받지 않는다는 것입니다.

개념적으로 이 모드는 기존 클라우드 스트리밍 애플리케이션과 비교할 수 있습니다. 대기 시간이 길기 때문에 헤드 탑재 시나리오에는 적합하지 않지만 더 높은 이미지 품질이 필요한 데스크톱 및 기타 평면 화면 애플리케이션에 실행 가능한 대안입니다. 따라서 당분간 GraphicsBindingSimD3D11에서만 사용할 수 있습니다.

성능 및 품질 고려 사항

포즈 모드를 선택하면 시각적 품질과 성능에 영향을 줍니다. HoloLens 2 디바이스의 Local pose mode에서 추가 재투영을 수행하기 위한 클라이언트 쪽의 추가 런타임 비용은 GPU 시간의 프레임당 약 1밀리초에 달합니다. 클라이언트 애플리케이션이 이미 프레임 예산인 16밀리초에 가까운 경우 이 추가 비용을 고려해야 합니다. 반면에는 로컬 콘텐츠가 없거나 아티팩트가 왜곡되는 경향이 없는 로컬 콘텐츠가 있는 애플리케이션 유형이 있습니다. 이러한 경우에는 원격 콘텐츠 재투영의 품질이 영향을 받지 않으므로 시각적 Local pose mode의 이점이 없습니다.

따라서 일반적인 권장 사항은 사용 사례별로 모드를 테스트하고 시각적 품질 향상이 추가 성능 오버헤드를 정당화하는지 확인하는 것입니다. 모드를 동적으로 전환할 수도 있습니다. 예를 들어 중요한 UI가 표시되는 경우에만 로컬 모드를 사용하도록 설정할 수 있습니다.

런타임에서 Pose mode를 변경하는 방법

다음 클라이언트 API를 사용하여 런타임에 모드를 변경할 수 있습니다.

RenderingSession session = ...;
session.GraphicsBinding.SetPoseMode(PoseMode.Local); // set local pose mode
ApiHandle<RenderingSession> session = ...;
session->GetGraphicsBinding()->SetPoseMode(PoseMode::Local); // set local pose mode

일반적으로 그래픽 바인딩 개체를 사용할 수 있을 때마다 모드를 변경할 수 있습니다. GraphicsBindingSimD3D11에 대한 중요한 차이점이 있습니다. 포즈 모드는 프록시 텍스처로 초기화된 경우에만 PoseMode.Remote로 변경할 수 있습니다. 그렇지 않은 경우 그래픽 바인딩이 다시 초기화될 때까지 PoseMode.LocalPoseMode.Passthrough 사이에서만 포즈 모드를 전환할 수 있습니다. ID3D11Texture2D 개체(프록시 경로) 또는 원하는 사용자 뷰포트의 widthheight(비 프록시 경로)에 대한 네이티브 포인터를 사용하는 GraphicsBindingSimD3d11.InitSimulation의 두 오버로드를 참조하세요.

데스크톱 Unity 런타임 고려 사항

GraphicsBindingSimD3D11의 기술적 배경과 Unity에서 오프스크린 렌더링이 작동하는 방식 때문에 ARR Unity 런타임에서는 다음과 같이 시작 RemoteManagerUnity 시 원하는 포즈 모드를 지정해야 합니다.

public static void InitRemoteManager(Camera camera)
{
    RemoteUnityClientInit clientInit = new RemoteUnityClientInit(camera, PoseMode.Remote);
    RemoteManagerUnity.InitializeManager(clientInit);
}

PoseMode.Remote를 지정하면 그래픽 바인딩이 오프스크린 프록시 텍스처로 초기화되고 모든 렌더링이 Unity 장면의 주 카메라에서 프록시 카메라로 리디렉션됩니다. 이 코드 경로는 PoseMode.Remote에 대한 런타임 포즈 모드 변경이 필요한 경우에만 사용하는 것이 좋습니다. 포즈 모드가 지정되지 않은 경우 ARR Unity 런타임은 현재 플랫폼에 따라 적절한 기본값을 선택합니다.

Warning

프록시 카메라 리디렉션은 다른 Unity 확장과 호환되지 않을 수 있으며, 주 카메라에서 장면 렌더링이 수행될 것으로 예상됩니다. 프록시 카메라를 쿼리하거나 다른 곳에 등록해야 하는 경우 RemoteManagerUnity.ProxyCamera 속성을 통해 검색할 수 있습니다. 특히 Cinemachine 플러그 인의 경우 Unity Cinemachine 플러그 인이 원격 포즈 모드에서 작동하지 않음 문제 해결 항목을 참조하세요.

PoseMode.Local 또는 PoseMode.Passthrough를 대신 사용하는 경우 그래픽 바인딩은 오프스크린 프록시 텍스처로 초기화되지 않으며 Unity 장면의 주 카메라를 사용하여 렌더링하는 빠른 경로가 사용됩니다. 런타임에 각 사용 사례에 원격 포즈 모드가 필요한 경우 RemoteManagerUnity 초기화 시 PoseMode.Remote를 지정해야 합니다. Unity의 주 카메라로 직접 렌더링하는 것이 더 효율적이며 다른 Unity 확장과 관련된 문제를 방지할 수 있습니다. 따라서 프록시가 아닌 렌더링 경로를 사용하는 것이 좋습니다.

다음 단계