首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SceneKit:使块更逼真或类似于3D

SceneKit:使块更逼真或类似于3D
EN

Stack Overflow用户
提问于 2016-07-06 03:06:54
回答 3查看 2.6K关注 0票数 2

下面的代码用于在SceneKit中创建场景和创建块。据我们的用户说,这些区块看起来很平,而且不够"3D“。截图1-2显示我们的应用程序。

屏幕截图3-5显示了用户的期望块是什么样子,这是更像3D。

在与不同的人交谈后,对于如何呈现看起来更像屏幕截图3-5的块有不同的看法。有些人说使用环境遮挡,另一些人说体素照明,有些人说使用点照明和阴影,或定向照明。

我们以前试过添加全光照明,但那没有效果,所以它被删除了。正如您在代码中所看到的,我们也对环境光节点进行了实验,但这也没有产生正确的结果。

什么是最好的方式来渲染我们的块,并实现类似的屏幕截图3-5?

注意:我们理解代码不是为了性能而优化的,也就是说,多边形是不应该显示的。这没什么。重点不是在性能上,而是在实现更多的3D类渲染。您可以在节点上假设一些硬限制,比如场景中不超过1K或10K。

代码:

代码语言:javascript
运行
复制
func createScene() {
    // Set scene view
    let scene = SCNScene()
    sceneView.jitteringEnabled = true
    sceneView.scene = scene

    // Add camera node
    sceneView.pointOfView = cameraNode

    // Make delegate to capture screenshots
    sceneView.delegate = self

    // Set ambient lighting
    let ambientLightNode = SCNNode()
    ambientLightNode.light = SCNLight()
    ambientLightNode.light!.type = SCNLightTypeAmbient
    ambientLightNode.light!.color = UIColor(white: 0.50, alpha: 1.0)
    //scene.rootNode.addChildNode(ambientLightNode)
    //sceneView.autoenablesDefaultLighting = true

    // Set floor
    setFloor()

    // Set sky
    setSky()

    // Set initial position for user node
    userNode.position = SCNVector3(x: 0, y: Float(CameraMinY), z: Float(CameraZoom))

    // Add user node
    scene.rootNode.addChildNode(userNode)

    // Add camera to user node
    // zNear fixes white triangle bug while zFar fixes white line bug
    cameraNode.camera = SCNCamera()
    cameraNode.camera!.zNear = Double(0.1)
    cameraNode.camera!.zFar = Double(Int.max)
    cameraNode.position = SCNVector3(x: 0, y: 0, z: 0) //EB: Add some offset to represent the head
    userNode.addChildNode(cameraNode)
}


private func setFloor() {
    // Create floor geometry
    let floorImage = UIImage(named: "FloorBG")!
    let floor = SCNFloor()
    floor.reflectionFalloffEnd = 0
    floor.reflectivity = 0
    floor.firstMaterial!.diffuse.contents = floorImage
    floor.firstMaterial!.diffuse.contentsTransform = SCNMatrix4MakeScale(Float(floorImage.size.width)/2, Float(floorImage.size.height)/2, 1)
    floor.firstMaterial!.locksAmbientWithDiffuse = true
    floor.firstMaterial!.diffuse.wrapS = .Repeat
    floor.firstMaterial!.diffuse.wrapT = .Repeat
    floor.firstMaterial!.diffuse.mipFilter = .Linear

    // Set node & physics
    // -- Must set y-position to 0.5 so blocks are flush with floor
    floorLayer = SCNNode(geometry: floor)
    floorLayer.position.y = -0.5
    let floorShape = SCNPhysicsShape(geometry: floor, options: nil)
    let floorBody = SCNPhysicsBody(type: .Static, shape: floorShape)
    floorLayer.physicsBody = floorBody
    floorLayer.physicsBody!.restitution = 1.0

    // Add to scene
    sceneView.scene!.rootNode.addChildNode(floorLayer)
}


private func setSky() {
    // Create sky geometry
    let sky = SCNFloor()
    sky.reflectionFalloffEnd = 0
    sky.reflectivity = 0
    sky.firstMaterial!.diffuse.contents = SkyColor
    sky.firstMaterial!.doubleSided = true
    sky.firstMaterial!.locksAmbientWithDiffuse = true
    sky.firstMaterial!.diffuse.wrapS = .Repeat
    sky.firstMaterial!.diffuse.wrapT = .Repeat
    sky.firstMaterial!.diffuse.mipFilter = .Linear
    sky.firstMaterial!.diffuse.contentsTransform = SCNMatrix4MakeScale(Float(2), Float(2), 1);

    // Set node & physics
    skyLayer = SCNNode(geometry: sky)
    let skyShape = SCNPhysicsShape(geometry: sky, options: nil)
    let skyBody = SCNPhysicsBody(type: .Static, shape: skyShape)
    skyLayer.physicsBody = skyBody
    skyLayer.physicsBody!.restitution = 1.0

    // Set position
    skyLayer.position = SCNVector3(0, SkyPosY, 0)

    // Set fog
    /*sceneView.scene?.fogEndDistance = 60
     sceneView.scene?.fogStartDistance = 50
     sceneView.scene?.fogDensityExponent = 1.0
     sceneView.scene?.fogColor = SkyColor */

    // Add to scene
    sceneView.scene!.rootNode.addChildNode(skyLayer)
}


func createBlock(position: SCNVector3, animated: Bool) {
  ...

  // Create box geometry
  let box = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
  box.firstMaterial!.diffuse.contents = curStyle.getContents() // "curStyle.getContents()" either returns UIColor or UIImage
  box.firstMaterial!.specular.contents = UIColor.whiteColor()

  // Add new block
  let newBlock = SCNNode(geometry: box)
  newBlock.position = position
  blockLayer.addChildNode(newBlock)
}

屏幕截图1-2 (我们的应用程序):

屏幕截图3-5 (块的理想视觉表示):

EN

回答 3

Stack Overflow用户

发布于 2016-07-08 04:08:49

我仍然认为有一些简单的事情,你可以做,这将大大改变你的场景是如何呈现。很抱歉没有使用您的代码,这个例子是我一直在做的事情。

现在你的场景只被周围的光线照亮。

代码语言:javascript
运行
复制
let aLight = SCNLight()
aLight.type = SCNLightTypeAmbient
aLight.color = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1.0)
let aLightNode = SCNNode()
aLightNode.light = aLight
scene.rootNode.addChildNode(aLightNode)

如果我只在我的场景中使用这盏灯,我会看到以下情况。注意所有的脸都是一样的,不管他们面对的方向是什么。有些游戏确实很好地达到了这种审美效果。

下面的代码块为这个场景添加了一个方向光。在这个光线中应用的转换不会对你的场景有效,重要的是要根据你想要的光的来源来定位光线。

代码语言:javascript
运行
复制
let dLight = SCNLight()
dLight.type = SCNLightTypeDirectional
dLight.color = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1.0)

let dLightNode = SCNNode()
dLightNode.light = dLight
var dLightTransform = SCNMatrix4Identity
dLightTransform = SCNMatrix4Rotate(dLightTransform, -90 * Float(M_PI)/180, 1, 0, 0)
dLightTransform = SCNMatrix4Rotate(dLightTransform, 37 * Float(M_PI)/180, 0, 0, 1)
dLightTransform = SCNMatrix4Rotate(dLightTransform, -20 * Float(M_PI)/180, 0, 1, 0)
dLightNode.transform = dLightTransform
scene.rootNode.addChildNode(dLightNode)

现在,我们根据每个脸相对于光线方向的角度对它们进行遮阳。

目前,SceneKit只支持使用SCNLightTypeSpot的阴影。使用聚光灯意味着我们需要同时定位(根据方向光)并定位它。我用它来代替方向灯。

代码语言:javascript
运行
复制
let sLight = SCNLight()
sLight.castsShadow = true
sLight.type = SCNLightTypeSpot
sLight.zNear = 50
sLight.zFar = 120
sLight.spotInnerAngle = 60
sLight.spotOuterAngle = 90

let sLightNode = SCNNode()
sLightNode.light = sLight
var sLightTransform = SCNMatrix4Identity
sLightTransform = SCNMatrix4Rotate(sLightTransform, -90 * Float(M_PI)/180, 1, 0, 0)
sLightTransform = SCNMatrix4Rotate(sLightTransform, 65 * Float(M_PI)/180, 0, 0, 1)
sLightTransform = SCNMatrix4Rotate(sLightTransform, -20 * Float(M_PI)/180, 0, 1, 0)
sLightTransform = SCNMatrix4Translate(sLightTransform, -20, 50, -10)
sLightNode.transform = sLightTransform
scene.rootNode.addChildNode(sLightNode)

在上面的代码中,我们首先告诉聚光灯投下阴影,默认情况下,场景中的所有节点都会投出阴影(这是可以更改的)。zNearzFar设置也很重要,必须进行指定,以便投射阴影的节点在与光源的距离范围内。此范围以外的节点不会投下阴影。

在阴影/阴影之后,还有许多其他的效果,你可以很容易地应用。场效应的深度为可供照相机使用。雾也同样容易被包括在内。

代码语言:javascript
运行
复制
scene.fogColor = UIColor.blackColor()
scene.fogStartDistance = 10
scene.fogEndDistance = 110
scenekitView.backgroundColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1.0)

更新结果显示,您可以从方向光获得阴影。通过更改其类型和设置orthographicScale,从上面修改聚光灯代码。orthographicScale的默认值似乎是1.0,显然不适合比1大得多的场景。

代码语言:javascript
运行
复制
let dLight = SCNLight()
dLight.castsShadow = true
dLight.type = SCNLightTypeDirectional
dLight.zNear = 50
dLight.zFar = 120
dLight.orthographicScale = 30

let dLightNode = SCNNode()
dLightNode.light = dLight
var dLightTransform = SCNMatrix4Identity
dLightTransform = SCNMatrix4Rotate(dLightTransform, -90 * Float(M_PI)/180, 1, 0, 0)
dLightTransform = SCNMatrix4Rotate(dLightTransform, 65 * Float(M_PI)/180, 0, 0, 1)
dLightTransform = SCNMatrix4Rotate(dLightTransform, -20 * Float(M_PI)/180, 0, 1, 0)
dLightTransform = SCNMatrix4Translate(dLightTransform, -20, 50, -10)
dLightNode.transform = dLightTransform
scene.rootNode.addChildNode(dLightNode)

生成以下图像。

场景大小为60x60,因此在这种情况下,将正交刻度设置为30会产生接近光线的物体的阴影。由于在绘制阴影地图时所使用的投影(正射与透视)的差异,定向光阴影看起来不同于光斑光。

票数 12
EN

Stack Overflow用户

发布于 2016-07-06 16:33:27

环境遮挡计算将给你最好的结果,但非常昂贵,特别是在动态变化的世界,它看起来是这样。

有几种方法来欺骗,并获得环境遮挡的外观。

这里有一个:在几何“标语牌”上放置透明的渐变阴影纹理,用于在所需的位置放置/呈现阴影。这将涉及到在确定放置什么标语牌之前,对新块周围的几何图形进行检查,以及阴影所需的纹理。但这可以使看起来非常好,在一个非常低的成本在多边形,绘制调用和过滤。这可能是最便宜的方法,而且看起来很好/很棒,而且只有在一个街区的世界里才能做到(看上去不错)。一个更有机的世界排除了这种技术。请原谅双关语。

或者,另一个类似的:将额外的纹理放置到具有阴影的对象中,并将其与对象中的其他纹理/材料混合。这将是有点微妙,我不是一个专家的材料在现场套件,所以不能肯定这是可能的和/或容易在其中。

或:使用混合纹理与顶点着色器,这是添加一个阴影从边缘触摸或其他需要/希望阴影基于你确定你想要的阴影,在什么地方,在多大程度。将仍然需要在地板/墙壁上的标牌戏法,除非你添加更多的顶点在平坦的表面,目的是为阴影的顶点阴影。

这是我为朋友的CD封面做的事..。展现了阴影的力量。它是正交的,不是真实的3D透视,但阴影给人的印象是深度,创造了空间的错觉:

票数 5
EN

Stack Overflow用户

发布于 2016-07-09 14:47:25

然而,上面(或下面)的所有答案似乎都是好的(在撰写本文时),

我所使用的(仅仅是为了设置一个简单的场景)是一个环境光(照亮所有方向的一切),以使事物可见。

然后一个全方位的光定位在你的场景中间,全能灯可以被升起(我的意思是Y向上)来照亮整个场景。全光给使用者一种阴影感,周围的光线使它更像太阳光。

例如:

想象一下,坐在客厅里(就像我现在一样),阳光透过你右边的窗户凝视着。

你显然可以看到一个区域的阴影,沙发没有得到阳光,但你仍然可以看到什么是在阴影中的细节。

现在!突然,你的汽车摆脱了周围的灯光繁荣!阴影现在是漆黑的,你不能再看到阴影中的东西了。

或者说周围的光线又回来了(真是令人松了一口气),但突然间全能灯就停止工作了。(大概是我的错:( )现在一切都是一样的,没有影子,没有差别,但是如果你把一张纸放在桌子上,从上面看,就没有影子了!)所以你认为这是桌子的一部分!在这样的世界里,你依赖于某物的轮廓来看它--你必须从侧面看桌子,看纸的厚度。

希望这能有所帮助(至少有一点)

注意:环境照明给出了类似于发射材料的效果

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38215784

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档