# visionOS 2: Spatial Computing ile Production App Geliştirme 2026
Apple Vision Pro ve visionOS 2, spatial computing'i gerçek bir geliştirici platformuna dönüştürdü. İlk nesil visionOS'un bazı kısıtlamaları ve "deneysel" hissiyatı, visionOS 2 ile yerini olgun, production-ready bir API setine bıraktı. Bu rehberde, gerçek bir visionOS 2 uygulaması geliştirirken öğrendiğim her şeyi paylaşıyorum: Environment API'den hand tracking'e, RealityKit 4'ten Metal 4 shader optimizasyonuna kadar.
💡 Pro Tip: visionOS 2 geliştirirken en büyük tuzak, spatial computing'i ekran tabanlı düşünmek. "Bu pencere nerede görünecek?" yerine "Kullanıcı bu nesneyle nasıl etkileşecek?" diye düşünmeye başladığınızda, uygulamanızın kalitesi dramatik biçimde yükseliyor.
İçindekiler
- visionOS 2 Mimarisi: Window Types
- Environment API ve Persistent Anchors
- ARKit 7 Hand Tracking (27-Joint Detection)
- Gaze Tracking ve Privacy Layer
- RealityKit 4 Component Sistemi
- ImmersiveSpace Lifecycle
- Metal 4 Compute Shaders
- USDZ ile 3D Content Pipeline
- Spatial Gesture Recognizers
- Physics Simulation
- Custom Shaders ile MaterialX
- Polygon Budget ve Optimizasyon
1. visionOS 2 Mimarisi: Window Types
visionOS 2'de üç temel window tipi var ve bunların doğru kullanımı, uygulamanızın deneyim kalitesini doğrudan belirliyor.
WindowGroup (2D Windows): Geleneksel iOS/macOS tarzı pencereler. Kullanıcı bunları uzayda istediği yere yerleştirebiliyor. Birden fazla WindowGroup aynı anda açık olabiliyor.
Volumetric Windows: 3D içerik için özel pencere tipi. Pencere boyutlarını siz belirleyebiliyorsunuz ancak kullanıcı pencereyi döndürebiliyor. Shared space'de çalışıyor, yani diğer uygulamalarla aynı anda görünebiliyor.
ImmersiveSpace: Tam immersive deneyim. Kullanıcının tüm alanı size ait oluyor. Shared space ve full space olmak üzere iki modu var.
swift
1import SwiftUI2import RealityKit3 4@main5struct SpatialApp: App {6 var body: some Scene {7 // Standard 2D pencere8 WindowGroup {9 ContentView()10 }11 12 // 3D volumetric pencere13 WindowGroup(id: "3D-Model-Viewer") {14 ModelViewerView()15 }16 .windowStyle(.volumetric)17 .defaultSize(width: 0.8, height: 0.8, depth: 0.4, in: .meters)18 19 // Immersive deneyim20 ImmersiveSpace(id: "Main-Immersive") {21 ImmersiveView()22 }23 .immersionStyle(24 selection: .constant(.mixed),25 in: .mixed, .progressive, .full26 )27 }28}29 30// Immersive space'e geçiş31struct ContentView: View {32 @Environment(\.openImmersiveSpace) private var openImmersiveSpace33 @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace34 @Environment(\.openWindow) private var openWindow35 36 @State private var immersiveSpaceIsShown = false37 38 var body: some View {39 NavigationStack {40 VStack(spacing: 20) {41 Button("3D Model Görüntüle") {42 openWindow(id: "3D-Model-Viewer")43 }44 .buttonStyle(.bordered)45 46 Button(immersiveSpaceIsShown ? "Çık" : "Immersive Moda Geç") {47 Task {48 if immersiveSpaceIsShown {49 await dismissImmersiveSpace()50 immersiveSpaceIsShown = false51 } else {52 let result = await openImmersiveSpace(id: "Main-Immersive")53 immersiveSpaceIsShown = result == .opened54 }55 }56 }57 .buttonStyle(.borderedProminent)58 }59 .navigationTitle("Spatial App")60 .padding()61 }62 }63}2. Environment API ve Persistent Anchors
visionOS 2'nin en güçlü yeni özelliği Environment API. Bu API, uygulamanızın gerçek dünya yüzeylerini (duvar, zemin, masa) algılamasını ve nesneleri bu yüzeylere kalıcı olarak "sabitlemesini" sağlıyor.
swift
1import ARKit2import RealityKit3import SwiftUI4 5class EnvironmentTracker: ObservableObject {6 private let arSession = ARKitSession()7 private let planeDetection = PlaneDetectionProvider(8 alignments: [.horizontal, .vertical]9 )10 private let sceneReconstruction = SceneReconstructionProvider(11 modes: [.classification]12 )13 14 @Published var detectedPlanes: [PlaneAnchor] = []15 @Published var meshAnchors: [MeshAnchor] = []16 17 func startTracking() async {18 guard PlaneDetectionProvider.isSupported,19 SceneReconstructionProvider.isSupported else {20 print("Environment API not supported")21 return22 }23 24 do {25 try await arSession.run([planeDetection, sceneReconstruction])26 } catch {27 print("ARKit session error: \(error)")28 return29 }30 31 // Plane detection updates32 for await update in planeDetection.anchorUpdates {33 await handlePlaneUpdate(update)34 }35 }36 37 @MainActor38 private func handlePlaneUpdate(_ update: AnchorUpdate<PlaneAnchor>) {39 switch update.event {40 case .added:41 detectedPlanes.append(update.anchor)42 case .updated:43 if let index = detectedPlanes.firstIndex(44 where: { $0.id == update.anchor.id }45 ) {46 detectedPlanes[index] = update.anchor47 }48 case .removed:49 detectedPlanes.removeAll { $0.id == update.anchor.id }50 }51 }52}53 54// Persistent anchor: Nesneleri gerçek dünyaya sabitleme55class PersistentAnchorManager {56 private let worldTracking = WorldTrackingProvider()57 private let arSession = ARKitSession()58 59 // visionOS 2: Persistent anchor kaydetme60 func saveAnchor(61 entity: Entity,62 in realityView: RealityView<some View>63 ) async throws -> UUID {64 // WorldAnchor oluştur65 var anchor = WorldAnchor(66 originFromAnchorTransform: entity.transformMatrix(67 relativeTo: nil68 )69 )70 71 // Anchor"ı kaydet72 try await worldTracking.queryDeviceAnchor(atTimestamp: CACurrentMediaTime())73 74 return anchor.id75 }76 77 // Kaydedilmiş anchor"ı geri yükle78 func loadSavedAnchor(id: UUID) async -> WorldAnchor? {79 for await update in worldTracking.anchorUpdates {80 if update.anchor.id == id {81 return update.anchor82 }83 }84 return nil85 }86}Persistent anchors, kullanıcının bir nesneyi masasına veya duvarına yerleştirip uygulamayı kapatıp açtığında nesnenin aynı yerde durmasını sağlıyor. Bu özellik, productivity uygulamaları için game-changing bir UX sunuyor.
3. ARKit 7 Hand Tracking (27-Joint Detection)
visionOS 2, ARKit 7 ile birlikte 27-noktalı hand tracking sunuyor. Her el için 27 joint noktasının pozisyon ve rotasyon verisi gerçek zamanlı alınabiliyor. Bu, önceki versiyonlara kıyasla çok daha doğru ve ayrıntılı gesture recognition imkânı sağlıyor.
swift
1import ARKit2import simd3 4class HandTracker: ObservableObject {5 private let handTracking = HandTrackingProvider()6 private let arSession = ARKitSession()7 8 @Published var leftHand: HandSkeleton?9 @Published var rightHand: HandSkeleton?10 @Published var detectedGesture: CustomGesture = .none11 12 enum CustomGesture {13 case none14 case pinch15 case openPalm16 case pointingUp17 case fist18 case thumbsUp19 }20 21 func startTracking() async {22 guard HandTrackingProvider.isSupported else {23 print("Hand tracking not supported")24 return25 }26 27 do {28 try await arSession.run([handTracking])29 } catch {30 print("Hand tracking error: \(error)")31 return32 }33 34 for await update in handTracking.anchorUpdates {35 await handleHandUpdate(update)36 }37 }38 39 @MainActor40 private func handleHandUpdate(_ update: AnchorUpdate<HandAnchor>) {41 let hand = update.anchor42 43 switch hand.chirality {44 case .left:45 leftHand = hand.handSkeleton46 case .right:47 rightHand = hand.handSkeleton48 if let skeleton = hand.handSkeleton {49 detectedGesture = detectGesture(skeleton: skeleton)50 }51 }52 }53 54 // 27-joint data ile custom gesture detection55 private func detectGesture(skeleton: HandSkeleton) -> CustomGesture {56 // Parmak joint'lerini al57 let thumbTip = skeleton.joint(.thumbTip)58 let indexTip = skeleton.joint(.indexFingerTip)59 let middleTip = skeleton.joint(.middleFingerTip)60 let ringTip = skeleton.joint(.ringFingerTip)61 let littleTip = skeleton.joint(.littleFingerTip)62 63 let thumbKnuckle = skeleton.joint(.thumbKnuckle)64 let indexKnuckle = skeleton.joint(.indexFingerKnuckle)65 66 guard thumbTip.isTracked, indexTip.isTracked else {67 return .none68 }69 70 // Pinch detection: thumb ve index arası mesafe71 let thumbPos = simd_float3(thumbTip.anchorFromJointTransform.columns.3.x,72 thumbTip.anchorFromJointTransform.columns.3.y,73 thumbTip.anchorFromJointTransform.columns.3.z)74 let indexPos = simd_float3(indexTip.anchorFromJointTransform.columns.3.x,75 indexTip.anchorFromJointTransform.columns.3.y,76 indexTip.anchorFromJointTransform.columns.3.z)77 78 let pinchDistance = simd_distance(thumbPos, indexPos)79 80 if pinchDistance < 0.02 { // 2cm81 return .pinch82 }83 84 // Thumbs up detection85 let thumbTipPos = simd_float3(86 thumbTip.anchorFromJointTransform.columns.3.x,87 thumbTip.anchorFromJointTransform.columns.3.y,88 thumbTip.anchorFromJointTransform.columns.3.z89 )90 let thumbKnucklePos = simd_float3(91 thumbKnuckle.anchorFromJointTransform.columns.3.x,92 thumbKnuckle.anchorFromJointTransform.columns.3.y,93 thumbKnuckle.anchorFromJointTransform.columns.3.z94 )95 96 let thumbDirection = simd_normalize(thumbTipPos - thumbKnucklePos)97 if thumbDirection.y > 0.8 && pinchDistance > 0.05 {98 return .thumbsUp99 }100 101 // Open palm: tüm parmaklar uzanmış102 let allExtended = [indexTip, middleTip, ringTip, littleTip].allSatisfy {103 $0.isTracked104 }105 106 if allExtended && pinchDistance > 0.06 {107 return .openPalm108 }109 110 return .none111 }112}113 114// Custom gesture ile RealityKit etkileşimi115struct HandTrackingRealityView: View {116 @StateObject private var handTracker = HandTracker()117 @State private var selectedEntity: Entity?118 119 var body: some View {120 RealityView { content in121 // 3D model yükle122 if let model = try? await Entity(123 named: "robot",124 in: .main125 ) {126 model.name = "robot"127 model.position = [0, 0, -0.5]128 content.add(model)129 }130 }131 .task {132 await handTracker.startTracking()133 }134 .onChange(of: handTracker.detectedGesture) { _, gesture in135 handleGesture(gesture)136 }137 }138 139 private func handleGesture(_ gesture: HandTracker.CustomGesture) {140 switch gesture {141 case .pinch:142 // Nesneleri pinch ile tutma143 print("Pinch detected — grab object")144 case .thumbsUp:145 // Onay aksiyonu146 print("Thumbs up — confirm")147 case .openPalm:148 // Menu aç149 print("Open palm — show menu")150 default:151 break152 }153 }154}27-joint hand tracking ile pratik olarak el hareketlerinin tamamını algılayabiliyorsunuz. Önemli not: Hand tracking, Persona özelliğiyle çakışıyor. Kullanıcı Persona aktifken hand tracking verisi kısıtlanıyor, bu yüzden uygulamanız Persona modunda da çalışacak şekilde graceful degradation planlamalısınız.
4. Gaze Tracking ve Privacy Layer
visionOS 2, gaze tracking için kullanıcı gizliliğini ön plana alan bir API sunuyor. Kullanıcının tam olarak nereye baktığını değil, neyle etkileşime girebileceğini belirlemenizi sağlayan bir soyutlama katmanı var.
Önemli: visionOS'ta eye tracking verisi doğrudan uygulamalara verilmiyor. Sistem, kullanıcının hangi UI elementine baktığını otomatik olarak "hover" state'ine çeviriyor. Bu privacy-first yaklaşım, uygulamaların hassas göz hareketi verisi toplamasını engelliyor.
Uygulamanız için gaze-based interaction şu şekilde çalışıyor: SwiftUI elementleri için hoverEffect() modifier yeterli. RealityKit entityleri için InputTargetComponent ve HoverEffectComponent kullanılıyor.
swift
1import RealityKit2import SwiftUI3 4// RealityKit entity"inde hover ve gaze interaction5struct GazeInteractiveView: View {6 var body: some View {7 RealityView { content in8 // Interaktif küre oluştur9 let sphere = ModelEntity(10 mesh: .generateSphere(radius: 0.1),11 materials: [SimpleMaterial(12 color: .systemBlue,13 isMetallic: false14 )]15 )16 17 // visionOS 2: Input target ve collision ekle18 sphere.components.set(InputTargetComponent())19 sphere.components.set(CollisionComponent(20 shapes: [.generateSphere(radius: 0.1)]21 ))22 23 // Hover effect — gaze ile etkileşim24 sphere.components.set(HoverEffectComponent(25 .highlight(.init(26 color: .white,27 strength: 3.028 ))29 ))30 31 sphere.position = [0, 1.5, -0.5]32 content.add(sphere)33 }34 .gesture(35 SpatialTapGesture()36 .targetedToAnyEntity()37 .onEnded { value in38 handleTap(on: value.entity)39 }40 )41 }42 43 private func handleTap(on entity: Entity) {44 // Tap animasyonu45 var transform = entity.transform46 transform.scale = SIMD3<Float>(1.2, 1.2, 1.2)47 48 entity.move(49 to: transform,50 relativeTo: entity.parent,51 duration: 0.1,52 timingFunction: .easeOut53 )54 55 // Geri küçül56 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {57 var originalTransform = entity.transform58 originalTransform.scale = SIMD3<Float>(1, 1, 1)59 entity.move(60 to: originalTransform,61 relativeTo: entity.parent,62 duration: 0.1,63 timingFunction: .easeIn64 )65 }66 }67}5. RealityKit 4 Component Sistemi
RealityKit 4, Entity-Component-System (ECS) mimarisini daha da güçlendirdi. Custom component'lar ve sistem tanımlamak artık çok daha temiz.
swift
1import RealityKit2 3// Custom component tanımlama4struct RotationComponent: Component {5 var speed: Float = 1.06 var axis: SIMD3<Float> = [0, 1, 0]7 var isRotating: Bool = true8}9 10struct FloatingComponent: Component {11 var amplitude: Float = 0.0512 var frequency: Float = 1.013 var startHeight: Float = 014 var timeOffset: Float = 015}16 17struct GlowComponent: Component {18 var color: SIMD3<Float> = [1, 0.8, 0.2]19 var intensity: Float = 2.020 var pulseSpeed: Float = 1.521}22 23// RealityKit 4: System tanımlama24class RotationSystem: System {25 static let query = EntityQuery(26 where: .has(RotationComponent.self)27 )28 29 required init(scene: Scene) {}30 31 func update(context: SceneUpdateContext) {32 for entity in context.entities(33 matching: Self.query,34 updatingSystemWhen: .rendering35 ) {36 guard var rotation = entity.components[RotationComponent.self],37 rotation.isRotating else { continue }38 39 let angle = rotation.speed * Float(context.deltaTime)40 let rotationQuaternion = simd_quatf(41 angle: angle,42 axis: rotation.axis43 )44 entity.orientation = entity.orientation * rotationQuaternion45 }46 }47}48 49class FloatingSystem: System {50 static let query = EntityQuery(51 where: .has(FloatingComponent.self)52 )53 54 required init(scene: Scene) {}55 56 func update(context: SceneUpdateContext) {57 let time = Float(Date().timeIntervalSince1970)58 59 for entity in context.entities(60 matching: Self.query,61 updatingSystemWhen: .rendering62 ) {63 guard var floating = entity.components[FloatingComponent.self] else { continue }64 65 let yOffset = floating.amplitude * sin(66 floating.frequency * time + floating.timeOffset67 )68 entity.position.y = floating.startHeight + yOffset69 }70 }71}72 73// Entity factory: Component'ları bir araya getir74class SpatialEntityFactory {75 static func createFloatingOrb(76 at position: SIMD3<Float>,77 color: UIColor = .systemCyan78 ) -> ModelEntity {79 let orb = ModelEntity(80 mesh: .generateSphere(radius: 0.08),81 materials: [82 PhysicallyBasedMaterial.makeEmissive(color: color, intensity: 2.0)83 ]84 )85 86 orb.position = position87 88 // Component'ları ekle89 orb.components.set(RotationComponent(speed: 0.5))90 orb.components.set(FloatingComponent(91 amplitude: 0.03,92 frequency: 0.8,93 startHeight: position.y,94 timeOffset: Float.random(in: 0...Float.pi * 2)95 ))96 orb.components.set(InputTargetComponent())97 orb.components.set(CollisionComponent(98 shapes: [.generateSphere(radius: 0.08)]99 ))100 orb.components.set(HoverEffectComponent())101 102 return orb103 }104}105 106extension PhysicallyBasedMaterial {107 static func makeEmissive(108 color: UIColor,109 intensity: Float110 ) -> PhysicallyBasedMaterial {111 var material = PhysicallyBasedMaterial()112 material.baseColor = .init(tint: color)113 material.emissiveColor = .init(color: color)114 material.emissiveIntensity = intensity115 material.roughness = .init(floatLiteral: 0.1)116 material.metallic = .init(floatLiteral: 0.0)117 return material118 }119}6. ImmersiveSpace Lifecycle
ImmersiveSpace lifecycle yönetimi, production uygulamalar için kritik. Kullanıcı başka uygulamaya geçtiğinde, telefon geldiğinde veya notification aldığında immersive space nasıl davranmalı?
swift
1import SwiftUI2import RealityKit3 4struct ImmersiveView: View {5 @Environment(\.scenePhase) private var scenePhase6 @StateObject private var sceneManager = ImmersiveSceneManager()7 8 var body: some View {9 RealityView { content, attachments in10 await sceneManager.setupScene(content: content)11 } update: { content, attachments in12 sceneManager.updateScene(content: content)13 } attachments: {14 // SwiftUI view'leri 3D uzaya yerleştir15 Attachment(id: "info-panel") {16 InfoPanelView(manager: sceneManager)17 .frame(width: 400, height: 300)18 }19 }20 .task {21 await sceneManager.startTracking()22 }23 // Scene phase değişimlerini handle et24 .onChange(of: scenePhase) { _, newPhase in25 switch newPhase {26 case .active:27 sceneManager.resumeExperience()28 case .inactive:29 // Telefon geldi veya notification — geçici durdur30 sceneManager.pauseExperience()31 case .background:32 // Kullanıcı başka uygulamaya geçti — tamamen durdur33 sceneManager.suspendExperience()34 @unknown default:35 break36 }37 }38 }39}40 41class ImmersiveSceneManager: ObservableObject {42 @Published var isExperienceActive = false43 @Published var frameCount = 044 45 private var updateTask: Task<Void, Never>?46 47 func setupScene(content: RealityViewContent) async {48 // Sahneyi hazırla49 isExperienceActive = true50 }51 52 func updateScene(content: RealityViewContent) {53 frameCount += 154 }55 56 func startTracking() async {57 // ARKit ve diğer tracking başlat58 }59 60 func pauseExperience() {61 // Animasyonları duraklat, tracking'i kısıtla62 isExperienceActive = false63 updateTask?.cancel()64 }65 66 func resumeExperience() {67 isExperienceActive = true68 startUpdateLoop()69 }70 71 func suspendExperience() {72 pauseExperience()73 // Kaynakları serbest bırak74 }75 76 private func startUpdateLoop() {77 updateTask = Task {78 while !Task.isCancelled && isExperienceActive {79 await Task.yield()80 }81 }82 }83}7. Metal 4 Compute Shaders
visionOS 2, Apple Silicon'un gücünü tam kullanmak için Metal 4 compute shader'larını destekliyor. Özellikle particle sistemleri ve physics hesaplamaları için GPU compute pipeline kritik.
GPU tarafında particle simülasyonu yazmak için MTLComputePipelineState ve custom Metal shader kullanılıyor. visionOS'ta Metal kullanımı iOS'tan farklı: her iki göz için ayrı render pass yönetmeniz gerekiyor. MTKView yerine CAMetalLayer veya doğrudan RealityKit'in render callback'leri üzerinden çalışmak daha performanslı.
Metal 4'ün visionOS 2'ye özgü en önemli özelliği: MTLDevice üzerinde supportsFamily(.metal4) kontrolü artık Vision Pro donanımında true dönüyor. Bu, mesh shading, ray tracing ve argument buffer tier 2 desteği anlamına geliyor.
8. USDZ ile 3D Content Pipeline
visionOS için içerik üretiminin temeli USDZ formatı. Apple"ın Reality Composer Pro aracı, USDZ asset'lerini visionOS'a özel optimize etmenizi sağlıyor.
Production USDZ pipeline:
- Kaynak format: Blender veya Maya'da FBX/OBJ olarak export
- Dönüştürme:
usdzconvertCLI aracı veya Reality Composer Pro - Texture optimizasyonu: ASTC compression (visionOS donanım desteği var)
- LOD (Level of Detail): Uzakta düşük polygon, yakında yüksek
- Material:
UsdPreviewSurfaceveyaMaterialX
USDZ asset"ınızı RealityKit'e yüklerken async loading kullanın, main thread'i bloklamayın:
swift
1import RealityKit2 3class ModelLoader {4 // Async model yükleme5 func loadModel(named name: String) async throws -> ModelEntity {6 let entity = try await ModelEntity(7 named: name,8 in: .main9 )10 11 // LOD ayarları12 if var modelComponent = entity.components[ModelComponent.self] {13 // Uzak mesafe için düşük detail14 entity.components.set(modelComponent)15 }16 17 return entity18 }19 20 // Birden fazla modeli paralel yükle21 func loadMultipleModels(names: [String]) async throws -> [ModelEntity] {22 return try await withThrowingTaskGroup(of: ModelEntity.self) { group in23 for name in names {24 group.addTask {25 try await self.loadModel(named: name)26 }27 }28 29 var models: [ModelEntity] = []30 for try await model in group {31 models.append(model)32 }33 return models34 }35 }36 37 // Texture streaming — büyük asset"ler için38 func preloadTextures(for entity: ModelEntity) async {39 // Entity"nin tüm texture"larını önceden belleğe al40 guard let modelComponent = entity.components[ModelComponent.self] else {41 return42 }43 44 // Material texture"larını iterate et45 for var material in modelComponent.materials {46 // PhysicallyBasedMaterial için texture preload47 if var pbr = material as? PhysicallyBasedMaterial {48 // BaseColor texture49 if let baseColorTexture = pbr.baseColor.texture {50 _ = baseColorTexture.resource51 }52 }53 }54 }55}9. Spatial Gesture Recognizers
visionOS 2'de gesture recognition, iOS'tan çok farklı çalışıyor. El hareketleri, göz odağı ve fiziksel dokunuş birleşerek "spatial gesture" oluşturuyor.
swift
1import SwiftUI2import RealityKit3 4struct SpatialGestureView: View {5 @State private var dragOffset: SIMD3<Float> = .zero6 @State private var scale: Float = 1.07 @State private var rotation: simd_quatf = .init(ix: 0, iy: 0, iz: 0, r: 1)8 9 var body: some View {10 RealityView { content in11 let box = ModelEntity(12 mesh: .generateBox(size: 0.2, cornerRadius: 0.02),13 materials: [SimpleMaterial(14 color: .systemIndigo,15 isMetallic: true16 )]17 )18 box.name = "main-box"19 box.components.set(InputTargetComponent())20 box.components.set(CollisionComponent(21 shapes: [.generateBox(size: [0.2, 0.2, 0.2])]22 ))23 box.components.set(HoverEffectComponent())24 content.add(box)25 }26 // Drag gesture27 .gesture(28 DragGesture()29 .targetedToAnyEntity()30 .onChanged { value in31 let translation = value.convert(32 value.gestureValue.translation3D,33 from: .local,34 to: .scene35 )36 value.entity.position = value.startTargetedEntity.position + SIMD3<Float>(translation)37 }38 )39 // Magnify (scale) gesture40 .gesture(41 MagnifyGesture()42 .targetedToAnyEntity()43 .onChanged { value in44 let newScale = Float(value.gestureValue.magnification)45 value.entity.scale = SIMD3<Float>(46 repeating: max(0.1, min(3.0, newScale))47 )48 }49 )50 // Rotate gesture51 .gesture(52 RotateGesture3D()53 .targetedToAnyEntity()54 .onChanged { value in55 let rotation = simd_quatf(value.gestureValue.rotation)56 value.entity.orientation = rotation57 }58 )59 }60}10. Physics Simulation
RealityKit 4'ün physics engine'i, visionOS 2'de gerçek zamanlı collision detection ve rigid body simulation sunuyor.
swift
1import RealityKit2 3class PhysicsSceneBuilder {4 func buildPhysicsScene(in content: RealityViewContent) {5 // Zemin düzlemi6 let floorMesh = MeshResource.generatePlane(7 width: 2,8 depth: 29 )10 let floorMaterial = SimpleMaterial(11 color: .systemGray6,12 isMetallic: false13 )14 let floorEntity = ModelEntity(15 mesh: floorMesh,16 materials: [floorMaterial]17 )18 floorEntity.position = [0, 0, -1]19 20 // Static physics body — hareket etmez ama çarpışma alır21 floorEntity.components.set(PhysicsBodyComponent(22 massProperties: .default,23 material: .generate(24 staticFriction: 0.6,25 dynamicFriction: 0.4,26 restitution: 0.327 ),28 mode: .static29 ))30 floorEntity.components.set(CollisionComponent(31 shapes: [.generateBox(size: [2, 0.01, 2])]32 ))33 content.add(floorEntity)34 35 // Dinamik objeler36 for i in 0..<5 {37 let cube = createPhysicsCube(38 at: [39 Float.random(in: -0.4...0.4),40 1.0 + Float(i) * 0.3,41 -0.842 ]43 )44 content.add(cube)45 }46 }47 48 private func createPhysicsCube(at position: SIMD3<Float>) -> ModelEntity {49 let size: Float = 0.0850 let cube = ModelEntity(51 mesh: .generateBox(size: size, cornerRadius: 0.005),52 materials: [SimpleMaterial(53 color: [.systemRed, .systemOrange, .systemYellow,54 .systemGreen, .systemBlue].randomElement()!,55 isMetallic: false56 )]57 )58 cube.position = position59 60 // Dynamic physics body — yerçekimine tabi61 cube.components.set(PhysicsBodyComponent(62 massProperties: .init(mass: 0.1),63 material: .generate(64 staticFriction: 0.5,65 dynamicFriction: 0.3,66 restitution: 0.467 ),68 mode: .dynamic69 ))70 cube.components.set(CollisionComponent(71 shapes: [.generateBox(size: [size, size, size])]72 ))73 cube.components.set(InputTargetComponent())74 75 return cube76 }77}11. Custom Shaders ile MaterialX
visionOS 2, MaterialX standardını destekliyor. Bu, Pixar gibi stüdyoların kullandığı endüstri standardı shader tanımlama formatı.
MaterialX ile custom shader yazmak, PhysicallyBasedMaterial'in ötesinde tam özelleştirilmiş görsel efektler üretmenizi sağlıyor. Reality Composer Pro'nun ShaderGraph editörü, MaterialX şemalarını görsel olarak oluşturmanıza imkân veriyor.
Production'da en çok kullanılan MaterialX pattern'leri: procedural texture generation (noise, gradient), time-based animation (pulsing, wave), fresnel effect (kenar parlaması), ve subsurface scattering (deri, wax benzeri materyaller).
12. Polygon Budget ve Optimizasyon
visionOS için polygon budget, iOS'tan çok farklı. Her iki göz için ayrı render nedeniyle GPU yükü teorik olarak iki katına çıkıyor. Pratik kural setleri:
Polygon limits (Vision Pro donanımı için):
- Statik sahne nesneleri: 50K-100K polygon/nesne
- Dinamik / animasyonlu: 20K-50K polygon/nesne
- Çok sayıda küçük nesne: 2K-10K/nesne
- Zemin/çevre: Max 500K toplam scene
- Particle system: Max 10K particle, shader basit tutun
Texture çözünürlükleri:
- Yakın nesneler: 2048x2048 (4K bile kabul edilebilir)
- Orta mesafe: 1024x1024
- Uzak nesneler: 512x512 veya daha az
- Normal map: Albedo ile aynı ya da yarısı
Draw call optimizasyonu:
- Aynı materyal kullanan nesneleri instanced rendering ile birleştirin
- LOD: 3 seviye (high, medium, low) standart kabul edilebilir
- Frustum culling RealityKit tarafından otomatik yapılıyor
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
Okuyucu Ödülü
- **Reality Composer Pro:** Asset önizleme ve MaterialX shader geliştirme için zorunlu - **Xcode visionOS Simulator:** Temel UI testi için yeterli ama fizik, hand tracking ve AOD testleri gerçek cihazda yapılmalı - **Metal GPU Frame Capture:** Draw call analizi için Instruments'tan çok daha ayrıntılı bilgi veriyor - **Reality Trace:** RealityKit update loop'unu profillemek için özel instrument, visionOS 2 SDK ile geliyor
Sonuç
visionOS 2, spatial computing geliştirmeyi gerçek anlamda olgunlaştırdı. Environment API ile persistent anchors, 27-noktalı hand tracking ve RealityKit 4'ün ECS mimarisi bir araya gelince gerçekten etkileyici deneyimler oluşturmak mümkün.
En kritik ders: Spatial computing"de kullanıcı deneyimini soyut değil, fiziksel olarak düşünün. Bir butona tıklamak yerine, bir nesneyi tutmak ve çevirmek. Bir liste kaydırmak yerine, uzayda nesneler arasında gezinmek. Bu zihniyet değişimi olmadan, visionOS uygulamaları sadece "3D'de iPad uygulaması" olmaktan öteye geçemiyor.
İlgili Yazılar
- Apple Vision Pro: 1 Yıllık Developer Deneyimi
- iOS 19 Beta: Developer Perspektifinden Yenilikler
- SwiftUI vs UIKit 2026: Yeni Proje Karar Rehberi
- Swift 6 Neler Yeni?
- watchOS 11: Workout API, Complications ve Live Activities 2026

