Using a Mtltexture as the Environment Map of a Scnscene

Using a MTLTexture as the environment map of a SCNScene

Lighting SCN Environment with an MTK texture

Using Xcode 13.3.1 on macOS 12.3.1 for iOS 15.4 app.


The trick is, the environment lighting requires a cube texture, not a flat image.

  • Create 6 square images for MetalKit cube texture

Sample Image

  • in Xcode Assets folder create Cube Texture Set

Sample Image

  • place textures to their corresponding slots

Sample Image

  • mirror images horizontally and vertically, if needed

Sample Image

Paste the code:

import ARKit
import MetalKit

class ViewController: UIViewController {

@IBOutlet var sceneView: ARSCNView!

override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene()

let imageName = "CubeTextureSet"
let textureLoader = MTKTextureLoader(device: sceneView.device!)

let environmentMap = try! textureLoader.newTexture(name: imageName,
scaleFactor: 2,
bundle: .main,
options: nil)

let daeScene = SCNScene(named: "art.scnassets/testCube.dae")!

let model = daeScene.rootNode.childNode(withName: "polyCube",
recursively: true)!

scene.lightingEnvironment.contents = environmentMap
scene.lightingEnvironment.intensity = 2.5
scene.background.contents = environmentMap

sceneView.scene = scene
sceneView.allowsCameraControl = true
scene.rootNode.addChildNode(model)
}
}

Apply metallic materials to models. Now MTL environment lighting is On.

Sample Image

If you need a procedural skybox texture – use MDLSkyCubeTexture class.

Also, this post may be useful for you.

SceneKit – Loading HDR or EXR lightingEnvironment has no effect

I usually use a Cube Texture Set, where each of 6 images is square (height == width).

Also, the following cube map representations are supported:

  • Vertical strip as single image (height == 6 * width)
  • Horizontal strip as single image (6 * height == width)
  • Spherical projection as single image (2 * height == width)

Here's a SwiftUI code:

func makeUIView(context: Context) -> SCNView {

let sceneView = SCNView(frame: .zero)
sceneView.scene = SCNScene()
// if EXR or HDR is 2:1 spherical map, it really meets the requirements
sceneView.scene?.lightingEnvironment.contents = UIImage(named: "std.exr")
sceneView.backgroundColor = .black
sceneView.autoenablesDefaultLighting = true
sceneView.allowsCameraControl = true

let node = SCNNode()
node.geometry = SCNSphere(radius: 0.1)
node.geometry?.firstMaterial?.lightingModel = .physicallyBased
node.geometry?.firstMaterial?.metalness.contents = 1.0
sceneView.scene?.rootNode.addChildNode(node)
return sceneView
}

Pay particular attention – you need .physicallyBased lighting model to get HDR or EXR reflections.

Sample Image

And let's set it for BG:

sceneView.scene?.background.contents = UIImage(named: "std.exr")

Sample Image


Why your .exr doesn't work?

The solutions is simple: delete your .exr from project, empty the Trash and after that drag-and-drop .exr file, in Choose options for adding these files window choose Add to targets:

Sample Image

Now your .exr must work.

SceneKit – How can I programmatically adjust 'Sun elevation' parameter?

You can simultaneously use MDL Sky Box for scene lighting and for BG:

sceneView.scene?.background.contents = MDLSkyCubeTexture(name: "procedural sky",
channelEncoding: .float32,
textureDimensions: vector_int2(512,512),
turbidity: 0.12,
sunElevation: 1.00,
upperAtmosphereScattering: 0.05,
groundAlbedo: 0.32)

sceneView.scene?.lightingEnvironment.contents = sceneView.scene?.background.contents

sceneView.scene?.lightingEnvironment.intensity = 2.0

access particular set of pixels MTLTexture in metal

Yes, as a look at the MTLTexture documentation would have shown you. You can use one of the getBytes() methods to copy a region of texture data out to a buffer, and one of the replace() methods to replace a region of the texture's pixel with data from a buffer you supply.



Related Topics



Leave a reply



Submit