Blog
Swift + Playgraoundメモ 3 – SceneKitで3Dライブコーディング!
注意: Swiftのソースは全てXcode 6 Beta 6で確認しています。それ以前のバージョンでの動作は確認していません。また今後のXcodeのバージョンで動く保証もありませんので、ご了承ください。
Generative 3D with Swift Playground from Atsushi Tadokoro on Vimeo.
「Swift + Playgraoundメモ 1 – SpriteKitでアニメーション」、「Swift + Playgraoundメモ 2 – SpriteKitで物理シミュレーション」に続く、Swift + Playgroundシリーズ第3弾。今回は、3DCGに挑戦してみた。
2Dのグラフィクスの表示やアニメーションには、SprikeKitをつかったのだけれど、3Dではその代わりにSceneKitというフレームワークを使うらしい。説明を読むと、OpenGLなどと比べると、より上位のレベルで、シーンの中の物体とその動きを記述できるとのこと。早速、Swift+Playgroundの環境で使ってみたい。
まずは、SpriteKitのときと同様に、Viewを定義して、そこにSceneに追加する。3DのSceneには、SCNSceneを使用する。
import SceneKit import XCPlayground let width:CGFloat = 400 let height:CGFloat = 400 // Viewを定義 var view = SCNView(frame: CGRect(x: 0, y: 0, width: width, height: height)) // Sceneを生成して、Viewに追加 var scene = SCNScene() view.scene = scene
ここにまずは背景色と照明(ライティング)の設定をしたい。こんな感じでコードを追加。Playgroundのタイムラインでの表示のためのコードも追加している。
import SceneKit import XCPlayground let width:CGFloat = 400 let height:CGFloat = 400 // Viewを定義 var view = SCNView(frame: CGRect(x: 0, y: 0, width: width, height: height)) // Sceneを生成して、Viewに追加 var scene = SCNScene() view.scene = scene // 背景の設定 view.backgroundColor = NSColor.blackColor() // ライティング view.autoenablesDefaultLighting = true // タイムラインに表示 XCPShowView("View", view)
次にカメラを配置する。3DCGは、三次元の情報を二次元平面に投影して、あたかも奥行があるように見せているものなので、どこから物体を見るのかという「視点」の設定が必須となる。
SceneKitでは、まずカメラを生成したあと、それをシーンのノードとして登録して追加するという手順を踏むようだ。こんな感じ。
import SceneKit import XCPlayground let width:CGFloat = 400 let height:CGFloat = 400 // Viewを定義 var view = SCNView(frame: CGRect(x: 0, y: 0, width: width, height: height)) // Sceneを生成して、Viewに追加 var scene = SCNScene() view.scene = scene // 背景の設定 view.backgroundColor = NSColor.blackColor() // ライティング view.autoenablesDefaultLighting = true // カメラ var camera = SCNCamera() var cameraNode = SCNNode() cameraNode.camera = camera cameraNode.position = SCNVector3(x: 0, y: 0, z: 3) scene.rootNode.addChildNode(cameraNode)
これで、環境の準備ができた。さっそく3Dの物体を表示してみたい。まずは、シンプルに立方体を置いてみる。立体感がわかるよう配置したあと、ちょっと回転させて斜めから見るようにしている。
import SceneKit import XCPlayground let width:CGFloat = 400 let height:CGFloat = 400 // Viewを定義 var view = SCNView(frame: CGRect(x: 0, y: 0, width: width, height: height)) // Sceneを生成して、Viewに追加 var scene = SCNScene() view.scene = scene // 背景の設定 view.backgroundColor = NSColor.blackColor() // ライティング view.autoenablesDefaultLighting = true // カメラ var camera = SCNCamera() var cameraNode = SCNNode() cameraNode.camera = camera cameraNode.position = SCNVector3(x: 0, y: 0, z: 3) scene.rootNode.addChildNode(cameraNode) // 立方体のジオメトリを生成 var box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1) // ノードに追加 var boxNode = SCNNode(geometry: box) // ちょっと回転 boxNode.rotation = SCNVector4(x: 1.0, y: 1.0, z: 0.0, w: CGFloat(M_PI * 0.25)); // ノードをシーンに追加 scene.rootNode.addChildNode(boxNode) // タイムラインに表示 XCPShowView("View", view)
これで、既にリアルな物体として表示される。マテリアル(質感)はデフォルトでも既に設定されているようだ。
次にこのデフォルトのマテリアルから独自なものに変更してみる。
import SceneKit import XCPlayground let width:CGFloat = 400 let height:CGFloat = 400 // Viewを定義 var view = SCNView(frame: CGRect(x: 0, y: 0, width: width, height: height)) // Sceneを生成して、Viewに追加 var scene = SCNScene() view.scene = scene // 背景の設定 view.backgroundColor = NSColor.blackColor() // ライティング view.autoenablesDefaultLighting = true // カメラ var camera = SCNCamera() var cameraNode = SCNNode() cameraNode.camera = camera cameraNode.position = SCNVector3(x: 0, y: 0, z: 3) scene.rootNode.addChildNode(cameraNode) // 立方体のジオメトリを生成 var box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1) // ノードに追加 var boxNode = SCNNode(geometry: box) // ちょっと回転 boxNode.rotation = SCNVector4(x: 1.0, y: 1.0, z: 0.0, w: CGFloat(M_PI * 0.25)); // ノードをシーンに追加 scene.rootNode.addChildNode(boxNode) // マテリアルを変更 box.firstMaterial.diffuse.contents = NSColor.blueColor(); box.firstMaterial.specular.contents = NSColor.whiteColor(); // タイムラインに表示 XCPShowView("View", view)
最後にこの立方体にアニメーションを適用してみる。アニメーションにはCABasicAnimationクラスを使用する。いろいろな動きの定義が可能なのだけれど、ここでは、クルクルと回転させる動きにしてみることに。
import SceneKit import XCPlayground let width:CGFloat = 400 let height:CGFloat = 400 // Viewを定義 var view = SCNView(frame: CGRect(x: 0, y: 0, width: width, height: height)) // Sceneを生成して、Viewに追加 var scene = SCNScene() view.scene = scene // 背景の設定 view.backgroundColor = NSColor.blackColor() // ライティング view.autoenablesDefaultLighting = true // カメラ var camera = SCNCamera() var cameraNode = SCNNode() cameraNode.camera = camera cameraNode.position = SCNVector3(x: 0, y: 0, z: 3) scene.rootNode.addChildNode(cameraNode) // 立方体のジオメトリを生成 var box = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1) // ノードに追加 var boxNode = SCNNode(geometry: box) // ちょっと回転 boxNode.rotation = SCNVector4(x: 1.0, y: 1.0, z: 0.0, w: CGFloat(M_PI * 0.25)); // ノードをシーンに追加 scene.rootNode.addChildNode(boxNode) // マテリアルを変更 box.firstMaterial.diffuse.contents = NSColor.blueColor(); box.firstMaterial.specular.contents = NSColor.whiteColor(); // アニメーション var spin = CABasicAnimation(keyPath: "rotation") spin.toValue = NSValue(SCNVector4:SCNVector4(x: 0.5, y: 1, z: 0.1, w: CGFloat(M_PI) * 2.0)) spin.duration = 5 spin.repeatCount = HUGE boxNode.addAnimation(spin, forKey: "spin") // タイムラインに表示 XCPShowView("View", view)
これで、アニメーションつきの3Dグラフィクスプログラミングの完成!! 簡単!!
最後にすこし応用的なサンプル。3Dの図形を立方体からトーラスに変更し、for文でくりかえして大量に描画する。さらに回転するアニメーションの速度を乱数で変化させて、いろいろな速度で回転するようにしてみた。
import SceneKit import XCPlayground let width:CGFloat = 600 let height:CGFloat = 600 var view = SCNView(frame: CGRect(x: 0, y: 0, width: width, height: height)) var scene = SCNScene() view.scene = scene view.backgroundColor = NSColor.blackColor() view.autoenablesDefaultLighting = true var camera = SCNCamera() var cameraNode = SCNNode() cameraNode.camera = camera cameraNode.position = SCNVector3(x: 0, y: 0, z: 3) scene.rootNode.addChildNode(cameraNode) for var i = 0; i < 40; i++ { var torus = SCNTorus( ringRadius: CGFloat(arc4random_uniform(150)) / 100.0, pipeRadius: 0.05) var torusNode = SCNNode(geometry: torus) scene.rootNode.addChildNode(torusNode) torus.firstMaterial.diffuse.contents = NSColor( calibratedHue: CGFloat(arc4random_uniform(100)) / 300.0 + 0.3, saturation: 0.5, brightness: 1.2, alpha: 0.9) torus.firstMaterial.specular.contents = NSColor.yellowColor() var spin = CABasicAnimation(keyPath: "rotation") spin.toValue = NSValue(SCNVector4:SCNVector4( x: CGFloat(random()), y: CGFloat(random()), z: CGFloat(random()), w: CGFloat(M_PI) * 2.0)) spin.duration = NSTimeInterval(arc4random_uniform(10) + 10) spin.repeatCount = HUGE torusNode.addAnimation(spin, forKey: "spin") } XCPShowView("View", view)
たったこれだけのコードで複雑な3D図形が生成された。楽しい!!