24

I'm new to iOS development and I've got myself stumped. I am trying to render a cube using SceneKit that has a different colour for each face.

This is what I've got so far:

func sceneSetup() {
    // 1
    let scene = SCNScene()

    // 2
    let BoxGeometry = SCNBox(width: 0.9, height: 0.9, length: 0.9, chamferRadius: 0.0)

    BoxGeometry.firstMaterial?.diffuse.contents = UIColor.redColor()
    let cube = SCNNode(geometry: BoxGeometry)
    cube.position = SCNVector3(x: 0, y: 0, z: -1)
    scene.rootNode.addChildNode(cube)

    // 3
    sceneView.scene = scene
    sceneView.autoenablesDefaultLighting = true
    sceneView.allowsCameraControl = true

But I'd like each face to have a different colour. How do I do that?

rickster
  • 124,678
  • 26
  • 272
  • 326
may19c19
  • 360
  • 1
  • 4
  • 15

3 Answers3

27

The box is composed out of six different elements (one for each side). You may also have noticed that a geometry object has one property for the first material but also a property for an array of materials.

An object with multiple elements and multiple materials will pick the increment the material (and wrap) for each element.

For example 4 elements and 1 material

Element   1  2  3  4
Material  1  1  1  1

or 4 elements and 2 materials

Element   1  2  3  4
Material  1  2  1  2  // note that they are repeating 

For example 4 elements and 7 materials

Element   1  2  3  4
Material  1  2  3  4  // (5, 6, 7) is unused

In the case of the box this means that you can use an array of six materials to have a unique material on each side of the box. I have an example of this in the sample code for one of the chapters for my Scene Kit book (in Objective-C):

// Each side of the box has its own color
// --------------------------------------
// All have the same diffuse and ambient colors to show the
// effect of the ambient light, even with these materials.

SCNMaterial *greenMaterial              = [SCNMaterial material];
greenMaterial.diffuse.contents          = [NSColor greenColor];
greenMaterial.locksAmbientWithDiffuse   = YES;

SCNMaterial *redMaterial                = [SCNMaterial material];
redMaterial.diffuse.contents            = [NSColor redColor];
redMaterial.locksAmbientWithDiffuse     = YES;

SCNMaterial *blueMaterial               = [SCNMaterial material];
blueMaterial.diffuse.contents           = [NSColor blueColor];
blueMaterial.locksAmbientWithDiffuse    = YES;

SCNMaterial *yellowMaterial             = [SCNMaterial material];
yellowMaterial.diffuse.contents         = [NSColor yellowColor];
yellowMaterial.locksAmbientWithDiffuse  = YES;

SCNMaterial *purpleMaterial             = [SCNMaterial material];
purpleMaterial.diffuse.contents         = [NSColor purpleColor];
purpleMaterial.locksAmbientWithDiffuse  = YES;

SCNMaterial *magentaMaterial            = [SCNMaterial material];
magentaMaterial.diffuse.contents        = [NSColor magentaColor];
magentaMaterial.locksAmbientWithDiffuse = YES;


box.materials =  @[greenMaterial,  redMaterial,    blueMaterial,
                   yellowMaterial, purpleMaterial, magentaMaterial];
David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
  • 6
    Nicely done, sir! One extra thing worth noting: with many geometries, you can tell how many materials it supports by querying its `geometryElementCount`. This doesn't work for `SCNBox`, though... it automatically recreates its set of geometry elements depending on how many materials you assign (that way it doesn't need six draw calls to render if it doesn't have six materials). – rickster Dec 16 '14 at 20:10
  • 2
    Which side is which (front, right, back, left, top, bottom)? –  Apr 03 '16 at 16:36
  • 1
    I would also like to know this^ Though I'm also unsure how you'd define front/right/back/left/top/bottom... – Ruben Martinez Jr. Jul 04 '17 at 01:37
  • Followup to my older comment: I never said what happens when you assign fewer than six materials. And I don't have details for each number handy, but the gist of it is that you get some roughly sensible distribution of repeated faces. For example, if you assign three materials, you get each pair of opposing faces sharing a material. Two materials gets three faces each. – rickster Aug 01 '17 at 22:36
  • 4
    @RubenMartinezJr. The array order defines which face is which. It should be the same order as specifying a cube map for [`SCNMaterialProperty.contents`](https://developer.apple.com/documentation/scenekit/scnmaterialproperty/1395372-contents): +x, -x, +y, -y, +z, -z. (Relative to the viewer in default orientation, that's right, left, top, bottom, far, near.) – rickster Aug 01 '17 at 22:42
  • 1
    Swift converted code for ease: https://gist.github.com/mattrobmattrob/e934d664ef0fc8d45b7837d9eb797255 – Matt Robinson Aug 31 '17 at 19:37
  • @DavidRonnqvist Thank you for sharing this! It sure beats making static texture images when all I want is to color the sides! – Justin Ngan Aug 23 '20 at 01:00
17

Here's a Swift 4 answer.

    let colors = [UIColor.green, // front
                  UIColor.red, // right
                  UIColor.blue, // back
                  UIColor.yellow, // left
                  UIColor.purple, // top
                  UIColor.gray] // bottom

    let sideMaterials = colors.map { color -> SCNMaterial in
        let material = SCNMaterial()
        material.diffuse.contents = color
        material.locksAmbientWithDiffuse = true
        return material
    }

    materials = sideMaterials

To change the front material just grab the material and change it's content

let frontMaterial = materials[0]
frontMaterial.diffuse.contents = UIColor.white
David
  • 14,205
  • 20
  • 97
  • 144
4

thai you for the fast help. i used the code you posed but was unable to use NSColor so i tried uicolor but all i was getting was a black cube so i tried this and i got it working

    let BoxGeometry = SCNBox(width: 0.95, height: 0.95, length: 0.95, chamferRadius: 0.0)

    let greenMaterial = SCNMaterial()
    greenMaterial.diffuse.contents = UIImage(named: "g")
    greenMaterial.locksAmbientWithDiffuse = true;

    let redMaterial = SCNMaterial()
    redMaterial.diffuse.contents = UIImage(named: "r")
    redMaterial.locksAmbientWithDiffuse = true;


    let blueMaterial  = SCNMaterial()
    blueMaterial.diffuse.contents = UIImage(named: "b")
    blueMaterial.locksAmbientWithDiffuse = true;


    let yellowMaterial = SCNMaterial()
    yellowMaterial.diffuse.contents = UIImage(named: "y")
    yellowMaterial.locksAmbientWithDiffuse = true;


    let purpleMaterial = SCNMaterial()
    purpleMaterial.diffuse.contents = UIImage(named: "p")
    purpleMaterial.locksAmbientWithDiffuse = true;


    let WhiteMaterial = SCNMaterial()
    WhiteMaterial.diffuse.contents = UIImage(named: "w")
    WhiteMaterial.locksAmbientWithDiffuse   = true;


    BoxGeometry.materials =  [greenMaterial,  redMaterial,    blueMaterial,
    yellowMaterial, purpleMaterial, WhiteMaterial];

g is a jpeg of a green and so on and that has got it working now.

may19c19
  • 360
  • 1
  • 4
  • 15