How to set contents of scenekit background to cube

2020-02-08 09:39发布

问题:

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.

回答1:

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.



回答2:

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


回答3:

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")]