4

The goal is to recreate the lighting for this OBJ file: https://poly.google.com/view/cKryD9VnDEZ

Code to load OBJ file into SceneKit (can download file from above link):

let modelPath = "model.obj"
let url = NSURL(string: modelPath)

let scene = SCNScene(named: modelPath)!
sceneView.autoenablesDefaultLighting = true
sceneView.allowsCameraControl = true
sceneView.scene = scene
sceneView.backgroundColor = UIColor.white

Options tried so far:

1) The default ambient lighting is much harsher than the Google Poly lighting. Removing the ambient lighting rendered everything too flat.

2) Using four directional lights: one in front, one behind, one below, and one above the model. All lights are angled to point at the model. This was the best, but still left some shadows and harsher areas not seen on Google Polymer.

3) Added two more lights to option #2, this time adding lights to the left and right. This one was worse than option #2 since the extra lights combined with the four existing lights and whitewashed the model.

UPDATE AFTER FOLLOWING SUGGESTIONS:

The code now implements an ambient light and a directional light.

Adding the directional light to the camera node, versus the scene root node, made no difference for some reason.

The light code is below.

There are two problems:

1) In Screenshot 1, the right side of the chest is too bright and shows no edges. The far left face of the chest is too dark. The face with the best lighting is in the center. How can you get the lighting to be like this for all faces (or better match the Google Poly lighting)?

2) In Screenshot 2, the directional light appears to have no effect. How can you ensure the back of the model is as light as the front with the suggested architecture of one ambient light and one directional light?

SCREENSHOT 1: enter image description here

SCREENSHOT 2: enter image description here

CODE:

    // Create ambient light
    let ambientLightNode = SCNNode()
    ambientLightNode.light = SCNLight()
    ambientLightNode.light!.type = .ambient
    ambientLightNode.light!.color = UIColor(white: 0.50, alpha: 1.0)

    // Add ambient light to scene
    scene.rootNode.addChildNode(ambientLightNode)

    // Create directional light
    let directionalLight = SCNNode()
    directionalLight.light = SCNLight()
    directionalLight.light!.type = .directional
    directionalLight.light!.color = UIColor(white: 0.40, alpha: 1.0)
    directionalLight.eulerAngles = SCNVector3(x: Float.pi, y: 0, z: 0)

    // Add directional light
    scene.rootNode.addChildNode(directionalLight)
Crashalot
  • 33,605
  • 61
  • 269
  • 439

3 Answers3

6

OBJ files loaded through Model I/O use physically based lighting by default. This model has a cartoonish look and uses a lot of ambient lighting with a few specular highlights.

You should start by converting all your materials to the lambert lighting model.

Then add an ambient light to you scene. There's a lot of ambient lighting in this scene, every part of the object is lit. A color of 75% white will do.

Finally attach a directional light to the camera to highlight the polygons facing the user. A color of 50% white sounds about right.

mnuages
  • 13,049
  • 2
  • 23
  • 40
  • 1
    Thanks so much! Hope you and your team can get more resources to keep building out SceneKit. – Crashalot Feb 15 '18 at 21:01
  • The lighting model option is grayed out in Xcode (can provide a screenshot if needed), so it's stuck on Blinn. Will try changing the lighting model within code (since we have to do that eventually anyway), but is there a reason the option is grayed out from the Scene Editor? – Crashalot Feb 15 '18 at 21:13
  • Additionally, for the finishing touch I would enable screen space ambient occlusion ( as it appears inside the mouth and where the legs and eyes meet the body/head). – Xartec Feb 16 '18 at 12:24
  • @Xartec how do you enable screen space ambient occlusion? – Crashalot Feb 21 '18 at 00:54
  • Posted in an answer below because the comments aren't suitable for code. – Xartec Feb 21 '18 at 01:35
  • Why do you need to add the directional light to the camera? Since it's a directional light, it seems to be no different than when adding it to the scene? Is there something something else you need to configure with the directional light if you add it to the camera node instead of the scene? @Xartec – Crashalot Mar 02 '18 at 08:06
  • well it depends on how you handle movements in your application. It makes more sense to move the camera in the scene, and so if you want the direction of the light to match the direction of the camera it’s very handy to have the light as a child of the camera. If instead the camera is static and the models are rotated, then having a light at the top level works equally well. – mnuages Mar 02 '18 at 21:45
  • the docs for directional lights say the node position has no effect; doesn't this conflict with what you're saying? the question is also updated with the lighting issue. right now, the directional light only lights up the front of the model, even when the model gets rotated. both states are shown as attachments. – Crashalot Mar 04 '18 at 06:48
  • @Xartec would appreciate your opinion on this, too. the question has been updated with code and screenshots to illustrate the lighting problem. thanks to you both! – Crashalot Mar 04 '18 at 07:55
  • The position doesn’t matter (because the directional light is like an infinite plane that acts as a light). Moving the light has no influence, but rotating it does change the direction. Adding the light to the camera makes it function a bit like a flashlight, i.e. if you rotate (aim) the camera the light will rotate with it. How did you end up rotating the model? If you rotate the model itself the lightnode can be a child of rootnode or the camera. If you rotate the camera around the object, the light needs to be a child of the camera so it rotates around the model together with the camera. – Xartec Mar 04 '18 at 18:26
  • @Xartec and mnuages thanks for your help! Finally got it working. Had to junk legacy code that moved camera around a sphere and just used the idea Xartec suggested of rotating a sphere with the camera as a child. – Crashalot Mar 06 '18 at 22:42
1

In addition to MNuages answer, try to enable screen space ambient occlusion (on the camera). The following enables it for the current camera:

scnView.pointOfView.camera.screenSpaceAmbientOcclusionIntensity = 1.7;
scnView.pointOfView.camera.screenSpaceAmbientOcclusionNormalThreshold = 0.1;
scnView.pointOfView.camera.screenSpaceAmbientOcclusionDepthThreshold = 0.08;
scnView.pointOfView.camera.screenSpaceAmbientOcclusionBias = 0.33;
scnView.pointOfView.camera.screenSpaceAmbientOcclusionRadius = 3.0;

You will probably have to tweak the values a bit to get the for you desired results, the above is just what works for me in a certain scene.

Xartec
  • 2,369
  • 11
  • 22
0

It appears that the .obj file and its .mtl file (Material) only support Physically Based shading.

To use advanced shading, like Phong, Lambert or Blinn, you need to use a .dae model (Collada).

You can export an .obj model as .dae using Blender with varying degrees of success:

https://www.blender.org

Otherwise, if you're going to use .obj and .mtl, you have to settle for Physically Based shading.

Michael N
  • 436
  • 5
  • 6