【问题标题】:Procedural mesh not rendering lighting [SceneKit - Xcode]程序网格不渲染光照 [SceneKit - Xcode]
【发布时间】:2019-06-16 06:21:54
【问题描述】:

我对 swift 和 Xcode 还是很陌生,但是我已经用其他语言编程了好几年了。我正在尝试在 SceneKit (iOS) 中以程序方式创建 3D 网格。我的代码按预期工作,但是在运行应用程序时,生成的对象呈现纯黑色,忽略所有照明。我还在场景中添加了一个立方体以显示场景照明正在工作。

我想可能是着色器有问题,或者我需要定义几何体的法线来解决这个问题。我尝试过使用 SCNMaterial 的一些属性,但它们似乎并没有改变任何东西。

如果只是定义法线的情况,请您告诉我如何在 Swift / SceneKit 中执行此操作。或者也许我错过了其他东西,任何帮助将不胜感激。

截图如下:

我的代码如下:

  public static func CreateMesh (size: CGFloat, resolution: CGFloat) -> SCNNode? {
    let axisCount = Int(floor(size / resolution))
    let bottomLeft = CGVector(
        dx: CGFloat(-(axisCount / 2)) * resolution,
        dy: CGFloat(-(axisCount / 2)) * resolution
    )

    var verts = Array(
        repeating: Array(
            repeating: (i: Int(0), pos: SCNVector3.init(x: 0, y: 0, z: 0)),
            count: axisCount),
        count: axisCount
    )
    var vertsStream = [SCNVector3]()

    var i : Int = 0
    for x in 0...axisCount-1 {
        for y in 0...axisCount-1 {
            verts[x][y] = (
                i,
                SCNVector3(
                    x: Float(bottomLeft.dx + CGFloat(x) * resolution),
                    y: Float.random(in: 0..<0.1),
                    z: Float(bottomLeft.dy + CGFloat(y) * resolution)
                )
            )
            vertsStream.append(verts[x][y].pos)

            i += 1
        }
    }

    var tris = [(a: Int, b: Int, c: Int)]()
    var trisStream = [UInt16]()
    for x in 0...axisCount - 2 {
        for y in 0...axisCount - 2 {
            // Quad
            tris.append((
                a: verts[x][y].i,
                b: verts[x][y+1].i,
                c: verts[x+1][y+1].i
            ))
            tris.append((
                a: verts[x+1][y+1].i,
                b: verts[x+1][y].i,
                c: verts[x][y].i
            ))
        }
    }
    for t in tris {
        trisStream.append(UInt16(t.a))
        trisStream.append(UInt16(t.b))
        trisStream.append(UInt16(t.c))
    }

    // Create scene element
    let geometrySource = SCNGeometrySource(vertices: vertsStream)
    let geometryElement = SCNGeometryElement(indices: trisStream, primitiveType: .triangles)
    let geometryFinal = SCNGeometry(sources: [geometrySource], elements: [geometryElement])
    let node = SCNNode(geometry: geometryFinal)

    ////////////////////////
    // FIX MATERIAL
    ////////////////////////
    let mat = SCNMaterial()
    mat.diffuse.intensity = 1
    mat.lightingModel = .blinn
    mat.blendMode = .replace

    node.geometry?.materials = [mat]

    return node
}

【问题讨论】:

    标签: swift scenekit lighting procedural


    【解决方案1】:

    经过大量搜索,我终于找到了一个帖子,其中有一行代码如下所示:

    let gsNormals = SCNGeometrySource(normals: normalStream)
    

    所以我从那里设法解决了如何设置表面法线。在 Xcode / Swift 中涉及到像这样的更高级主题时,似乎真的没有很多在线内容/学习材料,这是非常不幸的。

    我已将其设置为创建抛物线形状平面,仅用于测试。但是这段代码将用于从高度图生成网格,现在应该很容易实现。我认为这是非常有用的代码,因此我将其包含在下面,以防其他人遇到与我相同的问题。

    public static func CreateMesh (size: CGFloat, resolution: CGFloat) -> SCNNode? {
        let axisCount = Int(floor(size / resolution))
        let bottomLeft = CGVector(
            dx: CGFloat(-(axisCount / 2)) * resolution,
            dy: CGFloat(-(axisCount / 2)) * resolution
        )
    
        /// Verticies ///
        var verts = Array(
            repeating: Array(
                repeating: (i: Int(0), pos: SCNVector3.init(x: 0, y: 0, z: 0)),
                count: axisCount),
            count: axisCount
        )
        var vertsStream = [SCNVector3]()
    
        var i = 0
        for x in 0...axisCount - 1 {
            for y in 0...axisCount - 1 {
                var dx = axisCount / 2 - x
                dx = dx * dx
                var dy = axisCount / 2 - y
                dy = dy * dy
                let yVal = Float(Double(dx + dy) * 0.0125)
                verts[x][y] = (
                    i: i,
                    pos: SCNVector3(
                        x: Float(bottomLeft.dx + CGFloat(x) * resolution),
                        //y: Float.random(in: 0..<0.1),
                        y: yVal,
                        z: Float(bottomLeft.dy + CGFloat(y) * resolution)
                    )
                )
    
                vertsStream.append(verts[x][y].pos)
    
                i += 1
            }
        }
        ///
    
        /// Triangles ///
        var tris = [(a: Int, b: Int, c: Int)]()
        var trisStream = [UInt32]()
        for x in 0...axisCount - 2 {
            for y in 0...axisCount - 2 {
                // Quad
                tris.append((
                    a: verts[x][y].i,
                    b: verts[x][y+1].i,
                    c: verts[x+1][y].i
                ))
                tris.append((
                    a: verts[x+1][y].i,
                    b: verts[x][y+1].i,
                    c: verts[x+1][y+1].i
                ))
            }
        }
        for t in tris {
            trisStream.append(UInt32(t.a))
            trisStream.append(UInt32(t.b))
            trisStream.append(UInt32(t.c))
        }
        ///
    
        /// Normals ///
        var normalStream = [SCNVector3]()
        for x in 0...axisCount - 1 {
            for y in 0...axisCount - 1 {
                // calculate normal vector perp to average plane
                let leftX = x == 0 ? 0 : x - 1
                let rightX = x == axisCount - 1 ? axisCount - 1 : x + 1
                let leftY = y == 0 ? 0 : y - 1
                let rightY = y == axisCount - 1 ? axisCount - 1 : y + 1
    
                let avgXVector = float3(verts[rightX][y].pos) - float3(verts[leftX][y].pos)
                let avgYVector = float3(verts[x][rightY].pos) - float3(verts[x][leftY].pos)
    
                // If you are unfamiliar with how to calculate normals
                // search for vector cross product, this is used to find
                // a vector that is orthogonal to two other vectors, in our
                // case perpendicular to the surface
                let normal = cross(
                    normalize(avgYVector),
                    normalize(avgXVector)
                )
    
                normalStream.append(SCNVector3(normal))
            }
        }
        ///
    
        // Create scene element
        let gsGeometry = SCNGeometrySource(vertices: vertsStream)
        let gsNormals = SCNGeometrySource(normals: normalStream)
    
        let geometryElement = SCNGeometryElement(indices: trisStream, primitiveType: .triangles)
        let geometryFinal = SCNGeometry(sources: [gsGeometry, gsNormals], elements: [geometryElement])
    
        let node = SCNNode(geometry: geometryFinal)
    
        let mat = SCNMaterial()
        mat.isDoubleSided = true
        mat.lightingModel = .blinn
    
        node.geometry?.materials = [mat]
    
        return node
    }
    

    【讨论】:

    • 我遇到了类似的情况 - 对我来说,问题是SCNGeometryElement init 方法需要UInt32s 的数组,就像您在答案中但不是在问题中一样。仅使用 Integer 会静默失败:/
    猜你喜欢
    • 2016-09-16
    • 2015-12-15
    • 2021-10-30
    • 1970-01-01
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    • 1970-01-01
    相关资源
    最近更新 更多