I'm trying to light up a basic model I downloaded from Mixamo.
let scene = SCNScene(named: "art.scnassets/Ch45_nonPBR.dae")!
// 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: 40, z: 110)
// create and add a light to the scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .omni
lightNode.position = SCNVector3(x: 0, y: 50, z: 50)
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.white
scene.rootNode.addChildNode(ambientLightNode)
At present the camera is too close to the model. But if I change the z-value from 110, all I see is black. I imagine this has something to do with lighting. What should my lighting be so that I can see my model even if I change the z-value of my camera to a higher value so I can see the model from far away ?
EDIT: For example, right now you can see at this distance, part of the leg is not visible:
If I move further away, the whole model won't be visible!!
EDIT: For example, how do I edit the lighting so it looks just like in the Mixamo preview on their website:
I would like to move the camera far enough away so I can see the whole model on screen.
SCNCamera has a zFar property, the default value is 100, any surface further away from the camera than this is clipped to improve performance. In your screenshot the leg is the furthest part of the model from the camera so gets clipped first, and as you move further away the whole model is clipped.
You can just increase the zFar to a number suitable for your scene.
Related
I want to put a SCNLight node and project a light to transparent SCNFloor? This will give the effect of light projection on real surface. Similar to shadowOnly lighting model that cast shadow. Is this possible?
Yes, you can do that by casting a white (or bleeded color) shadow instead of a black one. For that you have to use deferred shadows type that are rendered in a post-processing pass.
Consider, you must use two separate lights in your scene – first one for white shadows casting, and a second one for lighting objects that cast these white shadows. You're able to include and exclude objects from lighting scheme implementing categoryBitMask property.
// SETTING A SHADOW CATCHER
let plane = SCNPlane(width: 10, height: 10)
plane.firstMaterial?.isDoubleSided = true
plane.firstMaterial?.lightingModel = .shadowOnly
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = -.pi / 2
scene.rootNode.addChildNode(planeNode)
// SETTING A LIGHT
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .directional
lightNode.light?.intensity = 50
lightNode.light?.color = UIColor.black
lightNode.eulerAngles.x = -.pi / 2
scene.rootNode.addChildNode(lightNode)
// DEFERRED SHADOWS
lightNode.light?.castsShadow = true
lightNode.light?.shadowMode = .deferred // important
lightNode.light?.forcesBackFaceCasters = true // important
lightNode.light?.shadowRadius = 10
lightNode.light?.shadowColor = UIColor.white
This created the effect I wanted. I added IES type light node on a plane whose material blend mode is set to screen mode.
let light = SCNLight()
light.type = .IES
light.intensity = 10000
let lightNode = SCNNode()
lightNode.light = light
//set slightly above of the plane, set on Z bc it's rotated on x axis
lightNode.position = SCNVector3Make(0, 0, 0.01)
let planeMaterial = SCNMaterial()
planeMaterial.blendMode = .screen
let planeForLight = SCNPlane(width: 0.9, height: 0.9)
planeForLight.materials = [planeMaterial]
let planeNodeForLight = SCNNode(geometry: planeForLight)
planeNodeForLight.transform = SCNMatrix4MakeRotation(-Float.pi / 2, 1, 0, 0)
im trying to show a earth model on screen, loaded model from here (tried and other same models), but id didn't show on screen
[let scene = SCNScene(named: "tierra-1.obj")
if scene == nil {
print("nuuuul")
return
}
// 2: Add camera node
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
// 3: Place camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 0)
// 4: Set camera on scene
scene?.rootNode.addChildNode(cameraNode)
// 5: Adding light to scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .omni
lightNode.position = SCNVector3(x: 0, y: 0, z: 0)
scene?.rootNode.addChildNode(lightNode)
// 6: Creating and adding ambien light to scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light?.type = .ambient
ambientLightNode.light?.color = UIColor.darkGray
scene?.rootNode.addChildNode(ambientLightNode)
// If you don't want to fix manually the lights
sceneView.autoenablesDefaultLighting = true
// Allow user to manipulate camera
sceneView.allowsCameraControl = true
// Show FPS logs and timming
sceneView.showsStatistics = true
// Set background color
sceneView.backgroundColor = UIColor.white
// Allow user translate image
sceneView.cameraControlConfiguration.allowsTranslation = false
// Set scene settings
sceneView.scene = scene][2]
but after loading I have empty screen (example), how to fix that?
I copied and ran your code using a custom obj, I got it loaded and in the viewport.
I loaded in an obj, using a node, and not trying to load the obj as the scene itself.
I noticed that your camera is at world space 0,0,0. that would most likely be inside the mesh, which would appear see through as materials are defaulted with backface culling on.
I assign the material to be blue, just to make it easier to spot incase the asset is very small. I would suggest selecting the asset in XCode, and you can see the bounding box size. This will help get a better idea for how far away you need to move the camera to get it to see the mesh.
import ModelIO
import SceneKit
import SceneKit.ModelIO
import PlaygroundSupport
let scene = SCNScene()
let sceneView = SCNView(frame: CGRect(x:0, y:0, width:800, height:800))
// Set scene settings
sceneView.scene = scene
// 2: Add camera node
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
// 3: Place camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 10)
// 4: Set camera on scene
scene.rootNode.addChildNode(cameraNode)
// Material
let blueMaterial = SCNMaterial()
blueMaterial.diffuse.contents = UIColor.blue
// Accessing the Playground resource folder
let path = Bundle.main.path(forResource:"randomAsset", ofType: "obj")
let asset = MDLAsset(url: URL(string: path!)!)
let object = asset.object(at: 0)
let objNode = SCNNode(mdlObject: object)
objNode.geometry?.materials = [blueMaterial]
scene.rootNode.addChildNode(objNode)
// Allow user to manipulate camera
sceneView.allowsCameraControl = true
// Show FPS logs and timming
//sceneView.showsStatistics = true
// Set background color
sceneView.backgroundColor = UIColor.gray
// Allow user translate image
sceneView.cameraControlConfiguration.allowsTranslation = true
PlaygroundPage.current.setLiveView(sceneView)
I dont think SceneKit supporst loading an Obj as a scene. They currently only allow types that have the ability to export scene data eg.camera,lights. https://developer.apple.com/documentation/scenekit/scnscenesource
If you want to load the obj file you will have to import it into your scene.
This post should help https://forums.developer.apple.com/thread/3979
If a scene contains multiple cameras, which camera does the projectPoint method use to project points from 3D to screen-space? If this is defined by the pointOfView property, then how come when I update the position of the pointOfView a given 3D point is still projected to the same 2D point?
Since SCNCamera belongs to SCNView, just set the PoV via the "pointOfView" instance property of a View to a required camera node.
let cameraNode001 = SCNNode()
cameraNode001.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode001)
cameraNode001.position = SCNVector3(x: 0, y: 0, z: 15)
let cameraNode002 = SCNNode()
cameraNode002.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode002)
cameraNode002.position = SCNVector3(x: 10, y: 10, z: 30)
let sceneView = self.view as! SCNView
sceneView.scene = scene
sceneView.pointOfView = cameraNode001
then you can change PoV:
sceneView.pointOfView = cameraNode002
How can I draw a 3D object (preferably a rectangle) with core graphics in Swift?
Is it possible or do I have to use a different library?
Is it possible with UIKit?
Borrowing from this answer: https://stackoverflow.com/a/24127282/887210
The key part for your question is:
SCNBox(width: 1, height: 4, length: 9, chamferRadius: 0)
This draws a rectangular box with SceneKit and UIKit. It's set up to be used in a custom UIViewController in your project but it can easily be adapted to other uses.
The example code:
override func loadView() {
// create a scene view with an empty scene
let sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
let scene = SCNScene()
sceneView.scene = scene
// default lighting
sceneView.autoenablesDefaultLighting = true
// a camera
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
scene.rootNode.addChildNode(cameraNode)
// a geometry object
let box = SCNBox(width: 1, height: 4, length: 9, chamferRadius: 0)
let boxNode = SCNNode(geometry: box)
scene.rootNode.addChildNode(boxNode)
// configure the geometry object
box.firstMaterial?.diffuse.contents = UIColor.red
box.firstMaterial?.specular.contents = UIColor.white
// set a rotation axis (no angle) to be able to
// use a nicer keypath below and avoid needing
// to wrap it in an NSValue
boxNode.rotation = SCNVector4(x: 1, y: 1, z: 0.0, w: 0.0)
// animate the rotation of the torus
let spin = CABasicAnimation(keyPath: "rotation.w") // only animate the angle
spin.toValue = 2.0*Double.pi
spin.duration = 10
spin.repeatCount = HUGE // for infinity
boxNode.addAnimation(spin, forKey: "spin around")
view = sceneView // Set the view property to the sceneView created here.
}
This question is similar to the question whether it is possible to draw a 3D object on a sheet of paper which is, between, 2D. The third dimension effect is achieved drawing additional lines as the projections. The third dimension could also be perceived through the motion, so, Core Animation is a possible companion to Core Graphics but it requires a lot of calculation, as result, it is quite complicated (using Core Animation).
Actually, SceneKit or Metal are the options to draw 3D models using Swift.
I am just starting off with SceneKit and I have some code that renders a cube onto the screen. It shows up find but as soon as I add a camera node the cube no longer appears on screen. Given that I'd set the position of the cube's Node to 0,0,0 and the same for the camera, I cannot figure out what is wrong. I attempted to set the pointOfView property of the SCNView to the camera node but this did not work.
Here is the code for clarification:
func setup() {
box = SCNBox(width: 30, height: 30, length: 30, chamferRadius: 5)
shapeNode.geometry = box
shapeNode.position = SCNVector3(x:0, y:0, z:0)
mainNode.addChildNode(shapeNode)
scene.rootNode.addChildNode(mainNode)
sceneKitView = SCNView(frame:bounds, options:nil)
sceneKitView.autoenablesDefaultLighting = true
sceneKitView.allowsCameraControl = true
sceneKitView.scene = scene
sceneKitView.backgroundColor = UIColor.blackColor()
addSubview(sceneKitView)
// cameraNode.camera = SCNCamera()
// cameraNode.position = SCNVector3Make(0, 0, 10)
// scene.rootNode.addChildNode(cameraNode)
// sceneKitView.pointOfView = cameraNode
}
if both your camera and cube are at (0, 0, 0) then the camera is inside the cube and can't see it. You can make your cube's material doubleSided so that back-facing triangles are visible, but you probably just want to move your camera at something like (0, 0, 100) (a camera's direction of view is along the negative Z axis, and your box has a size of 30).