View in English

  • Global Nav Open Menu Global Nav Close Menu
  • Apple Developer
Search
Cancel
  • Apple Developer
  • News
  • Discover
  • Design
  • Develop
  • Distribute
  • Support
  • Account
Only search within “”

Quick Links

5 Quick Links

Videos

Abrir menú Cerrar menú
  • Colecciones
  • Temas
  • Todos los videos
  • Información

Más videos

  • Información
  • Resumen
  • Transcripción
  • Código
  • Novedades del renderizado de Metal para apps inmersivas

    Descubre las últimas mejoras en el renderizado de Metal para apps inmersivas con Compositor Services. Aprende a agregar efectos de desplazamiento para resaltar los elementos interactivos de tu app y cómo renderizar con mayor fidelidad y calidad de renderizado dinámico. Descubre el nuevo estilo de inmersión progresiva. Además, descubre cómo puedes llevar experiencias inmersivas a las apps de macOS renderizando directamente el contenido de Metal desde Mac a Vision Pro. Para aprovechar al máximo esta sesión, primero ve “Discover Metal for immersive apps" del WWDC23.

    Capítulos

    • 0:00 - Introducción
    • 1:58 - Nuevas API de bucle de renderizado
    • 4:21 - Efectos de desplazamiento
    • 10:50 - Calidad de renderización dinámica
    • 14:44 - Inmersión progresiva
    • 18:32 - Renderizado espacial de macOS
    • 23:51 - Próximos pasos

    Recursos

    • Analyzing the performance of your Metal app
    • Optimizing GPU performance
    • Rendering hover effects in Metal immersive apps
      • Video HD
      • Video SD

    Videos relacionados

    WWDC25

    • Descubrir Metal 4
    • Explora la entrada de accesorios espaciales en visionOS
    • Explorar juegos de Metal 4
    • Novedades de SwiftUI
    • Novedades de visionOS 26
    • Ve más allá con los juegos de Metal 4

    WWDC24

    • Render Metal with passthrough in visionOS

    WWDC23

    • Discover Metal for immersive apps
  • Buscar este video…

    Hola, soy Ricardo, ingeniero de software de Apple. Hoy voy a mostrarte las nuevas funcionalidades que puedes adoptar al usar Metal para renderizar contenido inmersivo en Apple Vision Pro. El año pasado mostramos cómo aprovechar Metal, Compositor Services y ARKit para crear experiencias inmersivas renderizando tu contenido directamente en visionOS.

    Puedes implementar una experiencia completamente inmersiva, como en Demeo de Resolution Games, o adoptar el estilo de inmersión mixta para mostrar tu contenido junto con el mundo real.

    Ahora, gracias a los valiosos comentarios de desarrolladores como tú, el renderizado con Metal en visionOS admite nuevas funcionalidades emocionantes.

    Podrás añadir detalles aún más ricos y efectos interactivos a tus apps y juegos. En este video te explicaré todo sobre las nuevas funcionalidades de Compositor Services.

    Para aprovecharlas al máximo, debes estar familiarizado con la estructura Compositor Services y con las técnicas de renderizado en Metal. Si aún no has usado estas tecnologías, puedes conocerlas en los videos anteriores.

    Para adoptar las nuevas funcionalidades, primero tendrás que hacer cambios en tu ciclo de renderizado. Te explicaré cómo adoptar las nuevas API que hacen que la canalización sea más flexible. Una vez que hayas hecho eso, podrás agregar efectos de desplazamiento para resaltar los elementos interactivos de tu app. También podrás ajustar dinámicamente la resolución del contenido renderizado. Hay un nuevo estilo de inmersión progresiva que permite a las personas ajustar la inmersión con la Digital Crown. Y puedes usar tu Mac para renderizar contenido inmersivo directamente en Vision Pro. Empieza con API de bucle de renderizado. Empecemos.

    Una app inmersiva de Metal empieza en SwiftUI, donde se crea un espacio inmersivo que tiene una capa de compositor.

    La capa brinda un objeto de renderizado de capa para usar en su bucle de renderizado. Consulta fotogramas desde el renderizador de capas. De cada fotograma, obtienes elementos dibujables, que contienen texturas que usas para renderizar tu contenido. Si ya creaste una app inmersiva de Metal, estás consultando un único elemento dibujable para cada fotograma renderizado. Este año, Compositor Services incorpora la nueva función Query drawables que, devuelve un arreglo de elementos dibujables. Según el contexto del sistema, el arreglo contendrá uno o dos elementos dibujables. La mayoría de las veces obtendrás un solo elemento dibujable. Sin embargo, cuando grabes un video de alta calidad con Reality Composer Pro, un variograma devolverá dos elementos dibujables. Puedes identificar los elementos dibujables con la nueva propiedad target. El destinado a la pantalla de Vision Pro tiene el valor .builtIn y el de la grabación el valor .capture. Para saber cómo capturar videos de alta calidad, consulta la documentación para desarrolladores.

    Capaz la función de renderización de variogramas te sea familiar. Después de solicitar el siguiente variograma y actualizar el estado de la escena, llamo la función query drawable. Espero a entrar y renderizo la escena en el dibujo. Y renderizo la escena en el elemento dibujable.

    Ahora, en su lugar, sustituyo query drawable por query drawables. Me aseguro de que el arreglo no esté vacío y renderizo mi escena en todos los elementos dibujables.

    Xcode incluye una plantilla muy práctica para crear una app inmersiva con Metal. Es un gran punto de partida. Para verla, inicia un proyecto nuevo de Xcode para una app de visionOS y elige Metal 4 en el menú emergente Immersive Space Renderer.

    Ten en cuenta que Metal 3 aún es compatible este año y puedes usarlo en la plantilla seleccionando la opción “Metal”.

    Para saber más sobre cómo adoptar la última versión de Metal, consulta “Discover Metal 4”. Una vez que hayas adoptado la nueva función query drawables, podrás usar todas las funciones nuevas de este año, como añadir efectos de desplazamiento a los objetos interactivos de tu escena.

    Con ellos, la persona que usa tu app puede ver qué objetos son interactivos y anticipar los objetivos de sus acciones. El sistema destacará dinámicamente el objeto al que el usuario esté mirando. Por ejemplo, un juego de rompecabezas puede enfatizar las piezas que el jugador puede seleccionar.

    Imagina una app que renderiza una escena con varios objetos 3D, pero solo algunos son interactivos. Quiero asegurarme de que los efectos de desplazamiento solo se apliquen a los objetos interactivos. Cuando un objeto no es interactivo, no se rastrea y no tiene efecto de desplazamiento, así que lo renderizo normalmente. Por otro lado, si un objeto es interactivo, añado una nueva área de seguimiento al elemento dibujable. Debo asignar un identificador de objeto único a cada área de seguimiento que registre.

    Luego compruebo si el objeto debe tener un efecto de desplazamiento. Si no lo necesita, lo dibujo con el valor renderizador del área de seguimiento, sirve para detectar pellizcos en objetos. Si el objeto sí tiene un efecto de desplazamiento, lo configuro en el área de seguimiento antes de renderizar.

    En código, tengo que configurar mi renderizador de capas para usar efectos de desplazamiento. Establezco la textura de áreas de seguimiento con un formato de píxel de 8 bits, que admite hasta 255 objetos interactivos simultáneos. Verifico que las funcionalidades de la capa admitan el formato y lo indico en la configuración.

    En el código de registro de áreas de seguimiento, compruebo si el objeto es interactivo y, de ser así, registro una nueva área de seguimiento en el elemento dibujable con mi identificador de objeto. Asegúrate de conservar identificadores únicos durante todo el ciclo de vida de tus objetos. Luego verifico si el objeto tiene efecto de desplazamiento y, si lo tiene, lo añado con el atributo .automatic. Esto indica que el sistema agregará automáticamente el efecto cuando el usuario mire el objeto. Por último, lo renderizo.

    Con Compositor Services, el elemento dibujable brinda varias texturas que la app puede usar para renderizar contenido.

    Seguramente ya conozcas la textura de color, que es lo que el usuario ve. También existe la textura de profundidad, donde los colores más oscuros indican objetos más alejados del usuario; el sistema la usa para que el contenido mostrado sea más preciso a medida que el usuario se mueve por la escena. Este año se añade además la textura de áreas de seguimiento, que define las distintas regiones interactivas de escena. Un elemento dibujable te ofrece las texturas de color y profundidad; ahora también puedes solicitar la nueva textura de áreas de seguimiento. En ella dibujas zonas diferenciadas que corresponden a tus objetos interactivos. Con los efectos de desplazamiento, cuando alguien mira un objeto interactivo en tu escena, el sistema usa la textura de áreas de seguimiento para localizar la región correspondiente y aplica el efecto de desplazamiento en la parte coincidente de tu textura de color. Aquí vuelve a aparecer la función de renderizado de objetos con mi área de seguimiento configurada. Para renderizar la textura de áreas de seguimiento necesito un valor de renderizado calculado por sistema. Declaro una variable local para almacenarlo y lo obtengo de la área de seguimiento correspondiente. Si mi objeto no es interactivo, puedo usar el valor de renderizado nil predeterminado. Por último, lo paso a mi función draw, donde lo enviaré a la función de sombreador de fragmentos.

    Veamos el código del sombreador.

    La salida del sombreador de fragmentos incluye un valor de renderizado de área de seguimiento, asignado al color attachment en el índice 1. Ahí configuré mi textura de áreas de seguimiento. Vinculé el valor de renderizado a mi estructura de Uniforms y lo devuelvo en la salida del sombreador junto con el valor de color. Ahora mi app tiene efectos de desplazamiento interactivos. Hay un detalle más que debes tener en cuenta si usas antialiasing de muestreo múltiple o MSAA.

    Esta técnica funciona renderizando una textura intermedia de mayor resolución y luego promediando los valores de color dentro de una ventana de muestreo. Normalmente se hace mediante la opción multisample resolve en la acción de almacenamiento de la textura destino. No puedes resolver la textura de áreas de seguimiento de la misma forma que resuelves un píxel de color. Al hacerlo, promediarías los valores de renderizado y obtendrías un escalar inválido que no corresponde a ninguna área de seguimiento. Si resuelves en multisample el color, debes implementar un tile resolver personalizado para la textura de áreas de seguimiento. Puedes hacerlo usando la opción don’t care en el almacenamiento junto con una tile render pipeline personalizada. Una estrategia eficaz es elegir el valor de renderizado que aparece con más frecuencia en la ventana de muestreo de la textura MSAA de origen. Para un análisis detallado de los efectos de desplazamiento, incluido su uso con MSAA, consulta “Rendering hover effects in Metal immersive apps” en la documentación para desarrolladores.

    Las áreas de seguimiento permiten que tu app gestione las interacciones con los objetos más fácil que antes. Los eventos espaciales tienen un id. de área de seguimiento que puede ser nulo. Utilizo ese identificador para ver si coincide con alguno de los objetos de mi escena. Si encuentro un objeto destino, puedo ejecutar una acción sobre él.

    He mejorado la interactividad de mi app con los efectos de desplazamiento. La persona que la usa puede ver claramente qué objetos son accionables y cuál se activará cuando haga un pellizco. Esto facilita más que nunca el manejo de eventos de entrada. Ahora también puedes dibujar tu contenido con una fidelidad aún mayor que antes. Con la calidad de renderizado dinámica ajustas la resolución de tu contenido según la complejidad de tus escenas.

    Primero recordemos cómo funciona el foveated rendering. En una textura estándar sin foveación, los píxeles se distribuyen de manera uniforme. Con la foveación, el sistema te ayuda a dibujar en una textura cuyo centro tiene mayor densidad de píxeles. Así, tu app emplea sus recursos de cómputo y energía para mejorar la parte que probablemente esté mirando el usuario. Este año puedes aprovechar la calidad dinámica para la foveación. y controlar la calidad de los variogramas que renderiza tu app Primero, especifica la calidad de renderizado máxima adecuada para tu app, eso establece el límite superior de la sesión de renderizado. Luego, ajusta la calidad en tiempo de ejecución dentro del rango elegido según el tipo de contenido que muestres.

    Al aumentar la calidad de renderizado, el área de alta relevancia de tu textura se expande y el tamaño total de la textura crece. Ten en cuenta que incrementar la calidad implica que tu app usará más memoria y energía. Si renderizas texto o elementos de interfaz, te conviene fijar una calidad más alta; pero, si presentas una escena 3D compleja, quizá te limiten los recursos de cómputo. Para que tu app funcione sin problemas, debes equilibrar la calidad visual y el consumo de energía.

    Puedes usar Instruments para analizar el rendimiento en tiempo real y el depurador de Metal para profundizar y optimizar tu código y sombreadors de Metal. Recuerda perfilar tu app con las escenas más complejas para asegurarte de que dispone de tiempo suficiente para renderizar sus variogramas a un ritmo constante.

    Consulta el documento para desarrolladores y obtén información de cómo optimizar tus apps de renderizado con Metal.

    En este ejemplo de código he perfilado mi app y he decidido renderizar el menú con calidad 0.8 para que el texto se vea más nítido. Quiero renderizar el mundo con calidad 0.6 porque es una escena compleja. También añadí una propiedad calculada con la calidad máxima de renderizado que voy a usar. Esta es la configuración de mi capa. La calidad de renderizado dinámica solo puede usarse con foveación, así que compruebo si está activada y luego establezco la calidad máxima en el valor de mi propiedad calculada. Recuerda fijarla en el valor mínimo que tenga sentido para tu contenido. Si no lo haces, tu app usará más memoria de la necesaria.

    Cuando cargo una nueva escena, llamo a mi función para ajustar la calidad de renderizado. Solo es posible ajustar esa calidad si la foveación está activada. Según el tipo de escena, cambio la calidad de renderizado en consecuencia.

    Pasar de un valor de calidad a otro lleva algo de tiempo; no es inmediato. El sistema realiza la transición de forma fluida.

    Con la calidad de renderizado dinámica, tus escenas muy detalladas lucirán muy bien. La mayor resolución de las escenas renderizadas mejora la nitidez de los detalles finos. Pero recuerda que quizá debas reducir la calidad en escenas muy complejas. Puedes ajustar la calidad de renderizado de tu app según tu contenido.

    Novedad de este año: tu app Metal puede renderizarse dentro de un portal inmersivo progresivo. Con el estilo de inmersión progresiva, las personas que usan tu app controlan el nivel de inmersión girando la Digital Crown. Este modo las mantiene ancladas al entorno real y puede ayudarlas a sentirse más cómodas cuando ven escenas complejas con movimiento. Al ver una app Metal en modo inmersión progresiva, el sistema solo renderiza el contenido dentro del nivel de inmersión actual.

    A continuación se muestra una escena de un juego renderizada a inmersión completa. Y la misma escena a inmersión parcial después de que el usuario ajuste la Digital Crown. Compara las dos escenas y observa cómo se ahorra potencia de cómputo al no renderizar la zona resaltada fuera del portal. Esa parte no es visible, por lo que no es necesario renderizarla. Las nuevas API permiten usar una plantilla de portal calculada por el sistema para enmascarar tu contenido. Este óvalo blanco muestra dicha plantilla. El búfer de plantilla actúa como máscara sobre tu escena renderizada. Así, solo se dibuja el contenido dentro del portal. Como ves, la escena aún no tiene un borde suave. El difuminado lo aplica el sistema como paso final en tu búfer de comandos, y produce la imagen que ve el usuario.

    Para usar la plantilla y evitar renderizar contenido innecesario, primero configura tu capa de compositor. Asegúrate de que el formato de plantilla deseado esté admitido por las funcionalidades de la capa y establécelo en la configuración. Para aplicar la máscara, añade un contexto de renderizado al elemento dibujable con tu búfer de comandos. Dibujar la máscara en la plantilla evita procesar píxeles invisibles. También debes finalizar la codificación mediante tu contexto de renderizado, y no directamente en el codificador de comandos. De este modo, el efecto de portal se aplica con eficiencia. En mi app, creo un Immersive Space en SwiftUI y añado el estilo de inmersión progresiva como nueva opción a mi lista. La persona que usa la app puede alternar entre los estilos progresivo y completo. A continuación configuro la capa. Recuerda que el estilo progresivo solo funciona con el diseño por capas. Especifico un formato de plantilla de 8 bits por píxel. Compruebo que las funcionalidades lo admiten y lo establezco en la configuración. También fijo el número de muestras en 1 porque no uso MSAA. Si lo usas, ajusta ese valor al recuento de muestreo de MSAA.

    En mi renderizador, añado un contexto de renderizado al elemento dibujable y paso el mismo búfer de comandos que usaré para mis órdenes de renderizado. Dibujo mi máscara en la plantilla. Elijo un valor de plantilla que no utilizo en otras operaciones de plantilla y lo fijo como referencia en el codificador de renderizado. Así, el renderizador no dibuja el área fuera del nivel de inmersión actual. Tras renderizar la escena, observa que finalizo la codificación en el contexto de renderizado del elemento dibujable.

    Para ver un ejemplo funcional de un renderizador con estilo de inmersión progresiva, elige la opción progressive en la plantilla de app Metal para visionOS. eso te servirá para comenzar a crear una app Metal al estilo portal.

    Finalmente, exploremos el renderizado espacial en macOS.

    Hasta ahora he hablado de crear experiencias inmersivas nativas en Vision Pro. Este año, puedes usar la potencia de tu Mac para renderizar y transmitir contenido inmersivo directamente a Vision Pro. Esto permite añadir experiencias inmersivas a apps de Mac ya existentes.

    Por ejemplo, una app de modelado 3D puede previsualizar sus escenas directamente en Vision Pro. O también puedes crear una app inmersiva de macOS desde cero. Así, puedes diseñar experiencias inmersivas complejas que exijan gran capacidad de cómputo sin estar limitado por el consumo energético de Vision Pro. Iniciar una sesión inmersiva remota desde una app de Mac es muy sencillo. Cuando abras un Immersive Space en macOS, Vision Pro mostrará un aviso para aceptar la conexión.

    Si lo aceptas, comenzarás a ver tu contenido inmersivo renderizado en el Mac.

    Una app típica de Mac se construye con SwiftUI o AppKit. Puedes cualquiera de estas estructuras para crear y mostrar ventanas que el sistema renderiza con Core Animation. Puedes adoptar diversas estructuras de macOS para implementar la funcionalidad de tu app, y el sistema muestra tu contenido en la pantalla del Mac. Para crear una experiencia inmersiva compatible con Mac, emplearás las mismas estructuras que se usan para las apps inmersivas de visionOS. Primero, utiliza SwiftUI con el nuevo tipo de escena Remote Immersive Space. Después, adopta la estructura Compositor Services. Con ARKit y Metal colocas y renderizas tu contenido, y el sistema muestra directamente tu escena inmersiva en Vision Pro. El Remote Immersive Space de macOS aloja la capa de compositor y la sesión de ARKit como una app nativa de visionOS, y se conecta sin problemas a la pantalla y los sensores de Vision Pro. Para vincular tu sesión de ARKit a visionOS, existe un nuevo objeto de entorno de SwiftUI llamado Remote Device Identifier que se pasa al inicializador de la sesión. Así se organiza una app inmersiva de Mac:

    defino un Remote Immersive Space que contiene mi contenido de Compositor. Te mostraré como uso la capa de Compositor en un momento. En Mac solo se admiten los estilos de inmersión progresivo y completo. En la interfaz de mi app de Mac utilizo la nueva variable de entorno supportsRemoteScenes para comprobar si el Mac ofrece esta funcionalidad. Puedo personalizar la interfaz para mostrar un mensaje si las escenas remotas no se admiten. Si se admiten y aún no se ha abierto el Immersive Space, puedo lanzarlo. La última parte de mi app es el contenido del Compositor. Contiene la capa de Compositor y la sesión de ARKit. Creo y uso una capa de Compositor de la misma manera que lo hice en visionOS. Accedo al objeto de entorno Remote Device Identifier y lo paso al inicializador de la sesión de ARKit. Esto conectará la sesión ARKit de mi Mac a Vision Pro. Después inicio mi ciclo de renderizado, como en cualquier app inmersiva de Metal.

    ARKit y el proveedor de seguimiento del entorno están ahora disponibles en macOS, lo que te permite consultar la ubicación de Vision Pro en el espacio. Igual que en una app inmersiva nativa, utilizas la pose del dispositivo para actualizar la escena y los elementos dibujables antes de renderizar. Una app espacial de macOS admite cualquier dispositivo de entrada conectado al Mac. Puede utilizar controles de teclado y mouse. O puedes conectar un gamepad y manejar su entrada usando la estructura Game Controller. Además, puedes recibir eventos de pellizco en los elementos interactivos de tu escena mediante el modificador onSpatialEvent del renderizador de capas.

    Como novedad este año, también puedes crear escenas SwiftUI desde una app AppKit o UIKit existente. Esta es una excelente manera de agregar nuevas experiencias inmersivas a las apps Mac existentes. Aprende a hacerlo en “What’s new in SwiftUI”.

    Es común que los motores de renderizado se implementen en C o C++. Todas las API que he explicado tienen equivalentes nativos en C. Los tipos C de la estructura Compositor Services comienzan con el prefijo cp y siguen convenciones similares a bibliotecas, como Core Foundation. En ARKit, la propiedad cDevice ofrece un identificador de dispositivo remoto compatible con C; puedes pasarlo a tu biblioteca C e inicializar la sesión de ARKit con la función create with device. Ahora dispones de todos los elementos para usar tu Mac y potenciar contenido inmersivo en Vision Pro.

    Estoy deseando ver cómo aprovechas estas nuevas funcionalidades para mejorar tus apps inmersivas: permiten mayor interactividad, más fidelidad y el nuevo estilo de inmersión progresiva. Y me entusiasma lo que harás con las funcionalidades espaciales de macOS. Para saber cómo llevar tus apps inmersivas al siguiente nivel, consulta “Set the scene with SwiftUI in visionOS”. Y para tener un panorama general de las mejoras de la plataforma, revisa “What’s new in visionOS”. ¡Gracias por tu atención!

    • 0:01 - Scene render loop

      // Scene render loop
      
      extension Renderer {
          func renderFrame(with scene: MyScene) {
              guard let frame = layerRenderer.queryNextFrame() else { return }
      
              frame.startUpdate()
              scene.performFrameIndependentUpdates()
              frame.endUpdate()
      
              let drawables = frame.queryDrawables()
              guard !drawables.isEmpty else { return }
      
              guard let timing = frame.predictTiming() else { return }
              LayerRenderer.Clock().wait(until: timing.optimalInputTime)
              frame.startSubmission()
              scene.render(to: drawable)
              frame.endSubmission()
          }
      }
    • 5:54 - Layer configuration

      // Layer configuration
      
      struct MyConfiguration: CompositorLayerConfiguration {
          func makeConfiguration(capabilities: LayerRenderer.Capabilities,
                                 configuration: inout LayerRenderer.Configuration) {
              // Configure other aspects of LayerRenderer
      
              let trackingAreasFormat: MTLPixelFormat = .r8Uint
              if capabilities.supportedTrackingAreasFormats.contains(trackingAreasFormat) {
                  configuration.trackingAreasFormat = trackingAreasFormat
              }
          }
      }
    • 7:54 - Object render function

      // Object render function
      
      extension MyObject {
          func render(drawable: Drawable, renderEncoder: MTLRenderCommandEncoder) {
              var renderValue: LayerRenderer.Drawable.TrackingArea.RenderValue? = nil
              if self.isInteractive {
                  let trackingArea = drawable.addTrackingArea(identifier: self.identifier)
                  if self.usesHoverEffect {
                      trackingArea.addHoverEffect(.automatic)
                  }
                  renderValue = trackingArea.renderValue
              }
      		self.draw(with: commandEncoder, trackingAreaRenderValue: renderValue)
          }
      }
    • 8:26 - Metal fragment shader

      // Metal fragment shader
      
      struct FragmentOut
      {
          float4 color [[color(0)]];
          uint16_t trackingAreaRenderValue [[color(1)]];
      };
      
      fragment FragmentOut fragmentShader( /* ... */ )
      {
          // ...
      
          return FragmentOut {
              float4(outColor, 1.0),
              uniforms.trackingAreaRenderValue
          };
      }
    • 10:09 - Event processing

      // Event processing
      
      extension Renderer {
          func processEvent(_ event: SpatialEventCollection.Event) {
             let object = scene.objects.first {
                 $0.identifier == event.trackingAreaIdentifier
             }
             if let object {
                 object.performAction()
             }
         }
      }
    • 13:08 - Quality constants

      // Quality constants
      
      extension MyScene {
          struct Constants {
              static let menuRenderQuality: LayerRenderer.RenderQuality = .init(0.8)
              static let worldRenderQuality: LayerRenderer.RenderQuality = .init(0.6)
              static var maxRenderQuality: LayerRenderer.RenderQuality { menuRenderQuality }
          }
      }
    • 13:32 - Layer configuration

      // Layer configuration
      
      struct MyConfiguration: CompositorLayerConfiguration {
          func makeConfiguration(capabilities: LayerRenderer.Capabilities,
                                 configuration: inout LayerRenderer.Configuration) {
             // Configure other aspects of LayerRenderer
      
             if configuration.isFoveationEnabled {
                 configuration.maxRenderQuality = MyScene.Constants.maxRenderQuality
             }
      }
    • 13:57 - Set runtime render quality

      // Set runtime render quality
      
      extension MyScene {
          var renderQuality: LayerRenderer.RenderQuality {
              switch type {
              case .world: Constants.worldRenderQuality
              case .menu: Constants.menuRenderQuality
              }
          }
      }
      
      extension Renderer {
          func adjustRenderQuality(for scene: MyScene) {
              guard layerRenderer.configuration.isFoveationEnabled else {
                  return;
              }
              layerRenderer.renderQuality = scene.renderQuality
          }
      }
    • 16:58 - SwiftUI immersion style

      // SwiftUI immersion style
      
      @main
      struct MyApp: App {
          @State var immersionStyle: ImmersionStyle
      
          var body: some Scene {
              ImmersiveSpace(id: "MyImmersiveSpace") {
                  CompositorLayer(configuration: MyConfiguration()) { @MainActor layerRenderer in
                      Renderer.startRenderLoop(layerRenderer)
                  }
              }
              .immersionStyle(selection: $immersionStyle, in: .progressive, .full)
          }
      }
    • 17:12 - Layer configuration

      // Layer configuration
      
      struct MyConfiguration: CompositorLayerConfiguration {
          func makeConfiguration(capabilities: LayerRenderer.Capabilities,
                                 configuration: inout LayerRenderer.Configuration) {
              // Configure other aspects of LayerRenderer
              
              if configuration.layout == .layered {
                  let stencilFormat: MTLPixelFormat = .stencil8 
                  if capabilities.drawableRenderContextSupportedStencilFormats.contains(
                      stencilFormat
                  ) {
                      configuration.drawableRenderContextStencilFormat = stencilFormat 
                  }
                  configuration.drawableRenderContextRasterSampleCount = 1
              }
          }
      }
    • 17:40 - Render loop

      // Render loop
      
      struct Renderer {
          let portalStencilValue: UInt8 = 200 // Value not used in other stencil operations
      
          func renderFrame(with scene: MyScene,
                           drawable: LayerRenderer.Drawable,
                           commandBuffer: MTLCommandBuffer) {
              let drawableRenderContext = drawable.addRenderContext(commandBuffer: commandBuffer)
              let renderEncoder = configureRenderPass(commandBuffer: commandBuffer)
              drawableRenderContext.drawMaskOnStencilAttachment(commandEncoder: renderEncoder,
                                                                value: portalStencilValue)
              renderEncoder.setStencilReferenceValue(UInt32(portalStencilValue))
              
              scene.render(to: drawable, renderEncoder: renderEncoder)
      
              drawableRenderContext.endEncoding(commandEncoder: commandEncoder)
              drawable.encodePresent(commandBuffer: commandBuffer)
          }
      }
    • 20:55 - App structure

      // App structure
      
      @main
      struct MyImmersiveMacApp: App {
          @State var immersionStyle: ImmersionStyle = .full
      
          var body: some Scene {
              WindowGroup {
                  MyAppContent()
              }
      
              RemoteImmersiveSpace(id: "MyRemoteImmersiveSpace") {
                  MyCompositorContent()
              }
              .immersionStyle(selection: $immersionStyle, in: .full, .progressive)
         }
      }
    • 21:14 - App UI

      // App UI
      
      struct MyAppContent: View {
          @Environment(\.supportsRemoteScenes) private var supportsRemoteScenes
          @Environment(\.openImmersiveSpace) private var openImmersiveSpace
          @State private var spaceState: OpenImmersiveSpaceAction.Result?
      
          var body: some View {
              if !supportsRemoteScenes {
                  Text("Remote SwiftUI scenes are not supported on this Mac.")
              } else if spaceState != nil {
                  MySpaceStateView($spaceState)
              } else {
                  Button("Open remote immersive space") {
                      Task {
                          spaceState = await openImmersiveSpace(id: "MyRemoteImmersiveSpace")
                      }
                  }
              }
          }
      }
    • 21:35 - Compositor content and ARKit session

      // Compositor content and ARKit session
      
      struct MyCompositorContent: CompositorContent {
          @Environment(\.remoteDeviceIdentifier) private var remoteDeviceIdentifier
      
          var body: some CompositorContent {
              CompositorLayer(configuration: MyConfiguration()) { @MainActor layerRenderer in
                  guard let remoteDeviceIdentifier else { return }
                  let arSession = ARKitSession(device: remoteDeviceIdentifier)
                  Renderer.startRenderLoop(layerRenderer, arSession)
              }
          }
      }
    • 23:17 - C interoperability

      // Swift
      let remoteDevice: ar_device_t = remoteDeviceIdentifier.cDevice
Renderer.start_rendering(layerRenderer, remoteDevice)
      
      // C
      void start_rendering(cp_layer_renderer_t layer_renderer, ar_device_t remoteDevice) {
    ar_session_t session = ar_session_create_with_device(remoteDevice);
    // ...
}
    • 0:00 - Introducción
    • El renderizado de Metal en visionOS, junto con Compositor Services, trae nuevas y emocionantes funcionalidades este año, como efectos de desplazamiento sobre objetos con los que se puede interactuar, calidad de renderizado dinámico de tu contenido, un nuevo estilo de inmersión progresiva y la capacidad de renderizar contenido inmersivo en el Vision Pro desde macOS.

    • 1:58 - Nuevas API de bucle de renderizado
    • Se hizo un cambio importante en el bucle de renderizado en visionOS este año. El objeto queryDrawables ahora arroja una matriz de uno o dos elementos dibujables en lugar de solo uno. El segundo elemento dibujable estará presente siempre que grabes un video de alta calidad con Reality Composer Pro. Busca una plantilla que te ayude a iniciar en Xcode. Se admiten tanto Metal como Metal 4.

    • 4:21 - Efectos de desplazamiento
    • Una vez que hayas adoptado la nueva API de bucle de renderizado, podrás empezar a implementar efectos de desplazamiento sobre los objetos con los que se puede interactuar. Con estos efectos, las personas pueden ver qué objetos son interactivos y anticipar los objetivos de sus acciones. El sistema resaltará dinámicamente el objeto que la persona esté viendo. Por ejemplo, en un juego de rompecabezas se enfatizarán las piezas que el jugador puede seleccionar. Puedes hacer esto con la nueva textura de áreas de seguimiento, que define las regiones interactivas en tu escena. Hay algunas consideraciones extra que debes tener en cuenta cuando uses el suavizado multimuestra (MSAA).

    • 10:50 - Calidad de renderización dinámica
    • Ahora puedes dibujar tu contenido con una mayor fidelidad. Al usar la calidad de renderizado dinámico, puedes ajustar la resolución de tu contenido según la complejidad de tus escenas. Se basa en el renderizado foveado, que prioriza la densidad de pixeles donde el usuario está mirando. Puedes establecer una calidad de renderizado máxima y luego ajustar una calidad de ejecución acorde a ese rango. Una mayor calidad mejora la claridad del texto y de la interfaz, pero aumenta el uso de memoria y energía. Equilibrar la calidad y el rendimiento es crucial. Usa herramientas como Instruments y el depurador de Metal para encontrar ese equilibrio.

    • 14:44 - Inmersión progresiva
    • Este año, podrás renderizar contenido dentro de un portal inmersivo progresivo. Con esto, las personas controlan el nivel de inmersión al girar la Digital Crown. Esto las conecta con el entorno real y las ayuda a sentirse más cómodas cuando ven escenas complejas con movimiento. Para implementarlo, solicita al sistema que proporcione un búfer de plantilla con el fin de enmascarar el contenido que se encuentra fuera del límite del portal. El sistema aplica un efecto de difuminado en los bordes, creando una transición perfecta entre entornos reales y renderizados. Los pixeles que se encuentran fuera de la vista no se renderizan, lo que ahorra capacidad de procesamiento. Compartimos los detalles de implementación.

    • 18:32 - Renderizado espacial de macOS
    • El renderizado espacial de macOS permite aprovechar la capacidad de tu Mac para renderizar y transmitir contenido inmersivo al Apple Vision Pro. Esta nueva funcionalidad mejora las apps para Mac con experiencias inmersivas, como vistas previas de modelado 3D en tiempo real. ARKit y worldTrackingProvider ahora están disponibles en macOS. Esto te permite consultar la ubicación del Vision Pro en el espacio. El RemoteImmersiveSpace de macOS aloja CompositorLayer y ARKitSession, como lo haría una app nativa de visionOS. Hay un nuevo remoteDeviceIdentifier que deberás usar para conectar la sesión de ARKit de tu Mac al Vision Pro. Además, todas las API relevantes tienen equivalentes nativos en C.

    • 23:51 - Próximos pasos
    • Estas nuevas capacidades de Metal y Compositor Services en visionOS permiten brindar mejor interactividad, mayor fidelidad y un nuevo estilo de inmersión progresiva a tus apps y juegos. A continuación, consulta “Set the scene with SwiftUI in visionOS” y “What's new in visionOS 26”.

Developer Footer

  • Videos
  • WWDC25
  • Novedades del renderizado de Metal para apps inmersivas
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • App Store
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Fonts
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Open Source
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Sample Code
    • Tutorials
    • Downloads
    • Forums
    • Videos
    Open Menu Close Menu
    • Support Articles
    • Contact Us
    • Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Get the Apple Developer app.
    Copyright © 2025 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines