In questo tutorial, imparerai come creare una scena 3D di base in SceneKit senza le complessità di OpenGL. Ciò include geometria di base, telecamere, luci, materiali e ombre.
Il framework SceneKit è stato lanciato per la prima volta da Apple insieme a OS X 10.8 Mountain Lion e successivamente è stato reso disponibile su iOS con il rilascio di iOS 8. Lo scopo di questo framework è di consentire agli sviluppatori di integrare facilmente la grafica 3D in giochi e applicazioni senza le complessità di API grafiche, come OpenGL e Metal.
SceneKit ti permette semplicemente di fornire una descrizione delle risorse che vuoi nella tua scena, con il framework stesso che gestisce tutto il codice di rendering OpenGL per te. In questo primo tutorial, ti insegnerò alcuni dei fondamenti del lavoro con le risorse 3D e le basi del framework SceneKit.
Questo tutorial richiede che tu stia utilizzando Xcode 6 o versioni successive. Sebbene non sia necessario, ti consiglio di utilizzare un dispositivo fisico con iOS 8 per testare il codice di SceneKit. Puoi utilizzare il simulatore iOS, ma le prestazioni non sono grandi se la scena diventa più complessa. Tieni presente che il test su un dispositivo fisico iOS richiede che tu abbia un account sviluppatore iOS registrato.
La prima cosa che devi sapere su SceneKit è che le risorse, rappresentate dai nodi, sono disposte in un albero gerarchico chiamato grafico della scena. Se hai familiarità con lo sviluppo di iOS, questo albero funziona come un normale vedi la gerarchiain UIKit. Ogni scena che crei ha un singolo nodo radice su cui aggiungi i nodi successivi e che fornisce anche una base per il sistema di coordinate 3D di quella scena.
Quando aggiungi un nodo a una scena, la sua posizione viene specificata da un insieme di tre numeri, un vettore a tre componenti rappresentato dal SCNVector3
struttura nel tuo codice. Ognuna di queste tre componenti definisce la posizione del nodo sugli assi x, yez come mostrato nell'immagine sottostante.
La posizione del nodo radice della scena è definita come (0, 0, 0). Nell'immagine sopra, questa è la posizione in cui i tre assi si intersecano. La telecamera inclusa nell'immagine rappresenta la direzione predefinita in cui una telecamera punta quando viene aggiunta alla scena.
Ora che conosci alcune delle basi su come gli oggetti sono rappresentati da SceneKit, sei pronto per iniziare a scrivere del codice.
Apri Xcode e creane uno nuovo Applicazione iOS basato sul Applicazione vista singola modello. Mentre si potrebbe facilmente creare un'applicazione dal Gioco template usando SceneKit, per questo tutorial ho intenzione di mostrarvi come iniziare a lavorare con SceneKit da zero.
Entrare in un nome del prodotto, impostato linguaggio a veloce, e dispositivi a universale. Clic Il prossimo continuare.
Dopo aver creato il tuo progetto, vai a ViewController.swift e aggiungi la seguente istruzione import in alto per importare il framework di SceneKit:
importare SceneKit
Quindi, aggiungere la seguente implementazione di viewDidLoad
metodo nel ViewController
classe:
override func viewDidLoad () super.viewDidLoad () lascia sceneView = SCNView (frame: self.view.frame) self.view.addSubview (sceneView)
Nel viewDidLoad
metodo, creiamo a SCNView
oggetto, passando nella cornice della vista del controller della vista. Assegniamo il SCNView
istanza a una costante, sceneView
, e aggiungilo come una sottoview della vista del controller della vista.
Il SCNView
la classe è una sottoclasse di UIView
e fornisce uno sbocco per il contenuto di SceneKit. Oltre ad avere la funzionalità di una vista normale, a SCNView
ha anche diverse proprietà e metodi relativi al contenuto di SceneKit.
Per verificare che tutto funzioni correttamente, crea ed esegui la tua app. Vedrai che hai solo una vista bianca vuota.
Per rendere il contenuto in un SCNView
, devi prima creare un SCNScene
e assegnarlo alla vista. In questa scena, devi quindi aggiungere un macchina fotografica e almeno una luce. Per questo esempio, aggiungerai anche un cubo per renderizzare SceneKit. Aggiungi il seguente codice al viewDidLoad
metodo:
override func viewDidLoad () super.viewDidLoad () lascia scenaView = SCNView (frame: self.view.frame) self.view.addSubview (sceneView) lascia scena = SCNScene () scenaView.scene = scena lascia camera = SCNCamera () let cameraNode = SCNNode () cameraNode.camera = camera cameraNode.position = SCNVector3 (x: 0.0, y: 0.0, z: 3.0) lascia luce = SCNLight () light.type = SCNLightTypeOmni lascia lightNode = SCNNode () lightNode.light = light lightNode.position = SCNVector3 (x: 1.5, y: 1.5, z: 1.5) lascia cubeGeometry = SCNBox (larghezza: 1.0, altezza: 1.0, lunghezza: 1.0, chamferRadius: 0.0) let cubeNode = SCNNode (geometry: cubeGeometry) scena. rootNode.addChildNode (lightNode) scene.rootNode.addChildNode (cameraNode) scene.rootNode.addChildNode (cubeNode)
Passiamo al viewDidLoad
metodo passo dopo passo:
dentro
metodo. A meno che non stiate caricando una scena preparata da un file esterno, questo è l'inizializzatore che userete sempre.SCNCamera
oggetto e un SCNNode
istanza per la fotocamera. Quindi assegni il SCNCamera
oggetto al telecamera
proprietà di cameraNode
e sposta questo nodo lungo l'asse z per vedere il cubo che creerai un po 'più tardi.SCNLight
oggetto e a SCNNode
di nome lightNode
. Il SCNLight
l'istanza è assegnata al luce
proprietà del nodo luminoso. Il genere
proprietà del SCNLight
è impostato per SCNLightTypeOmni
. Questo tipo di luce distribuisce la luce in modo uniforme in tutte le direzioni da un punto nello spazio 3D. Puoi pensare a questo tipo di luce come una normale lampadina.SCNBox
classe, rendendo larghezza, altezza e lunghezza tutte uguali. Il SCNBox
la classe è una sottoclasse di SCNGeometry
ed è una delle forme primitive che puoi creare. Altre forme includono sfere, piramidi e tori. Crei anche un nodo che passa nel cubo per il geometria
parametro.SCNScene
l'oggetto rileva automaticamente quando un nodo contiene una telecamera o un oggetto leggero, rendendo la scena di conseguenza.Crea ed esegui la tua app e vedrai che ora un cubo nero viene illuminato dalla luce dall'angolo in alto a destra.
Sfortunatamente, il cubo non sembra tridimensionale al momento. Questo perché la fotocamera è posizionata direttamente di fronte ad essa. Quello che stai per fare ora è cambiare la posizione della telecamera in modo che abbia una visione migliore del cubo.
Per mantenere la fotocamera puntata direttamente sul cubo, tuttavia, aggiungerai anche un SCNLookAtConstraint
alla fotocamera. Inizia aggiornando la posizione della fotocamera come mostrato di seguito.
cameraNode.position = SCNVector3 (x: -3.0, y: 3.0, z: 3.0)
Successivamente, aggiungi il seguente frammento di codice al metodo viewDidLoad, dopo aver istanziato il nodo per il cubo:
let constraint = SCNLookAtConstraint (target: cubeNode) constraint.gimbalLockEnabled = true cameraNode.constraints = [vincolo]
La modifica della posizione sposta la telecamera verso sinistra e verso l'alto. Aggiungendo un vincolo, con il cubo come bersaglio e gimbalLockEnabled
impostato vero
, ti assicuri che la fotocamera rimarrà parallela all'orizzonte e al viewport, in questo caso lo schermo del tuo dispositivo. Ciò avviene disabilitando la rotazione lungo l'asse del rullo, l'asse che punta dalla telecamera al bersaglio del vincolo.
Crea ed esegui nuovamente la tua app e vedrai il tuo cubo in tutta la sua gloria 3D.
È tempo di aggiungere più realismo alla scena con materiali e ombre. Per prima cosa avrai bisogno di un altro oggetto su cui gettare un'ombra. Utilizza il seguente frammento di codice per creare un piano, un rettangolo piatto e posizionarlo sotto il cubo. Non dimenticare di aggiungere il nuovo nodo come nodo figlio al nodo radice della scena.
override func viewDidLoad () ... let cubeGeometry = SCNBox (width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0) let cubeNode = SCNNode (geometry: cubeGeometry) lascia planeGeometry = SCNPlane (width: 50.0, height: 50.0) let planeNode = SCNNode (geometry: planeGeometry) planeNode.eulerAngles = SCNVector3 (x: GLKMathDegreesToRadians (-90), y: 0, z: 0) planeNode.position = SCNVector3 (x: 0, y: -0.5, z: 0) ... scene.rootNode.addChildNode (lightNode) scene.rootNode.addChildNode (cameraNode) scene.rootNode.addChildNode (cubeNode) scene.rootNode.addChildNode (planeNode)
Modificando il nodo piano eulerAngles
proprietà, si ruota l'aereo all'indietro di 90 gradi lungo l'asse x. Dobbiamo fare questo, perché gli aerei sono creati verticalmente per impostazione predefinita. In SceneKit, gli angoli di rotazione sono calcolati in radianti piuttosto che in gradi, ma questi valori possono essere facilmente convertiti usando il GLKMathDegreesToRadians (_ :)
e GLKMathsRadiansToDegrees (_ :)
funzioni. GLK è l'acronimo di GLKit, il framework OpenGL di Apple.
Quindi, aggiungi un materiale al cubo e al piano. Per questo esempio, assegnerai al cubo e al piano un colore uniforme, rispettivamente rosso e verde. Aggiungi le seguenti linee al viewDidLoad
metodo per creare questi materiali.
override func viewDidLoad () ... planeNode.position = SCNVector3 (x: 0, y: -0.5, z: 0) let redMaterial = SCNMaterial () redMaterial.diffuse.contents = UIColor.redColor () cubeGeometry.materials = [redMaterial] let greenMaterial = SCNMaterial () greenMaterial.diffuse.contents = UIColor.greenColor () planeGeometry.materials = [greenMaterial] let constraint = SCNLookAtConstraint (target: cubeNode) ...
Per ciascuno SCNMaterial
oggetto, si assegnano i suoi contenuti diffusi a UIColor
valore. La proprietà diffusa di un materiale determina come appare quando è sotto la luce diretta. Si noti che il valore assegnato non deve essere a UIColor
oggetto. Esistono molti altri tipi di oggetti accettabili da assegnare a questa proprietà, ad esempio UIImage
, CALayer
, e persino una texture SpriteKit (SKTexture
).
Crea ed esegui di nuovo la tua app non solo per vedere l'aereo per la prima volta, ma anche i materiali che hai creato.
Ora è il momento di aggiungere alcune ombre alla scena. Dei quattro tipi di luce disponibili in SceneKit, solo le luci spot possono creare ombre. Per questo esempio, trasformerai la tua luce omni esistente in una luce spot, mirata verso il cubo. Aggiungi il seguente codice al viewDidLoad
metodo:
override func viewDidLoad () ... let light = SCNLight () light.type = SCNLightTypeSpot light.spotInnerAngle = 30.0 light.spotOuterAngle = 80.0 light.castsShadow = true let lightNode = SCNNode () lightNode.light = light lightNode.position = SCNVector3 ( x: 1.5, y: 1.5, z: 1.5) ... let constraint = SCNLookAtConstraint (target: cubeNode) constraint.gimbalLockEnabled = true cameraNode.constraints = [constraint] lightNode.constraints = [vincolo] ...
Per creare la luce spot, devi prima impostare il tipo di luce su SCNLightTypeSpot
. Quindi si specifica l'angolo interno ed esterno della luce spot in gradi. I valori predefiniti sono 0 e 45 rispettivamente. L'angolo interno determina quanta area la luce copre in luce diretta mentre l'angolo esterno decide quanta area è parzialmente illuminata. La differenza tra questi angoli diventerà chiara quando vedrai la scena risultante. Dici quindi esplicitamente alla luce per proiettare le ombre e aggiungi anche le stesse SCNLookAtConstraint
che hai creato per la tua fotocamera in precedenza.
Crea ed esegui la tua app per vedere la scena risultante. L'angolo interno specificato nel codice è mostrato dove il piano è un verde fisso, direttamente sotto il cubo. L'angolo esterno è mostrato dal gradiente di luce che sfuma verso il nero mentre si allontana dal bersaglio della luce.
Vedrai che ora il tuo cubo ha lanciato un'ombra correttamente. La luce spot, tuttavia, illumina solo una parte dell'aereo. Questo perché non c'è luce ambientale nella scena.
Una luce ambientale è una fonte di luce che illumina tutto con un'equa distribuzione della luce. Poiché una luce ambientale illumina l'intera scena, la sua posizione non ha importanza e puoi aggiungerla a qualsiasi nodo tu voglia, anche lo stesso nodo della tua fotocamera. Utilizza il seguente frammento di codice per creare una luce ambientale per la scena.
override func viewDidLoad () ... let camera = SCNCamera () let cameraNode = SCNNode () cameraNode.camera = camera cameraNode.position = SCNVector3 (x: -3.0, y: 3.0, z: 3.0) lascia AmbientLight = SCNLight () ambientLight .type = SCNLightTypeAmbient ambientLight.color = UIColor (rosso: 0.2, verde: 0.2, blu: 0.2, alpha: 1.0) cameraNode.light = ambientLight ...
Lo snippet di codice crea un SCNLight
, proprio come hai fatto prima. La principale differenza è la luce genere
proprietà, che è impostata su SCNLightTypeAmbient
. Puoi anche impostare il suo colore su un grigio scuro in modo da non sopraffare la scena. Il colore predefinito per una luce è bianco puro (valore RGB di 1, 1, 1) e avere questo colore su una luce ambientale fa sì che l'intera scena sia completamente illuminata come mostrato nello screenshot qui sotto.
Crea ed esegui l'app un'ultima volta per vedere il risultato finale.
Se sei arrivato alla fine di questo tutorial, ora dovresti essere a tuo agio con i seguenti argomenti:
SCNView
con un SCNScene
Nel prossimo tutorial di questa serie, imparerai alcuni concetti più avanzati del framework SceneKit, tra cui animazione, interazione con l'utente, sistemi di particelle e simulazione di fisica.