Home > database >  SceneKIt - how to assign ARSCNView video feed as a texture to a SCNGeometry
SceneKIt - how to assign ARSCNView video feed as a texture to a SCNGeometry

Time:08-25

Goal: get the video feed from ARSCNView. and assign as a video material to a SceneKIt SCNGeometry.

What I did:

        // retrieve the ship node
        let ship = scene.rootNode.childNode(withName: "shipMesh", recursively: true)!

        // apply AR feed to the ship node material
        let material = ship.geometry?.firstMaterial
        material!.diffuse.contents = arView.scene.background.contents

Problem: the ship is white, without AR video feed

Full code bellow:

import UIKit
import QuartzCore
import SceneKit
import ARKit

class GameViewController: UIViewController, ARSCNViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let arView = ARSCNView()
        
        
        // create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        
        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)
        
        // place the camera
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
        
        // create and add a light to the scene
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = .omni
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        scene.rootNode.addChildNode(lightNode)
        
        // create and add an ambient light to the scene
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = .ambient
        ambientLightNode.light!.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)
        
        // retrieve the ship node
        let ship = scene.rootNode.childNode(withName: "shipMesh", recursively: true)!
        
        let material = ship.geometry?.firstMaterial
        material!.diffuse.contents = arView.scene.background.contents
        material!.lightingModel = .constant

        
        
        // animate the 3d object
        //ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
        
        // retrieve the SCNView
        let scnView = self.view as! SCNView
        
        // set the scene to the view
        scnView.scene = scene
        
        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true
        
        // show statistics such as fps and timing information
        scnView.showsStatistics = true
        
        // configure the view
        scnView.backgroundColor = UIColor.black
        
        // add a tap gesture recognizer
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        scnView.addGestureRecognizer(tapGesture)
    }
    
    @objc
    func handleTap(_ gestureRecognize: UIGestureRecognizer) {
        // retrieve the SCNView
        let scnView = self.view as! SCNView
        
        // check what nodes are tapped
        let p = gestureRecognize.location(in: scnView)
        let hitResults = scnView.hitTest(p, options: [:])
        // check that we clicked on at least one object
        if hitResults.count > 0 {
            // retrieved the first clicked object
            let result = hitResults[0]
            
            // get its material
            let material = result.node.geometry!.firstMaterial!
            
            // highlight it
            SCNTransaction.begin()
            SCNTransaction.animationDuration = 0.5
            
            // on completion - unhighlight
            SCNTransaction.completionBlock = {
                SCNTransaction.begin()
                SCNTransaction.animationDuration = 0.5
                
                material.emission.contents = UIColor.black
                
                SCNTransaction.commit()
            }
            
            material.emission.contents = UIColor.red
            
            SCNTransaction.commit()
        }
    }
    
    override var shouldAutorotate: Bool {
        return true
    }
    
    override var prefersStatusBarHidden: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }

}

CodePudding user response:

Create a fresh (out of the box) ARKit SceneKit based App (the one with the SpaceShip)

Then you modify your viewWillAppear section like this:

Add the DispatchQueue stuff below.

Important: Add a short delay (here 5.0 seconds) to give the AR Subsystem enough time to initialise the Camera.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    // Create a session configuration
    let configuration = ARWorldTrackingConfiguration()

    // Run the view's session
    sceneView.session.run(configuration)
    
    DispatchQueue.main.asyncAfter(deadline: .now()   5.0) {
        let shipMesh = self.sceneView.scene.rootNode.childNode(withName: "shipMesh", recursively: true)
        shipMesh?.geometry?.firstMaterial?.diffuse.contents = self.sceneView.scene.background.contents
    }
    
}

I hope, this is what you were looking for.

SpaceShip Example Screenshot

  • Related