Partilhar via


Reprojeção da última fase

A Reprojeção de Estágio Tardio (LSR) é um recurso de hardware que ajuda a estabilizar hologramas quando o usuário se move.

Espera-se que os modelos estáticos mantenham visualmente sua posição quando você se move em torno deles. Se eles parecerem instáveis, esse comportamento pode sugerir problemas de LSR. Lembre-se de que transformações dinâmicas extras, como animações ou exibições de explosão, podem mascarar esse comportamento.

Você pode escolher entre dois modos LSR diferentes, ou seja , Planar LSR ou Depth LSR. Ambos os modos LSR melhoram a estabilidade do holograma, embora tenham suas limitações distintas. Comece experimentando o Depth LSR, pois é indiscutivelmente dando melhores resultados na maioria dos casos.

Como definir o modo LSR

Qual dos modos LSR é usado é determinado pelo fato de o aplicativo cliente enviar um buffer de profundidade. Se o buffer de profundidade for enviado, ele usará LSR de profundidade e LSR planar caso contrário.

Os parágrafos a seguir explicam como o envio do buffer de profundidade é realizado em aplicativos Unity e nativos, respectivamente.

Unity

No editor Unity, vá para File > Build Settings. Selecione Player Settings no canto inferior esquerdo e, em seguida, verifique se Player > XR Settings > Virtual Reality SDKs > Windows Mixed RealityEnable Depth Buffer Sharing está marcado:

Sinalizador Compartilhamento de buffer de profundidade habilitado

Se for, seu aplicativo usará Depth LSR, caso contrário, usará Planar LSR.

Ao usar OpenXR, o buffer de profundidade deve sempre ser enviado. A configuração pode ser encontrada em XR Plug-in Management > OpenXR. O modo de reprojeção pode então ser alterado através de uma extensão no plugin 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);
            }
        }
    }
}

Aplicativos C++ nativos

O envio do buffer de profundidade está totalmente sob controle do código de vinculação C++ nativo, independente da versão WMR ou OpenXR. A única condição que precisa ser atendida é que, no momento em que GraphicsBinding::BlitRemoteFrame é chamado, um buffer de profundidade deve ser vinculado à API gráfica.

Profundidade LSR

Para que o LSR de profundidade funcione, o aplicativo cliente deve fornecer um buffer de profundidade válido que contenha toda a geometria relevante a ser considerada durante o LSR.

Profundidade LSR tenta estabilizar o quadro de vídeo com base no conteúdo do buffer de profundidade fornecido. Como consequência, o conteúdo que não foi renderizado para ele, como objetos transparentes, não pode ser ajustado pelo LSR e pode mostrar artefatos de instabilidade e reprojeção.

Para reduzir a instabilidade da reprojeção de objetos transparentes, você pode forçar a gravação do buffer de profundidade. Consulte o sinalizador de material TransparencyWritesDepth para os materiais Color e PBR . No entanto, observe que a qualidade visual da interação de objetos transparentes/opacos pode sofrer ao ativar esse sinalizador.

Planar LSR

O LSR Planar não tem informações de profundidade por pixel, como o LSR de profundidade. Em vez disso, ele reprojeta todo o conteúdo com base em um plano que você deve fornecer a cada quadro.

Planar LSR reprojeta melhor os objetos que se encontram perto do plano fornecido. Quanto mais longe estiver um objeto, mais instável ele parecerá. Enquanto o LSR de profundidade é melhor em reprojetar objetos em diferentes profundidades, o LSR Planar pode funcionar melhor para o conteúdo se alinhar bem com um plano.

Configurar o Planar LSR no Unity

Os parâmetros do plano são derivados de um chamado ponto de foco. Ao usar WMR, o ponto de foco deve ser definido a cada quadro através do UnityEngine.XR.WSA.HolographicSettings.SetFocusPointForFrame. Consulte a API do Unity Focus Point para obter detalhes. Para OpenXR, o ponto de foco precisa ser definido através do ReprojectionSettings mostrado na seção anterior. Se você não definir um ponto de foco, um fallback será escolhido para você. No entanto, esse fallback automático muitas vezes leva a resultados abaixo do ideal.

Você mesmo pode calcular o ponto de foco, embora possa fazer sentido baseá-lo no calculado pelo host de Renderização Remota. Ligue RemoteManagerUnity.CurrentSession.GraphicsBinding.GetRemoteFocusPoint para obter isso.

Normalmente, tanto o cliente quanto o host processam conteúdo que o outro lado não conhece, como elementos da interface do usuário no cliente. Portanto, pode fazer sentido combinar o ponto de foco remoto com um calculado localmente.

Os pontos de foco calculados em dois quadros sucessivos podem variar muito. Simplesmente usá-los como está pode levar a hologramas que parecem estar pulando por aí. Para evitar esse comportamento, é aconselhável interpolar entre os pontos de foco anteriores e atuais.

Modos de pose de reprojeção

O escopo geral do problema com a renderização híbrida pode ser afirmado assim: O conteúdo remoto e local está dentro de poses distintas (ou seja, espaços de coordenadas) porque a pose remota é prevista pelo servidor, enquanto a pose local é a atual real. No entanto, no final de um quadro de renderização, tanto o conteúdo remoto quanto o local precisam ser alinhados e trazidos para a exibição. A ilustração a seguir mostra um exemplo em que poses locais e remotas são traduzidas em comparação com o visor de exibição:

Diagrama que ilustra a pose remota e local em relação ao visor de destino.

Dependendo do usado, o GraphicsBinding ARR fornece até três modos de reprojeção que funcionam ortogonalmente para o modo LSR discutido acima. Esses modos são referidos como Remote pose mode, Local pose modee Passthrough pose mode. Ao contrário do modo LSR, os modos de pose definem como o conteúdo remoto e local é combinado. A escolha do modo troca a qualidade visual do conteúdo local pelo desempenho em tempo de execução, portanto, os aplicativos devem considerar cuidadosamente qual opção é apropriada. Veja as considerações abaixo.

Remote pose mode

Remote pose mode é o modo padrão no ARR. Nesse modo, o conteúdo local é renderizado sobre o fluxo de imagem remoto de entrada usando a pose remota do quadro remoto. Em seguida, o resultado combinado é encaminhado para o SO para a reprojeção final. Embora essa abordagem use apenas uma reprojeção, a correção final é baseada no intervalo de ida e volta para que o erro de reprojeção total também seja aplicado ao conteúdo local. Como consequência, o grande delta de correção pode resultar em distorções significativas da geometria local, incluindo elementos da interface do usuário.

Usando a ilustração acima, a seguinte transformação é aplicada em Remote pose mode:

Etapas de reprojeção no modo de pose remota.

Local pose mode

Neste modo, a reprojeção é dividida em duas etapas distintas: Na primeira etapa, o conteúdo remoto é reprojetado no espaço de pose local, ou seja, o espaço com o qual o conteúdo local é renderizado em dispositivos VR/AR por padrão. Depois disso, o conteúdo local é renderizado em cima dessa imagem pré-transformada usando a pose local usual. Na segunda etapa, o resultado combinado é encaminhado ao SO para a reprojeção final. Como esta segunda reprojeção incorre apenas em um pequeno delta - na verdade, o mesmo delta que seria usado se o ARR não estivesse presente - os artefatos de distorção no conteúdo local são mitigados significativamente.

Assim, a ilustração tem esta aparência:

Etapas de reprojeção no modo de pose local.

Passthrough pose mode

Este modo de pose comporta-se essencialmente da mesma forma que Remote pose mode, o que significa que o conteúdo local e remoto são combinados no espaço remoto. No entanto, o conteúdo não será reprojetado após a combinação, mas permanecerá no espaço de pose remoto. A principal vantagem desse modo é que a imagem resultante não será afetada por artefatos de reprojeção.

Conceitualmente, esse modo pode ser comparado a aplicativos convencionais de streaming em nuvem. Devido à alta latência que incorre, ele não é adequado para cenários montados na cabeça, mas é uma alternativa viável para Desktop e outros aplicativos de tela plana onde uma maior qualidade de imagem é desejada. Portanto, só está disponível GraphicsBindingSimD3D11 por enquanto.

Considerações sobre desempenho e qualidade

A escolha do modo de pose tem implicações de qualidade visual e desempenho. O custo de tempo de execução extra no lado do cliente para fazer a reprojeção extra em Local pose mode um dispositivo HoloLens 2 equivale a cerca de 1 milissegundo por quadro de tempo de GPU. Esse custo extra precisa ser levado em consideração se o aplicativo cliente já estiver próximo do orçamento de quadro de 16 milissegundos. Por outro lado, existem tipos de aplicativos sem conteúdo local ou conteúdo local que não são propensos a artefatos de distorção. Nesses casos Local pose mode , não ganha nenhum benefício visual porque a qualidade da reprojeção de conteúdo remoto não é afetada.

O conselho geral seria, portanto, testar os modos por caso de uso e ver se o ganho de qualidade visual justifica a sobrecarga de desempenho extra. Também é possível alternar o modo dinamicamente, por exemplo, ativar o modo local apenas quando interfaces de usuário importantes são mostradas.

Como alterar o Pose mode tempo de execução at

A seguinte API de cliente pode ser usada para alterar o modo em tempo de execução:

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

Em geral, o modo pode ser alterado sempre que o objeto de vinculação de gráficos estiver disponível. Há uma distinção importante para GraphicsBindingSimD3D11: o modo de pose só pode ser alterado para PoseMode.Remote, se tiver sido inicializado com texturas proxy. Se esse não for o caso, o modo de pose só poderá ser alternado entre PoseMode.Local e PoseMode.Passthrough até que a ligação gráfica seja reinicializada. Veja as duas sobrecargas do , que levam ponteiros nativos para objetos ID3D11Texture2D (caminho de GraphicsBindingSimD3d11.InitSimulationproxy) ou o width e height do visor de visualização do usuário desejado (caminho não proxy).

Considerações sobre o tempo de execução do Desktop Unity

Devido ao histórico técnico e ao fato de como a renderização fora da tela funciona no Unity, o tempo de GraphicsBindingSimD3D11 execução do ARR Unity requer que o usuário especifique o modo de pose desejado na inicialização da RemoteManagerUnity seguinte maneira:

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

Se PoseMode.Remote for especificado, a ligação gráfica será inicializada com texturas de proxy fora da tela, e toda a renderização será redirecionada da câmera principal da cena Unity para uma câmera proxy. Esse caminho de código só é recomendado para uso se forem necessárias alterações no modo de pose de tempo de PoseMode.Remote execução. Se nenhum modo de pose for especificado, o tempo de execução do ARR Unity selecionará um padrão apropriado dependendo da plataforma atual.

Aviso

O redirecionamento da câmera proxy pode ser incompatível com outras extensões Unity, que esperam que a renderização de cena ocorra com a câmera principal. A câmera proxy pode ser recuperada através da RemoteManagerUnity.ProxyCamera propriedade, se ele precisa ser consultado ou registrado em outro lugar. Especificamente para o Cinemachine plugin, consulte esta entrada de solução de problemas: O plug-in Unity Cinemachine não funciona no modo de pose remota.

Se PoseMode.Local ou PoseMode.Passthrough for usado em vez disso, a ligação gráfica não será inicializada com texturas de proxy fora da tela, e um caminho rápido usando a câmera principal da cena Unity para renderizar será usado. Se o respetivo caso de uso exigir o modo de pose remota em tempo de execução, PoseMode.Remote deve ser especificado na RemoteManagerUnity inicialização. A renderização direta com a câmera principal do Unity é mais eficiente e pode evitar problemas com outras extensões do Unity. Portanto, é recomendável usar o caminho de renderização sem proxy.

Próximos passos