I am trying to set the background contents of a scene to a skybox effect using about array of 6 images.
I have created the array of images in the correct order, I know I need to then use
+ (instancetype) materialPropertyWithContents:(id)contents
However I'm struggling to work out how and where exactly I use that class method to return the property containing the cube map.
SCNScene's "background" property is of the SCNMaterialProperty class. So you can directly set it's contents to an array of 6 images to setup your skybox (see SCNScene.h).
aScene.background.contents = @[@"Right.png", @"Left.png", @"Top.png", @"Bottom.png", @"Back.png", @"Front.png"];
Make sure your 6 images are square and with the same dimensions.
HERES my awakeFromNib for a ScnView subclass
yes its confusing as the value assigned to content is id and theres so few samples.
HERES my awakeFromNib for a ScnView subclass
Use any 6 images of same size. TGA not required.
Google skybox to find examples.
This sample makes a skybox and also applys the same images to a cube which make it seem to mirror the sky.
Camera control is on so just move your mouse to rotate what looks like a mirrored cube
//
// SkyBoxSceneView.h
// SceneKit_Skybox
//
// Created by Brian Clear on 12/06/2014.
// Copyright (c) 2014 Brian Clear. All rights reserved.
//
#import <SceneKit/SceneKit.h>
@interface SkyBoxSceneView : SCNView
@end
//
// SkyBoxSceneView.m
// SceneKit_Skybox
//
// Created by Brian Clear on 12/06/2014.
// Copyright (c) 2014 Brian Clear. All rights reserved.
//
#import "SkyBoxSceneView.h"
@implementation SkyBoxSceneView
-(void)awakeFromNib
{
// create a new scene
SCNScene *scene = [SCNScene scene];
//-----------------------------------------------------------------------------------
//SET THE SKYBOX
//-----------------------------------------------------------------------------------
//it took me a while to get it working
//"APPLE IF YOU WANT SCENE KIT TO SUCCEED YOU NEED A FULL BLOWN GUIDE!!"
//-----------------------------------------------------------------------------------
//FIRST ISSUE - Error:scene.background is readonly
// I misread the help as "to set skybox set the scene.background"
/*
scene.background = ; //INCORRECT
scene.background.contents = ; //OK
*/
//I should have read it as "to set skybox set the scene.background content e.g. scene.background.contents"
//-----------------------------------------------------------------------------------
//ONLY EXAMPLE OF setting material.reflective DOESNT WORK for scene.background.content
/*
I couldnt get sky box to work for ages because the only example of using reflective property I found was in
in the 2014 sample code
AAPLSlideMaterialLayer.m
https://developer.apple.com/library/prerelease/mac/samplecode/SceneKitWWDC2014/Listings/Scene_Kit_Session_WWDC_2014_Sources_Slides_AAPLSlideMaterialLayer_m.html#//apple_ref/doc/uid/TP40014551-Scene_Kit_Session_WWDC_2014_Sources_Slides_AAPLSlideMaterialLayer_m-DontLinkElementID_62
_material.reflective.contents = @[@"right.tga", @"left.tga", @"top.tga", @"bottom.tga", @"back.tga", @"front.tga"];
so I tried it on scene.background.contents =
but didnt work
*/
//WRONG
//scene.background.contents = @[@"right.png", @"left.png", @"top.png", @"bottom.png", @"back.png", @"front.png"];
//-----------------------------------------------------------------------------------
//ATTEMPT 3 - I changed all tga to png but still nothing
//-----------------------------------------------------------------------------------
//ATTEMPT 4 - Note this is very wrong. I was way off here
//when I saw this
// _material.reflective.contents = @[@"right.tga", @"left.tga", @"top.tga", @"bottom.tga", @"back.tga", @"front.tga"];
//I then saw this
//scene.background.contents = ...
//I made the mistake of presuming that both "content" properties were the same
//SceneKit take a lot of id properties so WITHOUT A GUIDE you have to guess what goes into thes id properties
//I though scene.background was a SCNMaterialProperty cos it had scene.background.content
//same as material.reflective.content - reflective is a SCNMaterialProperty
//-----------------------------------------------------------------------------------
//tried it with SCNMaterialProperty.content
//but would never work as scene.background isnt a SCNMaterialProperty.content
// SCNMaterialProperty *scnMaterialProperty = [SCNMaterialProperty materialPropertyWithContents: @[@"right.tga", @"left.tga", @"top.tga", @"bottom.tga", @"back.tga", @"front.tga"]];
//-----------------------------------------------------------------------------------
//tried with png but same issue
// SCNMaterialProperty *scnMaterialProperty = [SCNMaterialProperty materialPropertyWithContents: @[@"right.png", @"left.png", @"top.png", @"bottom.png", @"back.png", @"front.png"]];
//-----------------------------------------------------------------------------------
//I had tried passing NSImage instead of NSString for material which worked
//boxNode.geometry.firstMaterial.reflective.contents = @[[NSImage imageNamed:@"right.tga"],....
//so tried that for scne.background.content
//but was doomed as not a SCNMaterialProperty
// SCNMaterialProperty *scnMaterialProperty = [SCNMaterialProperty materialPropertyWithContents: @[[NSImage imageNamed:@"right.tga"],
// [NSImage imageNamed:@"left.tga"],
// [NSImage imageNamed:@"top.tga"],
// [NSImage imageNamed:@"bottom.tga"],
// [NSImage imageNamed:@"back.tga"],
// [NSImage imageNamed:@"front.tga"]]];
//-----------------------------------------------------------------------------------
//Test 4 - try with one image
//WORKS - set whole background to one image
//scene.background.contents = [NSImage imageNamed:@"left.tga"];//OK
//this proved that the image does load
//-----------------------------------------------------------------------------------
//use same one image in a SCNMaterialProperty
//DOESNT WORK - so issue is the SCNMaterialProperty
// SCNMaterialProperty *scnMaterialProperty = [SCNMaterialProperty materialPropertyWithContents:[NSImage imageNamed:@"right.tga"]];
// scnMaterialProperty.intensity = 0.7;
// scene.background.contents = scnMaterialProperty;//OK
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//SKYBOX WORKS!!!!
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//version3 - pass array in directly (NOT through SCNMaterialProperty!!!!)
scene.background.contents = @[[NSImage imageNamed:@"right.tga"],
[NSImage imageNamed:@"left.tga"],
[NSImage imageNamed:@"top.tga"],
[NSImage imageNamed:@"bottom.tga"],
[NSImage imageNamed:@"back.tga"],
[NSImage imageNamed:@"front.tga"]];
//-----------------------------------------------------------------------------------
//DOESNT WORK
//scene.background.contents = @"frozen.mov";//
//-----------------------------------------------------------------------------------
//CAMERA and CUBE
//-----------------------------------------------------------------------------------
// create and add a camera to the scene
SCNNode *cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
[scene.rootNode addChildNode:cameraNode];
// place the camera
cameraNode.position = SCNVector3Make(0, 0, 2);
// create and add a 3d box to the scene
SCNNode *boxNode = [SCNNode node];
boxNode.geometry = [SCNBox boxWithWidth:1 height:1 length:1 chamferRadius:0.02];
[scene.rootNode addChildNode:boxNode];
//-----------------------------------------------------------------------------------
// create and configure a material
// SCNMaterial *material = [SCNMaterial material];
// material.diffuse.contents = [NSColor brownColor];//= [NSImage imageNamed:@"texture"];
// material.specular.contents = [NSColor brownColor];
// material.specular.intensity = 0.2;
// material.locksAmbientWithDiffuse = YES;
//
// //material.reflective.contents = @[@"right.tga", @"left.tga", @"top.tga", @"bottom.tga", @"back.tga", @"front.tga"];
// material.reflective.contents = @[@"right.png", @"left.png", @"top.png", @"bottom.png", @"back.png", @"front.png"];
//
// material.diffuse.contents = [NSColor blackColor];
//
//
// // set the material to the 3d object geometry
// boxNode.geometry.firstMaterial = material;
//
// earth-reflective.jpg
// boxNode.geometry.firstMaterial.reflective.intensity = 0.7;
//
//boxNode.geometry.firstMaterial.reflective.contents = [NSImage imageNamed:@"earth-reflective"];
// boxNode.geometry.firstMaterial.reflective.contents = @[@"right.tga", @"left.tga", @"top.tga", @"bottom.tga", @"back.tga", @"front.tga"];
//-----------------------------------------------------------------------------------
//CUBE MATERIAL
//-----------------------------------------------------------------------------------
//make the cube reflect the sky
//the sky isnt really being reflected comment out line above "scene.background.contents = ...."
//and cube will still reflect the sky
//also comment out both of these lines "boxNode.geometry.firstMaterial.reflective
//and sky box will still work
//-----------------------------------------------------------------------------------
//VERSION 1 - ALSO WORKS!
boxNode.geometry.firstMaterial.reflective.contents = @[[NSImage imageNamed:@"right.tga"],
[NSImage imageNamed:@"left.tga"],
[NSImage imageNamed:@"top.tga"],
[NSImage imageNamed:@"bottom.tga"],
[NSImage imageNamed:@"back.tga"],
[NSImage imageNamed:@"front.tga"]];
boxNode.geometry.firstMaterial.reflective.intensity = 0.7;
//-----------------------------------------------------------------------------------
//VERSION 2 - ALSO WORKS!
//this uses same image for all sides of the cube
//boxNode.geometry.firstMaterial.reflective.contents = [NSImage imageNamed:@"right.tga"];//ok
//boxNode.geometry.firstMaterial.reflective.intensity = 0.7;
//-----------------------------------------------------------------------------------
//VERSION 3 - BLACK 2010 a space odyssey shiny cube
//get the earth-reflective.jpg from
//https://developer.apple.com/library/mac/samplecode/SceneKit_Slides_WWDC2013/Introduction/Intro.html
// boxNode.geometry.firstMaterial.reflective.contents = [NSImage imageNamed:@"earth-reflective"];
// boxNode.geometry.firstMaterial.reflective.intensity = 0.7;
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
//REQUIRED else above reflections look weird
boxNode.geometry.firstMaterial.diffuse.contents = [NSColor blackColor];
boxNode.geometry.firstMaterial.specular.intensity = 0.0;
//-----------------------------------------------------------------------------------
// animate the 3d object - camera control is on so cube spins with the sky
//comment in to animate cube
// CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"rotation"];
// animation.toValue = [NSValue valueWithSCNVector4:SCNVector4Make(1, 1, 0, M_PI*2)];
// animation.duration = 5;
// animation.repeatCount = MAXFLOAT; //repeat forever
// [boxNode addAnimation:animation forKey:nil];
// set the scene to the view
self.scene = scene;
// allows the user to manipulate the camera
self.allowsCameraControl = YES;
// show statistics such as fps and timing information
self.showsStatistics = YES;
}
@end
Swift 5.0 / iOS 13
Some changes in newer Swift versions:
// Be aware, that the order of the images is relevant, not the names, and
// "BACK" means the background at the most negativ value of z-dimension
// (exactly where the standard camera looks at, so you could probably think its "front")
background.contents = [UIImage(named: "Right"),
UIImage(named: "Left"),
UIImage(named: "Top"),
UIImage(named: "Bottom"),
UIImage(named: "Back"),
UIImage(named: "Front")]