SceneKit日和(3)
どうも、吉村です。
もう3回目です。
前回と前々回では、低レベルなAPIを通じて、SceneKitの世界へ旅立ちました。
なので今回は高レベルAPIを使って、より大きな世界へ飛躍しようと思います。
今回のチャレンジではSceneKitとAVFoundation、そしてCoreImageテクノロジーを統合します。
まずは準備として、立方体を用意します。
インターフェースビルダーの構成は特に目新しいものはなく、
単に
IBOutlet SCNView *_view;
だけバインドしておくだけになります。
今まで触れませんでしたが、
インターフェースビルダーで、
SceneViewのプロパティとして、Allow camera controlにチェックをつけておけば、
コードを書かなくても、マウスでの単純なカメラ回転などの処理を行ってくれます。
今回は、強制的なレンダリングループは必要ないので、Playにはチェックしていません。
では一旦立方体を用意するコードを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
_view.scene = [[SCNScene alloc] init]; _view.showsStatistics = YES; SCNCamera *camera = [SCNCamera camera]; SCNNode *cameraNode = [SCNNode node]; cameraNode.camera = camera; cameraNode.position = SCNVector3Make(0, 0, 30); [_view.scene.rootNode addChildNode:cameraNode]; SCNMaterial *planeMaterial = [SCNMaterial material]; planeMaterial.diffuse.contents = [NSColor redColor]; SCNBox *boxGeometry = [SCNBox boxWithWidth:20 height:20 length:20 chamferRadius:0]; boxGeometry.materials = @[planeMaterial]; SCNNode *node = [SCNNode node]; node.geometry = boxGeometry; [_view.scene.rootNode addChildNode:node]; _view.pointOfView = cameraNode; |
いくつか補足すると、
SCNBox *boxGeometry = [SCNBox boxWithWidth:20 height:20 length:20 chamferRadius:0];
で立方体を用意します。chamferRadiusは角丸ですね。
planeMaterial.diffuse.contents = [NSColor redColor];
contentsにNSColorを入れると、単色で塗りつぶす事が出来ます。
さて、次にAVFoundationの統合として、カメラを使ってみましょう。
AVFoundationの変数を追記すると、変数が以下のようになります。
1 2 3 4 5 6 7 8 |
{ IBOutlet SCNView *_view; AVCaptureDevice *_captureDevice; AVCaptureDeviceInput *_captureDeviceInput; AVCaptureSession *_captureSession; AVCaptureVideoPreviewLayer *_captureVideoPreviewLayer; } |
そして、カメラを初期化します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// キャプチャーデバイスの初期化 NSError *error; _captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; _captureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:_captureDevice error:&error]; if(_captureDeviceInput == nil){ NSLog(@"%@", error); NSAssert(0, @""); } // キャプチャーセッションの初期化 _captureSession = [[AVCaptureSession alloc] init]; NSAssert([_captureSession canAddInput:_captureDeviceInput], @""); [_captureSession addInput:_captureDeviceInput]; // レイヤーの初期化 _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession]; _captureVideoPreviewLayer.frame = NSMakeRect(0, 0, 1000, 1000); _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; [_captureSession startRunning]; |
この辺りはiOSと大体同じですね。
カメラを選ぶ場合はより複雑ですが、ひとまずこれでよしとしましょう。
では、統合します。
1 2 |
//planeMaterial.diffuse.contents = [NSColor redColor]; planeMaterial.diffuse.contents = _captureVideoPreviewLayer; |
はい、完成です。
さらに倍プッシュでCoreImageを統合しましょう。
1 2 3 4 5 |
// CoreImage CIFilter *filter = [CIFilter filterWithName:@"CIPixellate"]; [filter setDefaults]; [filter setValue:@50 forKey:@"inputScale"]; _captureVideoPreviewLayer.filters = @[filter]; |
なんということでしょう。
たったこれだけで、各種フレームワークが統合されてしまいました。
面倒なグルーコードなど、1行も必要なかったのです。
以上のコードを実行すると、
このようになります。
コードがちょっと分散してしまったので、
全コードを以下にまとめます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#import "USNMainController.h" #import #import #import @implementation USNMainController { IBOutlet SCNView *_view; AVCaptureDevice *_captureDevice; AVCaptureDeviceInput *_captureDeviceInput; AVCaptureSession *_captureSession; AVCaptureVideoPreviewLayer *_captureVideoPreviewLayer; } - (void)awakeFromNib { // キャプチャーデバイスの初期化 NSError *error; _captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; _captureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:_captureDevice error:&error]; if(_captureDeviceInput == nil){ NSLog(@"%@", error); NSAssert(0, @""); } // キャプチャーセッションの初期化 _captureSession = [[AVCaptureSession alloc] init]; NSAssert([_captureSession canAddInput:_captureDeviceInput], @""); [_captureSession addInput:_captureDeviceInput]; // レイヤーの初期化 _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession]; _captureVideoPreviewLayer.frame = NSMakeRect(0, 0, 1000, 1000); _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; [_captureSession startRunning]; // CoreImage CIFilter *filter = [CIFilter filterWithName:@"CIPixellate"]; [filter setDefaults]; [filter setValue:@50 forKey:@"inputScale"]; _captureVideoPreviewLayer.filters = @[filter]; // SceneKit _view.scene = [[SCNScene alloc] init]; _view.showsStatistics = YES; SCNCamera *camera = [SCNCamera camera]; SCNNode *cameraNode = [SCNNode node]; cameraNode.camera = camera; cameraNode.position = SCNVector3Make(0, 0, 30); [_view.scene.rootNode addChildNode:cameraNode]; SCNMaterial *planeMaterial = [SCNMaterial material]; planeMaterial.diffuse.contents = _captureVideoPreviewLayer; SCNBox *boxGeometry = [SCNBox boxWithWidth:20 height:20 length:20 chamferRadius:0]; boxGeometry.materials = @[planeMaterial]; SCNNode *node = [SCNNode node]; node.geometry = boxGeometry; [_view.scene.rootNode addChildNode:node]; _view.pointOfView = cameraNode; } @end |
ビルドするためには、
Quartz.framework,
AVFoundation.framework
が追加で必要になるので、注意しましょう。
このように高レベルAPIを駆使して富豪的にプログラミングをするというのも、
SceneKitの醍醐味です。
是非皆さんも味わってみてはいかがでしょうか。