View in English

  • Apple Developer
    • Get Started

    Explore Get Started

    • Overview
    • Learn
    • Apple Developer Program

    Stay Updated

    • Latest News
    • Hello Developer
    • Platforms

    Explore Platforms

    • Apple Platforms
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    Featured

    • Design
    • Distribution
    • Games
    • Accessories
    • Web
    • Home
    • CarPlay
    • Technologies

    Explore Technologies

    • Overview
    • Xcode
    • Swift
    • SwiftUI

    Featured

    • Accessibility
    • App Intents
    • Apple Intelligence
    • Games
    • Machine Learning & AI
    • Security
    • Xcode Cloud
    • Community

    Explore Community

    • Overview
    • Meet with Apple events
    • Community-driven events
    • Developer Forums
    • Open Source

    Featured

    • WWDC
    • Swift Student Challenge
    • Developer Stories
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Centers
    • Documentation

    Explore Documentation

    • Documentation Library
    • Technology Overviews
    • Sample Code
    • Human Interface Guidelines
    • Videos

    Release Notes

    • Featured Updates
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • Downloads

    Explore Downloads

    • All Downloads
    • Operating Systems
    • Applications
    • Design Resources

    Featured

    • Xcode
    • TestFlight
    • Fonts
    • SF Symbols
    • Icon Composer
    • Support

    Explore Support

    • Overview
    • Help Guides
    • Developer Forums
    • Feedback Assistant
    • Contact Us

    Featured

    • Account Help
    • App Review Guidelines
    • App Store Connect Help
    • Upcoming Requirements
    • Agreements and Guidelines
    • System Status
  • Quick Links

    • Events
    • News
    • Forums
    • Sample Code
    • Videos
 

Videos

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

Más videos

  • Información
  • Resumen
  • Código
  • Ve más allá con los juegos de Metal 4

    Profundiza en los últimos avances de Metal 4. Presentaremos las nuevas funciones de trazado de rayos que te ayudarán a llevar las cargas de trabajo más complejas y visualmente ricas a Apple Chip. Descubre cómo MetalFX puede ayudar a escalar cargas de trabajo mejorando renderizados, interpolando fotogramas y eliminando ruido de escenas.

    Para aprovechar al máximo esta sesión, te recomendamos que primero eches una mirada a “Descubrir Metal 4” y “Explorar juegos de Metal 4”.

    Capítulos

    • 0:00 - Introducción
    • 2:13 - Mejorar renderizados
    • 7:17 - Interpolar fotogramas
    • 13:50 - Trazar rayos con Metal 4
    • 19:25 - Eliminar ruido y mejorar calidad
    • 26:08 - Próximos pasos

    Recursos

      • Video HD
      • Video SD

    Videos relacionados

    WWDC25

    • Combina el aprendizaje automático y los gráficos de Metal 4
    • Descubrir Metal 4
    • Explorar juegos de Metal 4
    • Novedades del renderizado de Metal para apps inmersivas
    • Sube de nivel tus juegos

    WWDC23

    • Your guide to Metal ray tracing

    WWDC22

    • Boost performance with MetalFX Upscaling
  • Buscar este video…
    • 6:46 - Reactive Mask

      // Create reactive mask setup in shader
      out.reactivity = m_material_id == eRain ? (m_material_id == eSpark ? 1.0f : 0.0f) : 0.8f;
      
      // Set reactive mask before encoding upscaler on host
      temporalUpscaler.reactiveMask = reactiveMaskTexture;
    • 8:35 - MetalFX Frame Interpolator

      // Create and configure the interpolator descriptor
      MTLFXFrameInterpolatorDescriptor* desc = [MTLFXFrameInterpolatorDescriptor new];
      desc.scaler = temporalScaler;
      // ...
      
      // Create the effect and configure your effect
      id<MTLFXFrameInterpolator> interpolator = [desc newFrameInterpolatorWithDevice:device];
      interpolator.motionVectorScaleX = mvecScaleX;
      interpolator.motionVectorScaleY = mvecScaleY;
      interpolator.depthReversed = YES;
      
      // Set input textures
      interpolator.colorTexture = colorTexture;
      interpolator.prevColorTexture = prevColorTexture;
      interpolator.depthTexture = depthTexture;
      interpolator.motionTexture = motionTexture;
      interpolator.outputTexture = outputTexture;
    • 12:45 - Interpolator present helper class

      #include <thread>
      #include <mutex>
      #include <sys/event.h>
      #include <mach/mach_time.h>
      
      
      class PresentThread
      {
          int m_timerQueue;
          std::thread m_encodingThread, m_pacingThread;
          std::mutex m_mutex;
          std::condition_variable m_scheduleCV, m_threadCV, m_pacingCV;
          float m_minDuration;
          
          uint32_t m_width, m_height;
          MTLPixelFormat m_pixelFormat;
          
          const static uint32_t kNumBuffers = 3;
          uint32_t m_bufferIndex, m_inputIndex;
          bool m_renderingUI, m_presentsPending;
          
          CAMetalLayer *m_metalLayer;
          id<MTLCommandQueue> m_presentQueue;
      
          id<MTLEvent> m_event;
          id<MTLSharedEvent> m_paceEvent, m_paceEvent2;
          uint64_t m_eventValue;
          uint32_t m_paceCount;
          
          int32_t m_numQueued, m_framesInFlight;
          
          id<MTLTexture> m_backBuffers[kNumBuffers];
          id<MTLTexture> m_interpolationOutputs[kNumBuffers];
          id<MTLTexture> m_interpolationInputs[2];
          id<MTLRenderPipelineState> m_copyPipeline;
          
          std::function<void(id<MTLRenderCommandEncoder>)> m_uiCallback = nullptr;
          
          void PresentThreadFunction();
          void PacingThreadFunction();
          
          void CopyTexture(id<MTLCommandBuffer> commandBuffer, id<MTLTexture> dest, id<MTLTexture> src, NSString *label);
      
      public:
          
          PresentThread(float minDuration, CAMetalLayer *metalLayer);
          ~PresentThread()
          {
              std::unique_lock<std::mutex> lock(m_mutex);
              m_numQueued = -1;
              m_threadCV.notify_one();
              m_encodingThread.join();
          }
          void StartFrame(id<MTLCommandBuffer> commandBuffer)
          {
              [commandBuffer encodeWaitForEvent:m_event value:m_eventValue++];
          }
      
          void StartUI(id<MTLCommandBuffer> commandBuffer)
          {
              assert(m_uiCallback == nullptr);
              if(!m_renderingUI)
              {
                  CopyTexture(commandBuffer, m_interpolationInputs[m_inputIndex], m_backBuffers[m_bufferIndex], @"Copy HUDLESS");
                  m_renderingUI = true;
              }
          }
          
          void Present(id<MTLFXFrameInterpolator> frameInterpolator, id<MTLCommandQueue> queue);
          
          id<MTLTexture> GetBackBuffer()
          {
              return m_backBuffers[m_bufferIndex];
          }
      
          void Resize(uint32_t width, uint32_t height, MTLPixelFormat pixelFormat);
          
          void DrainPendingPresents()
          {
              std::unique_lock<std::mutex> lock(m_mutex);
              while(m_presentsPending)
                  m_scheduleCV.wait(lock);
          }
          
          bool UICallbackEnabled() const
          {
              return m_uiCallback != nullptr;
          }
          
          void SetUICallback(std::function<void(id<MTLRenderCommandEncoder>)> callback)
          {
              m_uiCallback = callback;
          }
          
      };
      
      PresentThread::PresentThread(float minDuration, CAMetalLayer *metalLayer)
          : m_encodingThread(&PresentThread::PresentThreadFunction, this)
          , m_pacingThread(&PresentThread::PacingThreadFunction, this)
          , m_minDuration(minDuration)
          , m_numQueued(0)
          , m_metalLayer(metalLayer)
          , m_inputIndex(0u)
          , m_bufferIndex(0u)
          , m_renderingUI(false)
          , m_presentsPending(false)
          , m_framesInFlight(0)
          , m_paceCount(0)
          , m_eventValue(0)
      {
          id<MTLDevice> device = metalLayer.device;
          m_presentQueue = [device newCommandQueue];
          m_presentQueue.label = @"presentQ";
          m_timerQueue = kqueue();
          
          metalLayer.maximumDrawableCount = 3;
          
          Resize(metalLayer.drawableSize.width, metalLayer.drawableSize.height, metalLayer.pixelFormat);
          
          m_event = [device newEvent];
          m_paceEvent = [device newSharedEvent];
      	m_paceEvent2 = [device newSharedEvent];
      }
      
      
      void PresentThread::Present(id<MTLFXFrameInterpolator> frameInterpolator, id<MTLCommandQueue> queue)
      {
          id<MTLCommandBuffer> commandBuffer = [queue commandBuffer];
          
          if(m_renderingUI)
          {
              frameInterpolator.colorTexture = m_interpolationInputs[m_inputIndex];
              frameInterpolator.prevColorTexture = m_interpolationInputs[m_inputIndex^1];
              frameInterpolator.uiTexture = m_backBuffers[m_bufferIndex];
          }
          else
          {
              frameInterpolator.colorTexture = m_backBuffers[m_bufferIndex];
              frameInterpolator.prevColorTexture = m_backBuffers[(m_bufferIndex + kNumBuffers - 1) % kNumBuffers];
              frameInterpolator.uiTexture = nullptr;
          }
          
          frameInterpolator.outputTexture = m_interpolationOutputs[m_bufferIndex];
      
          [frameInterpolator encodeToCommandBuffer:commandBuffer];
          [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
              std::unique_lock<std::mutex> lock(m_mutex);
              m_framesInFlight--;
              m_scheduleCV.notify_one();
              m_paceCount++;
              m_pacingCV.notify_one();
          }];
          [commandBuffer encodeSignalEvent:m_event value:m_eventValue++];
          [commandBuffer commit];
      
          std::unique_lock<std::mutex> lock(m_mutex);
          m_framesInFlight++;
          m_numQueued++;
          m_presentsPending = true;
          m_threadCV.notify_one();
          while((m_framesInFlight >= 2) || (m_numQueued >= 2))
              m_scheduleCV.wait(lock);
      
          m_bufferIndex = (m_bufferIndex + 1) % kNumBuffers;
          m_inputIndex = m_inputIndex^1u;
          m_renderingUI = false;
      }
      
      void PresentThread::CopyTexture(id<MTLCommandBuffer> commandBuffer, id<MTLTexture> dest, id<MTLTexture> src, NSString *label)
      {
          MTLRenderPassDescriptor *desc = [MTLRenderPassDescriptor new];
          desc.colorAttachments[0].texture = dest;
          desc.colorAttachments[0].loadAction = MTLLoadActionDontCare;
          desc.colorAttachments[0].storeAction = MTLStoreActionStore;
          id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:desc];
          [renderEncoder setFragmentTexture:src atIndex:0];
          [renderEncoder setRenderPipelineState:m_copyPipeline];
          [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
          if(m_uiCallback)
              m_uiCallback(renderEncoder);
          renderEncoder.label = label;
          [renderEncoder endEncoding];
      }
      
      
      void PresentThread::PacingThreadFunction()
      {
          NSThread *thread = [NSThread currentThread];
          [thread setName:@"PacingThread"];
          [thread setQualityOfService:NSQualityOfServiceUserInteractive];
          [thread setThreadPriority:1.f];
          
          mach_timebase_info_data_t info;
          mach_timebase_info(&info);
          
          // maximum delta (0.1ms) in machtime units
          const uint64_t maxDeltaInNanoSecs = 100000000;
          const uint64_t maxDelta = maxDeltaInNanoSecs * info.denom / info.numer;
          
          uint64_t time = mach_absolute_time();
          
          uint64_t paceEventValue = 0;
          
          for(;;)
          {
              std::unique_lock<std::mutex> lock(m_mutex);
              while(m_paceCount == 0)
                  m_pacingCV.wait(lock);
              m_paceCount--;
              lock.unlock();
              
              // we get signal...
              const uint64_t prevTime = time;
              time = mach_absolute_time();
      		m_paceEvent.signaledValue = ++paceEventValue;
      
              const uint64_t delta = std::min(time - prevTime, maxDelta);
              const uint64_t timeStamp = time + ((delta*31)>>6);
              
              struct kevent64_s timerEvent, eventOut;
              struct timespec timeout;
              timeout.tv_nsec = maxDeltaInNanoSecs;
              timeout.tv_sec = 0;
              EV_SET64(&timerEvent,
                       0,
                       EVFILT_TIMER,
                       EV_ADD | EV_ONESHOT | EV_ENABLE,
                       NOTE_CRITICAL | NOTE_LEEWAY | NOTE_MACHTIME | NOTE_ABSOLUTE,
                       timeStamp,
                       0,
                       0,
                       0);
              
              kevent64(m_timerQueue, &timerEvent, 1, &eventOut, 1, 0, &timeout);
              
              // main screen turn on...
              m_paceEvent2.signaledValue = ++paceEventValue;
          }
      }
      
      
      void PresentThread::PresentThreadFunction()
      {
          NSThread *thread = [NSThread currentThread];
          [thread setName:@"PresentThread"];
          [thread setQualityOfService:NSQualityOfServiceUserInteractive];
          [thread setThreadPriority:1.f];
          
      
          uint64_t eventValue = 0;
          uint32_t bufferIndex = 0;
      
          uint64_t paceEventValue = 0;
      
          for(;;)
          {
              std::unique_lock<std::mutex> lock(m_mutex);
              
              if(m_numQueued == 0)
              {
                  m_presentsPending = false;
                  m_scheduleCV.notify_one();
              }
              
              while(m_numQueued == 0)
                  m_threadCV.wait(lock);
              
              if(m_numQueued < 0)
                  break;
              lock.unlock();
      
              @autoreleasepool
              {
                  id<CAMetalDrawable> drawable = [m_metalLayer nextDrawable];
      
      			lock.lock();
      			m_numQueued--;
      			m_scheduleCV.notify_one();
      			lock.unlock();
      
                  id<MTLCommandBuffer> commandBuffer = [m_presentQueue commandBuffer];
                  [commandBuffer encodeWaitForEvent:m_event value:++eventValue];
                  CopyTexture(commandBuffer, drawable.texture, m_interpolationOutputs[bufferIndex], @"Copy Interpolated");
                  [commandBuffer encodeSignalEvent:m_event value:++eventValue];
      			[commandBuffer encodeWaitForEvent:m_paceEvent value:++paceEventValue];
      
                  if(m_minDuration > 0.f)
                      [commandBuffer presentDrawable:drawable afterMinimumDuration:m_minDuration];
                  else
                      [commandBuffer presentDrawable:drawable];
                  [commandBuffer commit];
              }
              
              @autoreleasepool
              {
                  id<MTLCommandBuffer> commandBuffer = [m_presentQueue commandBuffer];
                  id<CAMetalDrawable> drawable = [m_metalLayer nextDrawable];
                  CopyTexture(commandBuffer, drawable.texture, m_backBuffers[bufferIndex], @"Copy Rendered");
      			[commandBuffer encodeWaitForEvent:m_paceEvent2 value:++paceEventValue];
                  if(m_minDuration > 0.f)
                      [commandBuffer presentDrawable:drawable afterMinimumDuration:m_minDuration];
                  else
                      [commandBuffer presentDrawable:drawable];
                  [commandBuffer commit];
              }
              
              bufferIndex = (bufferIndex + 1) % kNumBuffers;
          }
      }
      
      void PresentThread::Resize(uint32_t width, uint32_t height, MTLPixelFormat pixelFormat)
      {
          if((m_width != width) || (m_height != height) || (m_pixelFormat != pixelFormat))
          {
              id<MTLDevice> device = m_metalLayer.device;
      
              if(m_pixelFormat != pixelFormat)
              {
                  id<MTLLibrary> lib = [device newDefaultLibrary];
                  MTLRenderPipelineDescriptor *pipelineDesc = [MTLRenderPipelineDescriptor new];
                  pipelineDesc.vertexFunction = [lib newFunctionWithName:@"FSQ_VS_V4T2"];
                  pipelineDesc.fragmentFunction = [lib newFunctionWithName:@"FSQ_simpleCopy"];
                  pipelineDesc.colorAttachments[0].pixelFormat = pixelFormat;
                  m_copyPipeline = [device newRenderPipelineStateWithDescriptor:pipelineDesc error:nil];
                  m_pixelFormat = pixelFormat;
              }
              
              DrainPendingPresents();
              
              m_width = width;
      		m_height = height;
              
              MTLTextureDescriptor *texDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixelFormat width:width height:height mipmapped:NO];
      		texDesc.storageMode = MTLStorageModePrivate;
              for(uint32_t i = 0; i < kNumBuffers; i++)
              {
                  texDesc.usage = MTLTextureUsageShaderRead|MTLTextureUsageShaderWrite|MTLTextureUsageRenderTarget;
                  m_backBuffers[i] = [device newTextureWithDescriptor:texDesc];
                  texDesc.usage = MTLTextureUsageShaderRead|MTLTextureUsageRenderTarget;
                  m_interpolationOutputs[i] = [device newTextureWithDescriptor:texDesc];
              }
              texDesc.usage = MTLTextureUsageShaderRead|MTLTextureUsageRenderTarget;
              m_interpolationInputs[0] = [device newTextureWithDescriptor:texDesc];
              m_interpolationInputs[1] = [device newTextureWithDescriptor:texDesc];
      
          }
      }
    • 13:00 - Set intersection function table offset

      // Set intersection function table offset on host-side geometry descriptors
      NSMutableArray<MTLAccelerationStructureGeometryDescriptor *> *geomDescs ...;
      for (auto g = 0; g < geomList.size(); ++g)
      {
          MTLAccelerationStructureGeometryDescriptor *descriptor = ...;
          descriptor.intersectionFunctionTableOffset = g;
          ...
          [geomDescs addObject:descriptor];
      }
    • 13:01 - Set up the intersector

      // Set up the intersector
      metal::raytracing::intersector<intersection_function_buffer, instancing, triangle> trace;
      trace.set_geometry_multiplier(2); // Number of ray types, defaults to 1
      trace.set_base_id(1);             // Set ray type index, defaults to 0
    • 13:02 - Ray trace intersection function buffers

      // Ray trace intersection function buffers
      
      // Set up intersection function buffer arguments
      intersection_function_buffer_arguments ifb_arguments;
      ifb_arguments.intersection_function_buffer = raytracingResources.ifbBuffer;
      ifb_arguments.intersection_function_buffer_size = raytracingResources.ifbBufferSize;
      ifb_arguments.intersection_function_stride = raytracingResources.ifbBufferStride;
      
      // Set up the ray and finish intersecting
      metal::raytracing::ray r = { origin, direction };
      auto result = trace.intersect(r, ads, ifb_arguments);
    • 13:02 - Change of temporal scaler setup to denoised temporal scaler setup

      // Change of temporal scaler setup to denoised temporal scaler setup
      
      MTLFXTemporalScalerDescriptor* desc = [MTLFXTemporalScalerDescriptor new];
      desc.colorTextureFormat = MTLPixelFormatBGRA8Unorm_sRGB;
      desc.outputTextureFormat = MTLPixelFormatBGRA8Unorm_sRGB;
      desc.depthTextureFormat = DepthStencilFormat;
      desc.motionTextureFormat = MotionVectorFormat;
      
      desc.diffuseAlbedoTextureFormat = DiffuseAlbedoFormat;
      desc.specularAlbedoTextureFormat = SpecularAlbedoFormat;
      desc.normalTextureFormat = NormalVectorFormat;
      desc.roughnessTextureFormat = RoughnessFormat;
      
      desc.inputWidth = _mainViewWidth;
      desc.inputHeight = _mainViewHeight;
      desc.outputWidth = _screenWidth;
      desc.outputHeight = _screenHeight;
      temporalScaler = [desc newTemporalDenoisedScalerWithDevice:_device];
    • 13:04 - Change temporal scaler encode to denoiser temporal scaler encode

      // Change temporal scaler encode to denoiser temporal scaler encode
      
      temporalScaler.colorTexture = _mainView;
      temporalScaler.motionTexture = _motionTexture;
      
      temporalScaler.diffuseAlbedoTexture = _diffuseAlbedoTexture;
      temporalScaler.specularAlbedoTexture = _specularAlbedoTexture;
      temporalScaler.normalTexture = _normalTexture;
      temporalScaler.roughnessTexture = _roughnessTexture;
      
      temporalScaler.depthTexture = _depthTexture;
      temporalScaler.jitterOffsetX = _pixelJitter.x;
      temporalScaler.jitterOffsetY = -_pixelJitter.y;
      temporalScaler.outputTexture = _upscaledColorTarget;
      temporalScaler.motionVectorScaleX = (float)_motionTexture.width;
      temporalScaler.motionVectorScaleY = (float)_motionTexture.height;
      [temporalScaler encodeToCommandBuffer:commandBuffer];
    • 16:04 - Creating instance descriptors for instance acceleration structure

      // Creating instance descriptors for instance acceleration structure
      MTLAccelerationStructureInstanceDescriptor *grassInstanceDesc, *treeInstanceDesc = . . .;
      grassInstanceDesc.intersectionFunctionTableOffset = 0;
      treeInstanceDesc.intersectionFunctionTableOffset  = 1;
      
      // Create buffer for instance descriptors of as many trees/grass instances the scene holds
      id <MTLBuffer> instanceDescs = . . .;
      for (auto i = 0; i < scene.instances.size(); ++i)
      . . .
    • 0:00 - Introducción
    • Obtén información sobre la serie de juegos Metal 4, que incluye técnicas avanzadas y prácticas recomendadas para desarrollar juegos de alto nivel y apps profesionales en las plataformas de Apple. Las API de Metal 4 te permiten escalar tus cargas de trabajo para lograr resoluciones y frecuencias de fotogramas más altas en todos los dispositivos Apple.

    • 2:13 - Mejorar renderizados
    • MetalFX tiene un escalador basado en aprendizaje automático que puede ayudarte a lograr resoluciones y frecuencias de fotogramas más altas. Como novedad este año, el escalador temporal de MetalFX admite entradas de tamaño dinámico para que puedas reducir dinámicamente la resolución de entrada en fotogramas de especial complejidad. De manera opcional, puedes usar las sugerencias de reactividad para indicarle al escalador la reactividad de los píxeles y así obtener resultados más nítidos en áreas con efectos o partículas transparentes. Además, puedes verificar que el valor de exposición que pasas al escalador sea correcto con la nueva herramienta de depuración de exposición.

    • 7:17 - Interpolar fotogramas
    • Como novedad este año, MetalFX Frame Interpolation genera un fotograma adicional entre los dos fotogramas renderizados. Existen varias técnicas para renderizar la interfaz de usuario con Frame Interpolation. También hay consideraciones que debes tener en cuenta al definir el ritmo y la presentación de fotogramas interpolados y aquellos renderizados de forma nativa.

    • 13:50 - Trazar rayos con Metal 4
    • Metal 4 incorpora varias funciones nuevas de trazado de rayos relacionadas con la creación de estructuras de aceleración y las funciones de intersección. Si migras desde otras API, es fácil portar tus tablas de enlace de sombreadores a los búferes de funciones de intersección de Metal. Una vez que tengas todo en marcha, también puedes optimizar cómo Metal 4 crea tus estructuras de aceleración.

    • 19:25 - Eliminar ruido y mejorar calidad
    • Al realizar el trazado de rayos en escenas, puedes lograr el mejor equilibrio entre calidad y rendimiento con la reducción de ruido con menos rayos lanzados. La nueva API MetalFX mejora los procesos de renderizado de trazado de rayos en tiempo real integrando la reducción de ruido directamente en el proceso de aumento de escala. Esto simplifica el enfoque tradicional, que supone pasos separados para el trazado, la reducción de ruido y la composición. Al especificar búferes auxiliares libres de ruido, como normales, albedo difuso, rugosidad y albedo especular, puedes obtener resultados robustos, de alto rendimiento y de alta calidad sin la necesidad de ajustar parámetros por separado para cada escena.

    • 26:08 - Próximos pasos
    • Si ya integraste MetalFX Upscaler, esta es tu oportunidad de actualizar a la interpolación de fotogramas. Si aún no utilizas MetalFX, primero familiarízate con el escalador. A continuación, asegúrate de que tus efectos de trazado de rayos utilicen búferes de función de intersección y el escalador con reducción de ruido.

Developer Footer

  • Videos
  • WWDC25
  • Ve más allá con los juegos de Metal 4
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • Apple Intelligence
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Downloads
    • Sample Code
    • Videos
    Open Menu Close Menu
    • Help Guides & Articles
    • Contact Us
    • Forums
    • Feedback & 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
    • Mini Apps Partner 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
    Read the latest news.
    Get the Apple Developer app.
    Copyright © 2026 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines