Buscar en Unity

Gráficos XR: mejores prácticas y soluciones

Last updated: January 2019

Lo que obtendrás de esta página: muchos consejos para optimizar tus shaders y cómo utilizar Post-Processing Stack de la forma más eficiente. Este es un buen artículo si eres un programador experimentado con Unity y estás o estarás desarrollando contenido XR (VR o AR).

En la actualidad, Unity es compatible con cuatro métodos de renderizado para XR: multi-pass, single-pass, el recientemente lanzado single-pass instancing y Android single-pass (que es similar a single-pass instancing).

Multi-pass
  • Requiere recorrer el gráfico de la escena dos veces: para cada una de las dos texturas que tienes que representan a los ojos, tienes que renderizars tus objetos/escenas por separado.
  • No compartes el trabajo de GPU entre las texturas, lo que hace que la ruta de renderizado sea la menos eficiente; aunque funciona en la mayoría de dispositivos de manera predeterminada por lo que requiere pocos cambios.
  • Sí comparte culling y parte del renderizado de generación de sombras.
Single-pass
  • Con este método, dos texturas se incluyen dentro de una textura más grande (a la que se hace referencia como textura doble ancho o double-wide texture).
  • Para el primer objeto que se dibujará, la ventana de observación se establece en el lado izquierdo, el objeto se dibuja; y a continuación, la ventana de observación pasa al lado derecho y el objeto vuelve a ser dibujado.
  • El gráfico de la escena se recorre solo una vez, por lo que es mucho más rápido en el CPU. Sin embargo, se requiere muchos cambios más de estado del GPU para lograrlo.

gráficos xr de unity

Un ejemplo sencillo del renderizado estéreo single-pass

Single-pass instancing
  • Disponible en 2017.2 y versiones superiores.
  • Las dos texturas que representan a los ojos se incluyen en un arreglo de texturas (dos slices del mismo arreglo de texturas).
  • Cuando se establece la ventana de observación, se aplica a ambos slices de manera automática. Cuando haces un dibujo, esta hace un dibujo de instancia que es dos veces más grande que el normal. Dependiendo de la instancia, Unity determina qué slice renderizará.
  • Implica la misma cantidad de trabajo de GPU, pero menos draw calls y trabajo para el CPU, por lo que es significativamente más eficiente.
  • Disponible para:
    • Windows 10 con D3D11 con controladores de gráficos recientes.
    • Hololens.
    • PS4.
  • Regresaremos a multi-pass si la extensión no es soportada en el dispositivo de destino.
  • DrawProceduralIndirect(...) requiere cambios manuales.

gráficos xr de unity

Un ejemplo de single-pass instancing

Añade estas macros a tus shaders si usas single-pass instancing

Si eliges single-pass instancing, te recomendamos encarecidamente que añadas las siguientes macros a tus shaders de vértices y fragmentos.

Para shaders de vértices:

gráficos xr de unity

Código de shader sin macros.

gráficos xr de unity

El mismo código con la primera macro añadida...

gráficos xr de unity

La segunda macro...

gráficos xr de unity

La tercera...

gráficos xr de unity

Y la cuarta macro añadida.

Si resulta que necesitas usar unity_StereoEyeIndex en el shader de fragmentos, esto es lo que debes hacer:

gráficos xr de unity

Algunas diferencias cuando se usa RenderScale versus RenderViewportScale

Digamos que quieres redimensionar una textura de 1 a 0,5. Si lo haces usando RenderScale, se borra la textura original, y se crea una nueva textura que tiene la cuarta parte del tamaño de la textura original, ya que es 0,5 en cada dimensión.

Esta es una operación costosa si se hace dinámicamente mientras tu juego está en ejecución, y esa es la razón por la que RenderViewportScale es una opción más eficiente. Establece la ventana de observación en una porción más pequeña de la misma textura y sencillamente renderiza así, como esto:

gráficos xr de unity

Esto es mucho más eficiente, pero presenta algunos problemas ya que solo estás usando una porción de la textura (consulta la sección a continuación donde encontrarás más consejos sobre RenderViewportScale).

Así, RenderScale verdaderamente borra y después crea texturas nuevas, mientras que RenderViewportScale modifica la ventana de observación. Algunas otras diferencias que hay que tener en cuenta son:

  • XRSettings.eyeTextureResolutionScale es para el tamaño de textura real mientras que XRSettings.renderViewportScale se aplica a la ventana de observación usada para el renderizado.
  • XRSettings.renderViewportScale no es compatible con el renderizado diferido.
  • La escala se aplica a ambas dimensiones, por lo que un valor de 0,5 dará lugar a una imagen que tiene 25 % del tamaño original.
La pila de posprocesamiento en XR: desafíos y soluciones

La versión más reciente de la excelente pila posprocesamiento está aquí. Usar la pila en contenido XR implica ciertos desafíos; la siguiente sección aborda estos desafíos, así como las soluciones clave que ahora están disponibles para ti.

Desafío

Para usar muchos de los efectos de posprocesamiento más recientes, tendrás que renderizar a un objetivo de renderizado intermedio que se base en el tamaño y propiedades del objetivo fuente:

  • En el caso de doble ancho o double-wide, la textura render tiene al menos dos veces el ancho de la porción de ojo individual.
  • Con single-pass instancing y el Android single-pass similar, necesitamos hacer un arreglo de textura, con un slice por ojo.

Solución

  • Para hacer menos conjeturas y reducir el tiempo dedicado a intentar generar el formato correcto de textura de espacio de pantalla, Unity presenta XRSettings.eyeTextureDesc (en 2017.2 y versiones superiores).
  • Esto devuelve un RenderTextureDescriptor que se obtuvo del gestor de XRTexture del motor, lo que significa que se configura para coincidir con las texturas gestionadas por el motor.
  • Puedes consultar también el RenderTextureDescriptor en una textura fuente si esta se encuentra disponible. Si estás usando la infraestructura heredada MonoBehaviour.OnRenderImage, por ejemplo, tendrás esa textura fuente y puedes echar mano del descriptor directamente desde esta.
  • Al usar eyeTextureDesc, puedes sustituir todas tus asignaciones RenderTexture por el RenderTextureDescriptor suministrado, lo cual resulta más eficiente que generar parámetros manualmente.
  • RenderTextureDescriptor es una APl más sencilla; se adapta a lo que hacen las API subyacentes. Si usas argumentos explícitos, Unity los empaqueta en un RenderTextureDescriptor, y los transfiere al núcleo del motor (en RenderTexture.GetTemporary o CommandBuffer.GetTemporaryRT). Así manejas esto en el lado del script en lugar de hacer que la capa intermedia se encargue de toda la gestión por ti.

Desafío

En XR, hay diferencia entre el tamaño de la textura del render físico y el tamaño de la pantalla lógica/ojo.

Solución

  • Para la asignación intermedia de la textura del render, vas a querer usar eyeTextureDesc ya que esto asignará estas texturas físicas.
  • Si tienes un script o una lógica de shader que se basa en el tamaño de la pantalla (es decir, si vas a usar los parámetros de tamaño de pantalla en un shader, o vas crear una pirámide de texturas), vas a querer basar esto en el tamaño lógico. Para eso, puedes usar XRSettings.eyeTextureHeight y XRSettings.eyeTextureWidth.
  • Estos representan los tamaños de textura "por ojo", que se requieren para saber el tamaño de la pantalla en la que estás trabajando.
  • Ten presente que en el caso del doble ancho, eyeTextureWidth no tiene exactamente el ancho de eyeTextureDesc. Esto se debe a que el descriptor, a los efectos de la asignación de texturas, duplica el ancho y entonces lo engrosa un poquito para asegurarse de que está configurado para el mip mapping.

Este es un ejemplo del código que has utilizado antes de que estuviese disponible eyeTextureWidth, para determinar qué ancho querías para el ancho de tu pantalla:

gráficos xr de unity

Ahora, simplemente puedes extraer el ancho de la pantalla de eyeTextureWidth, y usarlo para algo así como la relación de pantalla. Mira el ejemplo de script que te ofrecemos a continuación:

gráficos xr de unity

Desafío

¿De qué manera te aseguras de que tus coordenadas de texturas sean correctas para estéreo, y de que estás muestreando para enviarlas al ojo correcto?

Si estás muestreando a partir de una textura doble ancho, tendrás que asegurarte de que las coordenadas de textura se muestreen para la mitad correcta de la textura (cada mitad corresponde a un ojo).

Solución

Necesitarás un corrector de coordenadas de textura por ojo, y para esto, Unity te ofrece dos soluciones.

1. TRANSFORM_TEX macro

  • This is a macro that works with _ST properties in ShaderLab. It’s most commonly used with MainTex_ST.
  • La propiedad _ST se actualiza antes de cada ojo en el modo doble ancho single-pass, transformando tus coordenadas de textura para que tengan el rango correcto para el ojo. Se encarga automáticamente también de RenderViewportScale por ti.

gráficos xr de unity

  • Unity will populate _ST shader properties if you’re using Graphics.Blit or CommandBuffer.Blit:
    • MainTex_ST siempre está lleno.
    • Se llenarán también otras propiedades _ST si la textura es una textura XR (VRTextureUsage!=None).
  • Esta macro no funcionará con procedimientos de blit personalizados, como BlitFullScreenTriangle en la pila posprocesamiento más reciente.

2. Funciones de ayudante unity_StereoScaleOffset y UnityStereoTransformScreenSpaceTex/TransformStereoScreenSpaceTex

unity_StereoScaleOffset es un arreglo de float4s:

  • UnityStereoTransformScreenSpaceTex
  • TransformStereoScreenSpaceTex

Se declara como parte de un bloque de constantes single-pass (UnityStereoGlobals). Se indexa en esto con Unity_StereoEyeIndex.

En el caso de doble ancho, unity_StereoEyeIndex se ciñe a un búfer de constantes y después cada draw obtiene el ojo correcto actualizado. De esta manera, el valor del ojo izquierdo estará en un búfer de constantes, y el ojo izquierdo en draw: entonces el valor del ojo derecho se pondrá en el búfer de constantes y entonces el ojo derecho estará en draw. Las instancias del shader sabrán que para todas las draws que estén usando, pueden examinar el búfer de constantes y encontrar el valor correcto para el unity_StereoEyeIndex.

gráficos xr de unity

Una vez que sabes que unity_StereoEyeIndex se llenó correctamente, puedes usarlo con confianza ya que seleccionará al elemento correcto de unity_StereoScaleOffset.

gráficos xr de unity

Hay algunos inconvenientes cuando se usa unity_StereoScaleOffset:

  • Esta disponible únicamente para single-pass, por lo que tienes que usar los métodos del ayudante.
  • El uso directo lleva a multi-pass y monoscopic y esto dará lugar a errores de compilación de shades. Además, no toma en cuenta a RenderViewportScale.

¿Cómo te aseguras de que tus arreglos de texturas se declaren correctamente, y que estás muestreando a partir del slice correcto?

Solución

Usa UNITY_DECLARE_SCREENSPACE_TEXTURE. Los lenguajes de shader tratan los arreglos de texturas de manera diferente que las texturas "normales". Single-pass instancing y Android single-pass usan arreglos de texturas, por lo que necesitamos asegurarnos de declarar nuestras texturas correctamente.

gráficos xr de unity

De manera similar al doble ancho, necesitamos muestrear a partir de la porción de ojo correcta; estas porciones de ojo se colocan en los slices del conjunto de texturas. Podemos extraer el slice correcto de unity_StereoEyeIndex y mediante el uso de la macro UNITY_SAMPLE_SCREENSPACE_TEXTURE.

gráficos xr de unity

Cómo gestionar RenderViewportScale: algunos puntos que debes tener en cuenta

  • unity_StereoScaleOffset no ofrece soporte.
  • La mayoría de los efectos posprocesamiento emplean CommandBuffer.Blit/Graphics.Blit, lo que significa que pueden usar_MainTex_ST (y otros métodos _ST) para ser compatibles con RenderViewportScale.
  • Sin embargo, con la v.2 de la pila de posprocesamiento de Unity, no puedes usar estos métodos. Para solucionar este problema, tendrás que introducir tu propio soporte, lo que es bastante sencillo porque la v.2 de la pila usa su propia versión de infraestructura de shader, lo que significa que es fácil de anular. Esto es lo que tienes que hacer:

    En el lado del shader:

    • Crear una constante nueva: Declarar "float rvsGlobal" dentro de xrLib.hlsl
    • Reprocesar TransformStereoScreenSpaceTex para utilizar rvsGlobal

    En el lado del Script:

    • Ceñirse al valor RenderViewportScale como una propiedad global a partir del script
    • Usar Shader.SetGlobalFloat

    gráficos xr de unity

Fijación de estéreo

Si estás haciendo muestras de espacio de pantalla cercano en un shader, querrás asegurarte de no muestrear accidentalmente el ojo incorrecto en un doble ancho single-pass, o fuera del área RenderViewportScale válida. Aquí es donde interviene UnityStereoClamp: simplemente fija las muestras de coordenadas a la porción correcta de la textura.

gráficos xr de unity

El uso es muy sencillo, pero se requiere la inspección manual para encontrar muestreos cercanos.

Siempre que estés compensando desde la coordenada de textura generada por interpolación, probablemente debas usar Unity.StereoClamp().

  • Ten cuidado con los efectos del muestreo de las mismas coordenadas debido a la fijación.
  • No uses el shader de vértices.
Algunos otros consejos antes de comenzar:
  • Asegúrate de que el efecto de posprocesamiento que estás usando tenga sentido para XR.
    • Most temporal effects add blurriness and latency
    • Simulating depth of field, a property of physical cameras, in a headset, which is simulating the human eye, will most likely cause nausea.
  • Si estás usando multi-pass y mantienes texturas históricas, necesitas un conjunto de texturas históricas por ojo.
    • Singular history set could/will lead to sharing history between eyes!
    • Works automatically with single-pass
  • Si tienes problemas, prueba los diferentes modos de renderizado estéreo.
    • Nature of artifact can provide hints on where things might be going wrong.
Mas recursos

¡Debemos saberlo! ¿Te gustó este contenido?

Sí. Que sigan llegando Me da igual. Podría ser mejor
Lo tengo

Usamos cookies para brindarte la mejor experiencia en nuestro sitio web. Visita nuestra página de política de cookies si deseas más información.