如何将Scenekit背景设置为立方体贴图

9

我正在尝试使用大约6张图片的数组将场景的背景内容设置为Skybox效果。

我已经按照正确的顺序创建了图像数组,我知道接下来需要使用

+ (instancetype) materialPropertyWithContents:(id)contents

然而,我正在努力弄清楚在哪里以及如何使用该类方法来返回包含立方体贴图的属性。

4个回答

27

SCNScene的"background"属性属于SCNMaterialProperty类。因此,您可以直接将其内容设置为包含6张图片的数组,以设置您的天空盒(请参见SCNScene.h)。

aScene.background.contents = @[@"Right.png", @"Left.png", @"Top.png", @"Bottom.png", @"Back.png", @"Front.png"];

确保您的6张图片是正方形且具有相同的尺寸。


能否对天空盒进行动画处理? - Alessandro Pirovano
值得注意的是,图像的尺寸必须是2的幂。 - gargantuan
@gargantuan 为什么? - George

4

这是一个ScnView子类的awakeFromNib方法。

由于分配给content的值为id类型且示例很少,因此可能会令人感到困惑。

使用任何6个相同大小的图片,不需要TGA格式。可以在Google中搜索skybox以查找示例。

此示例创建了一个天空盒,并将相同的图像应用于立方体,使其看起来像是天空的镜像。

启用了相机控制功能,只需移动鼠标以旋转看起来像是镜像立方体的视角。

//
//  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

1
根据SCNMaterialProperty.contents的定义,您可以使用以下任何一种: 1.水平条形图像,其中6 * image.height == image.width 2.垂直条纹图像,其中image.height == 6 * image.width 3.水平十字架图像,其中4 * image.height == 3 * image.width 4.垂直十字架图像,其中3 * image.height == 4 * image.width 5.经/纬图像,其中image.height == 2 * image.width 6.6个图像的NSArray。 - alexchandel

3

Swift 5.0 / iOS 14

一些在较新版本的Swift中的变化:

    // Be aware, that the order of the images is relevant, not the names, and
    // "Front" means the background at the most negativ value of z-dimension
    // (exactly where the default camera looks at)
    
    background.contents = [UIImage(named: "Right"),
                           UIImage(named: "Left"),
                           UIImage(named: "Top"),
                           UIImage(named: "Bottom"),
                           UIImage(named: "Front"),
                           UIImage(named: "Back")]

    // alternatively
    background.contents = [UIImage(named: "east"),
                           UIImage(named: "west"),
                           UIImage(named: "sky"),
                           UIImage(named: "floor"),
                           UIImage(named: "north"),
                           UIImage(named: "south")]

有没有 SwiftUI 的解决方案? - Mc.Lover

2

这是我使用的顺序:

background.contents = [UIImage(named: "Right"),
                       UIImage(named: "Left"),
                       UIImage(named: "Top"),
                       UIImage(named: "Bottom"),
                       UIImage(named: "Front"),
                       UIImage(named: "Back")]

差不多一样,只需要交换最后两个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接