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

Vídeos

Abrir menu Fechar menu
  • Coleções
  • Tópicos
  • Todos os vídeos
  • Sobre

Voltar para WWDC25

  • Sobre
  • Resumo
  • Transcrição
  • Código
  • Novidades na renderização no Metal para apps imersivos

    Descubra as melhorias mais recentes na renderização no Metal para apps imersivos com o framework Compositor Services. Saiba como adicionar efeitos de hover para destacar os elementos interativos do seu app e como renderizar com mais fidelidade usando qualidade de renderização dinâmica. Saiba mais sobre o novo estilo de imersão progressiva. Além disso, descubra como incluir experiências imersivas nos apps para macOS renderizando o conteúdo diretamente no Metal do Mac para o Vision Pro. Para aproveitar ao máximo esta sessão, primeiro assista ao vídeo “Discover Metal for immersive apps” da WWDC23.

    Capítulos

    • 0:00 - Introdução
    • 1:58 - Novas APIs de loops de renderização
    • 4:21 - Efeitos de foco
    • 10:50 - Qualidade de renderização dinâmica
    • 14:44 - Imersão progressiva
    • 18:32 - Renderização espacial para macOS
    • 23:51 - Próximas etapas

    Recursos

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

    Vídeos relacionados

    WWDC25

    • Aprimore jogos com o Metal 4
    • Conheça o Metal 4
    • Explore a entrada de acessórios espaciais no visionOS
    • Explore jogos feitos com o Metal 4
    • Novidades do SwiftUI
    • Novidades do visionOS 26

    WWDC24

    • Render Metal with passthrough in visionOS

    WWDC23

    • Discover Metal for immersive apps
  • Buscar neste vídeo...

    Olá! Meu nome é Ricardo. Sou Software Engineer na Apple. Hoje, vou mostrar os novos recursos que você pode adotar ao usar Metal para renderizar conteúdo imersivo no Apple Vision Pro. No ano passado, mostramos como usar o Metal, o Compositor Services e o ARKit para criar experiências imersivas renderizando diretamente seu conteúdo no visionOS.

    Você pode implementar uma experiência totalmente imersiva, como no Demeo da Resolution Games, ou adotar o estilo de imersão mista, para mostrar seu conteúdo junto com o mundo real.

    E agora, graças aos comentários valiosos de desenvolvedores como você, a renderização do Metal no visionOS suporta novos recursos incríveis.

    Você poderá adicionar detalhes ainda mais ricos e efeitos interativos aos seus apps e jogos. Vou explicar tudo sobre os novos recursos do Compositor Services neste vídeo.

    Para aproveitar ao máximo, você precisa conhecer o framework Compositor Services e as técnicas de renderização do Metal. Se você nunca usou essas tecnologias antes, aprenda sobre elas nestes vídeos anteriores.

    Para adotar os novos recursos, primeiro você precisa fazer mudanças no loop de renderização atual. Vou explicar como adotar novas APIs que tornem o pipeline mais flexível. Depois disso, você poderá adicionar efeitos de foco para destacar os elementos interativos do seu app. Também será possível ajustar dinamicamente a resolução do conteúdo renderizado. Há um novo estilo de imersão progressiva que permite que as pessoas ajustem seu nível de imersão com a Digital Crown. E você pode usar seu Mac para renderizar conteúdo imersivo direto no Vision Pro. Tudo começa com as novas APIs de loops de renderização. Vamos lá!

    Um app imersivo do Metal começa no SwiftUI, onde você cria um espaço imersivo com uma camada de compositor.

    Ela fornece um objeto renderizador para uso no seu loop de renderização. Você consulta quadros no renderizador de camada. De cada quadro, você obtém drawables, que contêm texturas usadas para renderizar seu conteúdo. Se você já criou um app imersivo do Metal, está consultando um único drawable para cada quadro renderizado. Este ano, o Compositor Services tem uma nova função de consulta a drawables que retorna uma matriz de drawables. Com base no contexto do sistema, essa matriz conterá um ou dois drawables. Na maioria das vezes, você terá um único drawable. Porém, quando você estiver gravando um vídeo de alta qualidade com o Reality Composer Pro, um quadro retornará dois drawables. Você pode identificar os drawables com a nova propriedade target. O drawable da sua tela do Vision Pro tem o valor .builtIn, e o de gravação tem o valor .capture. Para saber como capturar vídeos de alta qualidade, consulte a documentação para desenvolvedores.

    Esta função renderFrame pode ser familiar para você. Depois de consultar o próximo quadro e atualizar o estado da cena, chamo a função queryDrawable. Então, aguardo o tempo de entrada ideal e renderizo a cena para o drawable.

    Agora, substituo queryDrawable por queryDrawables. Eu me certifico de que a matriz não está vazia e renderizo a cena para todos os drawables.

    O Xcode inclui um modelo prático que mostra como criar um app imersivo do Metal. É um ótimo ponto de partida. Para conferir, inicie um novo projeto do Xcode para um app do visionOS e selecione Metal 4 no menu Immersive Space Renderer.

    O Metal 3 ainda é totalmente suportado este ano, e você pode usá-lo no modelo do Xcode selecionando a opção "Metal".

    Saiba mais sobre como adotar a versão mais recente do Metal em "Conhecer o Metal 4".

    Depois de adotar a nova função queryDrawables, você poderá usar todos os novos recursos este ano, como adicionar efeitos de foco aos objetos interativos da sua cena.

    Com eles, a pessoa que usa seu app pode ver quais objetos são interativos e pode antecipar os alvos de suas ações. O sistema destacará dinamicamente o objeto que o espectador está olhando. Por exemplo, um quebra-cabeça pode destacar as peças que o jogador pode selecionar.

    Imagine um app que está renderizando uma cena com vários objetos 3D, mas só alguns deles são interativos. Quero garantir que os efeitos de foco sejam aplicados apenas aos objetos interativos da cena. Quando um objeto não é interativo, ele não é rastreado e não tem um efeito de foco. Então eu renderizo normalmente. Por outro lado, se um objeto é interativo, eu incluo uma nova área de rastreamento ao drawable. É necessário atribuir um identificador de objeto exclusivo a cada área de rastreamento registrada.

    Em seguida, verifico se o objeto deve ter um efeito de foco. Se não deve, eu o desenho com o valor de renderização da área de rastreamento. Isso é útil para detectar gestos de pinça nos meus objetos. Mas, se o objeto tiver um efeito de foco, eu o configuro na área de rastreamento antes de renderizar.

    No código, preciso configurar meu renderizador de camada para usar efeitos de foco. Defino a textura das áreas de rastreamento para usar um formato de pixel de 8 bits. Ele suporta até 255 objetos interativos simultâneos. Eu garanto que meus recursos de camada sejam compatíveis com o formato e o defino na configuração.

    No código de registro da área de rastreamento, verifico se o objeto é interativo. Se for, registro uma nova área de rastreamento no drawable com meu identificador de objeto. Mantenha registro dos identificadores exclusivos durante o ciclo de vida dos objetos. Em seguida, verifico se o objeto tem um efeito de foco e, se tiver, adiciono-o com o atributo .automatic. Assim, o sistema adicionará automaticamente um efeito de foco quando o espectador olhar para o objeto. Por fim, eu renderizo.

    Com o Compositor Services, o drawable fornece várias texturas que seu app pode usar para renderizar conteúdo.

    Talvez você conheça a textura de cor, que é o que o usuário do seu app vê. Há também a textura de profundidade, em que cores mais escuras indicam objetos mais distantes do espectador. O sistema usa essa textura para aumentar a precisão do conteúdo à medida que o espectador se move pela cena. Este ano, incluímos uma textura de áreas de rastreamento, que define as diferentes regiões interativas da sua cena. Um drawable fornece as texturas de cor e profundidade. Agora você também pode consultar a nova textura de áreas de rastreamento. Nela, você desenha áreas distintas correspondentes aos seus objetos interativos. Com os efeitos de foco, quando alguém olha para um objeto interativo na sua cena, o sistema usa a textura de áreas de rastreamento para encontrar a região correspondente e aplica um efeito de foco na parte correspondente da textura de cor. Veja de novo a função de renderização de objeto com a área de rastreamento configurada. Para renderizar a textura de áreas de rastreamento, preciso de um renderValue calculado pelo sistema. Declaro uma variável local para armazená-lo e o obtenho da área de rastreamento correspondente. Se meu objeto não for interativo, posso usar o renderValue padrão nil. Por fim, eu o passo para a função de desenho, onde vou enviá-lo para o shader de fragmento.

    Vamos ver o código do shader.

    A saída do shader de fragmento tem um trackingAreaRenderValue, associado ao anexo de cor do índice 1. Eu defini a textura das minhas áreas de rastreamento lá. Vinculei o valor de renderização à minha struct Uniforms e o retorno na saída do shader junto com o valor de cor. Agora, meu app tem efeitos interativos de foco. Tenha mais um ponto em mente se você usa antisserrilhamento por multiamostragem ou MSAA.

    Essa técnica renderiza uma textura intermediária em resolução maior e faz a média dos valores de cor em uma janela de amostragem. Isso geralmente é feito usando a opção multisampleResolve na ação de armazenamento da textura de destino. Não é possível resolver a textura das áreas de rastreamento da mesma forma que um pixel de cor. Essa ação calcularia a média dos seus valores de renderização, gerando um escalar inválido não correspondente a nenhuma área de rastreamento. Se você usar multisampleResolve para a cor, implemente um tile resolver personalizado para a textura das áreas de rastreamento. Faça isso usando a opção de armazenamento .dontCare com um pipeline de renderização de bloco personalizado. Uma boa estratégia é escolher o valor de renderização que aparece com mais frequência na janela de amostragem na textura de origem do MSAA. Para uma revisão detalhada dos efeitos de foco e como usá-los com o MSAA, leia "Renderizar efeitos de foco em apps imersivos do Metal" na documentação para desenvolvedores.

    As áreas de rastreamento também permitem que seu app lide com interações com objetos de forma mais fácil. Os eventos espaciais agora incluem um identificador de área de rastreamento anulável. Uso esse identificador para ver se ele corresponde a algum dos meus objetos de cena. Se eu encontrar um objeto-alvo, posso executar uma ação nele.

    Melhorei a interatividade do meu app com efeitos de foco. A pessoa que usa meu app pode ver claramente quais objetos são acionáveis e quais serão ativados com o gesto de pinça. E isso facilita lidar com eventos de entrada mais do que nunca! Agora, você também pode desenhar seu conteúdo com mais fidelidade que antes. Usando a qualidade de renderização dinâmica, você pode ajustar a resolução do conteúdo com base na complexidade das cenas.

    Primeiro, vou lembrar como funciona a renderização foveada. Em uma textura padrão não foveada, os pixels são distribuídos uniformemente pela superfície. Com a renderização foveada, o sistema ajuda a desenhar uma textura com maior densidade de pixels no centro. Assim, seu app usa recursos computacionais e de energia para melhorar a aparência do conteúdo que o espectador tem mais chance de olhar. Este ano, você pode aproveitar a qualidade dinâmica da renderização foveada. Já é possível controlar a qualidade dos quadros renderizados pelo seu app. Primeiro, especifique uma qualidade de renderização máxima adequada ao app. Isso define o limite máximo para a sessão de renderização do app. Depois, ajuste a qualidade do tempo de execução dentro do intervalo escolhido com base no tipo de conteúdo exibido.

    Aumentar a qualidade da renderização expande a área de alta relevância da textura, levando a uma textura geralmente maior. Lembre-se: aumentar a qualidade também significa que o app vai usar mais memória e energia. Se você estiver renderizando texto ou elementos da interface, será útil definir uma qualidade de renderização mais alta. Mas se você exibir uma cena 3D complexa, pode esbarrar nos recursos de computação. Para garantir que seu app funcione bem, encontre um equilíbrio entre imagens de alta qualidade e a quantidade de energia que o app usa.

    Você pode usar o Instruments para analisar o desempenho em tempo real do app e o depurador do Metal para investigar e otimizar seu código e os shaders. Lembre-se: é importante profilar seu app com as cenas mais complexas para garantir tempo suficiente para renderizar os quadros de forma estável.

    Consulte a documentação para desenvolvedores e saiba como otimizar seus apps de renderização do Metal.

    Neste exemplo de código, profilei meu app e determinei que quero renderizar o menu do app com qualidade 0.8. Assim, o texto fica mais nítido. Quero renderizar o mundo com uma qualidade 0.6 porque é uma cena complexa. Também incluí uma propriedade calculada com a qualidade máxima de renderização que usarei. Aqui está a configuração de camada. A qualidade de renderização dinâmica só funciona com foveação. Confiro se está ativada. Depois, defino a qualidade máxima de renderização para o valor da propriedade calculada. Lembre-se de definir o valor mínimo que faz sentido para seu conteúdo. Caso contrário, o app usará mais memória do que deveria.

    Quando carrego uma nova cena, chamo minha função adjustRenderQuality. Ajustar a qualidade da renderização só é possível com a foveação ativada. Troco o tipo de cena e ajusto renderQuality de acordo com ele.

    A transição entre valores de qualidade leva um pouco de tempo. Não é instantânea. O sistema garante uma transição suave.

    Com qualidade de renderização dinâmica, suas cenas detalhadas vão brilhar. A maior resolução das cenas renderizadas pode ajudar muito na nitidez dos seus detalhes. Mas lembre-se: você pode precisar diminuir a qualidade nas cenas muito complexas. Agora dá para ajustar a qualidade de renderização do app ao conteúdo.

    Este ano, seu app do Metal pode ser renderizado dentro de um portal progressivo e imersivo. Com o estilo de imersão progressiva, os usuários do seu app controlam o nível de imersão girando a Digital Crown. Esse modo os ancora no ambiente real e pode deixá-los mais à vontade enquanto visualizam cenas complexas com movimento. Quando você visualiza um app do Metal no modo de imersão progressiva, o sistema renderiza apenas o conteúdo que está dentro do nível de imersão atual.

    Veja a cena de um jogo sendo renderizada em imersão total. E aqui está a mesma cena renderizada em imersão parcial, depois que o espectador ajusta a Digital Crown. Compare as cenas e observe como você pode economizar potência computacional não renderizando a área destacada fora do portal. Essa parte não está visível, então não é necessário renderizá-la. As novas APIs permitem mascarar seu conteúdo com um estêncil de portal gerado pelo sistema. Este oval branco mostra o estêncil de portal correspondente. O buffer do estêncil funciona como máscara para a cena renderizada. Com ele, você só renderizar o conteúdo dentro do portal. Observe que a cena sendo renderiza ainda não tem bordas suavizadas. O desvanecimento é aplicado pelo sistema como última etapa no buffer de comando, resultando na cena vista pelo usuário.

    Para usar o estêncil para evitar renderização de conteúdo desnecessário, configure sua camada de compositor. Verifique se o formato de estêncil desejado é compatível com os recursos de camada e defina-o na sua configuração. Para aplicar a máscara de estêncil do portal, adicione um contexto de renderização ao drawable com o buffer de comando. Desenhar sua máscara no estêncil vai impedir que pixels invisíveis sejam renderizados. Finalize a codificação com o contexto de renderização em vez de encerrar o codificador de comando. Assim, o efeito de portal é aplicado de forma eficiente no seu conteúdo. No meu app, crio um Espaço Imersivo no SwiftUI e adiciono o estilo de imersão progressiva como uma nova opção à minha lista. A pessoa que usa meu app pode alternar entre o estilo progressivo e o completo. Então configuro a camada. Primeiro, saiba que o estilo de imersão progressiva só funciona com o layout em camadas. Especifico o formato de estêncil desejado para 8 bits por pixel. Verifico se os recursos suportam esse formato e o defino na minha configuração. Defino a contagem de amostras como 1, já que não estou usando MSAA. Se você usa essa técnica, defina-a como a contagem de amostragem do MSAA.

    No renderizador, adiciono um contexto de renderização ao drawable. Passo o mesmo buffer de comando que usarei para os comandos de renderização. Então desenho minha máscara de portal no anexo do estêncil. Selecionei um valor de estêncil que não uso em nenhuma outra operação de estêncil. Defino o valor de referência do estêncil no codificador de renderização. Assim, meu renderizador não desenhará a área fora do nível de imersão atual. Após renderizar a cena, veja como termino a codificação no contexto de renderização do drawable.

    Para ver um exemplo prático de um renderizador usando o estilo de imersão progressiva, escolha a opção Progressive no modelo de app do Metal para visionOS. Assim você vai começar a criar um app do Metal em estilo de portal.

    Vamos conhecer a renderização espacial do macOS.

    Até agora, falei sobre a criação de experiências imersivas nativas no Vision Pro. Este ano, você poderá usar a eficiência do Mac para renderizar e transmitir conteúdo imersivo direto para o Vision Pro. Assim, é possível adicionar experiências imersivas aos apps existentes para Mac.

    Por exemplo, um app de modelagem 3D pode visualizar suas cenas direto no Vision Pro. Ou você pode criar do zero um app imersivo para macOS. Assim, você cria experiências imersivas complexas de alto processamento sem as limitações de consumo de energia do Vision Pro. É muito fácil iniciar uma sessão imersiva remota em um app para Mac. Ao abrir um Espaço Imersivo no macOS, você receberá uma solicitação para aceitar a conexão no Vision Pro.

    Faça isso, e você começará a ver o conteúdo imersivo renderizado no Mac.

    Um app típico do Mac é criado com SwiftUI ou AppKit. Você usa um desses frameworks para criar e exibir janelas. O sistema renderiza o conteúdo da janela com Core Animation. Você pode usar vários frameworks do macOS para implementar funcionalidades do app. E o sistema exibe seu conteúdo na tela do Mac. Para criar uma experiência imersiva compatível com Mac, use os mesmos frameworks que permitem criar apps imersivos para visionOS. Primeiro, use SwiftUI com o novo tipo de cena RemoteImmersiveSpace. Depois, adote o framework Compositor Services. Use ARKit e Metal para posicionar e renderizar o conteúdo. E o sistema exibe sua cena imersiva diretamente no Vision Pro. O RemoteImmersiveSpace do macOS hospeda o CompositorLayer e o ARKitSession, como um app nativo do visionOS. Eles se conectam perfeitamente à tela e aos sensores do Vision Pro. Para conectar o ARKitSession ao visionOS, há um novo objeto remoteDeviceIdentifier no ambiente do SwiftUI, que você passa ao inicializador da sessão. É assim que um app imersivo para Mac é estruturado.

    Defino um novo RemoteImmersiveSpace, que contém MyCompositorContent. Mostrarei como ele usa a camada de compositor em breve. No Mac, apenas os estilos de imersão progressiva e total são suportados. Na interface do meu app para Mac, eu uso a nova variável supportsRemoteScenes para verificar se meu Mac tem esse recurso. Posso ajustar a interface para mostrar uma mensagem se cenas remotas não forem suportadas. Se houver suporte e eu ainda não abri o espaço imersivo, posso iniciá-lo. A última parte do meu app é o MyCompositorContent. Ele inclui o CompositorLayer e o ARKitSession. Eu crio e uso um CompositorLayer da mesma forma que fiz no visionOS. Acesso o novo objeto remoteDeviceIdentifier do ambiente do SwiftUI e o passo para o inicializador do ARKitSession. Isso conectará o ARKitSession do meu Mac ao Vision Pro. Por último, inicio meu loop de renderização como faria em um app imersivo do Metal.

    O ARKit e o provedor mundial de rastreamento agora estão disponíveis no macOS. Isso permite consultar a localização do Vision Pro no espaço. Assim como faria em um app imersivo nativo, você usará a pose do dispositivo para atualizar sua cena e drawables antes de renderizar. Um app espacial para macOS suporta qualquer dispositivo de entrada conectado ao Mac. Você pode usar controles de teclado e mouse. Ou pode conectar um controle e gerenciar entradas usando o framework Game Controller. Além disso, é possível usar gestos de pinça nos elementos interativos da cena imersiva com o modificador onSpatialEvent no seu layerRenderer.

    Agora você também pode criar cenas do SwiftUI a partir de um app do AppKit ou UIKit existente. Essa é uma excelente forma de adicionar novas experiências imersivas a apps para Mac. Saiba mais sobre como fazer isso em "Novidades do SwiftUI".

    É comum que os mecanismos de renderização sejam implementados em C ou C++. Todas as APIs que mencionei têm equivalentes nativos em C. Os tipos C para o framework Compositor Services começam com o prefixo "cp". Eles usam padrões e convenções semelhantes às bibliotecas C conhecidas, como Core Foundation. Para ARKit, a propriedade cDevice fornece um identificador de dispositivo remoto compatível com C. Você pode passá-lo para seu framework C e inicializar o ARKitSession com a função create_with_device. Agora você tem o que precisa para usar seu Mac para exibir conteúdo imersivo no Vision Pro.

    Estou animado para ver como você vai usar os novos recursos para melhorar seus apps imersivos. Eles permitem melhor interatividade, maior fidelidade e o novo estilo de imersão progressiva. E mal posso esperar para ver o que você fará com os novos recursos espaciais do macOS. Para saber como elevar o nível dos seus apps imersivos, confira a sessão "Aprimorar com o SwiftUI no visionOS". E para uma visão geral de outras melhorias da plataforma, consulte "Novidades do visionOS".

    Agradeço sua participação.

    • 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 - Introdução
    • A renderização com o Metal no visionOS, juntamente com os Compositor Services, traz recursos novos este ano, incluindo: efeitos de destaque em objetos interativos, qualidade de renderização dinâmica para o conteúdo, novo estilo de imersão progressiva e a capacidade de renderizar conteúdo imersivo no Vision Pro a partir do macOS.

    • 1:58 - Novas APIs de loops de renderização
    • Houve uma mudança no loop de renderização no visionOS. Em vez de retornar um recurso gráfico, o objeto queryDrawables retorna uma matriz com um ou dois recursos gráficos. O segundo recurso gráfico estará presente durante a gravação de um vídeo em alta qualidade com o Reality Composer Pro. Confira no Xcode um modelo para ajudar você a começar. O Metal e o Metal 4 são compatíveis.

    • 4:21 - Efeitos de foco
    • Depois de adotar a nova API de loop de renderização, comece a implementar efeitos de destaque nos objetos interativos. Com os efeitos de destaque, as pessoas podem ver quais objetos são interativos e antecipar os alvos de suas ações. O sistema destacará dinamicamente o objeto que a pessoa estiver olhando. Um jogo de quebra-cabeça pode destacar as peças que o jogador pode selecionar. Para isso, use a nova textura de áreas de rastreamento, que define as diferentes regiões interativas na cena. Há algumas considerações adicionais se você estiver usando MSAA (multisample antialiasing).

    • 10:50 - Qualidade de renderização dinâmica
    • Você pode desenhar seu conteúdo com uma fidelidade ainda maior. Ao usar a qualidade de renderização dinâmica, você pode ajustar a resolução do conteúdo com base na complexidade das cenas. Ela se baseia na renderização foveada, priorizando a densidade de pixels onde o espectador está olhando. Defina a qualidade máxima de renderização e ajuste a qualidade em tempo de execução nesse intervalo. Qualidade mais alta melhora a clareza, mas aumenta o uso de memória e energia. Equilibrar qualidade e desempenho é fundamental. Encontre o equilíbrio com ferramentas como o Instruments e o depurador do Metal.

    • 14:44 - Imersão progressiva
    • Novidade este ano: renderize conteúdo dentro de um portal imersivo progressivo. Com isso, as pessoas controlam o nível de imersão girando a Digital Crown. Isso as conecta ao ambiente real e pode ajudar a proporcionar mais conforto na visualização de cenas complexas com movimento. Para implementar isso, o sistema deve fornecer um buffer de estêncil para mascarar o conteúdo fora dos limites do portal. O sistema aplica um efeito de esmaecimento nas bordas do portal, criando uma transição suave entre os ambientes real e renderizado. Os pixels fora da visão do portal não são renderizados, poupando poder de processamento. Os detalhes da implementação são compartilhados.

    • 18:32 - Renderização espacial para macOS
    • A renderização espacial no macOS permite aproveitar o poder do Mac para renderizar e transmitir conteúdo imersivo para o Apple Vision Pro. Esse recurso permite que apps Mac sejam aprimorados com experiências imersivas, como pré-visualizações em tempo real de modelagem 3D. O ARKit e o worldTrackingProvider agora estão disponíveis no macOS. Isso permite consultar a localização do Vision Pro no espaço. O RemoteImmersiveSpace do macOS hospeda o CompositorLayer e o ARKitSession, como um app nativo do visionOS faria. Há um novo remoteDeviceIdentifier que será usado para conectar a sessão ARKit do Mac ao Vision Pro. E todas as APIs relevantes têm equivalentes nativas em C.

    • 23:51 - Próximas etapas
    • Essas funcionalidades do Metal e dos Compositor Services no visionOS permitem oferecer melhor interatividade, maior fidelidade e o novo estilo de imersão progressiva aos apps e jogos. Confira “Defina a cena com SwiftUI no visionOS” e “Novidades no visionOS 26”.

Developer Footer

  • Vídeos
  • WWDC25
  • Novidades na renderização no Metal para apps imersivos
  • 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