ios - SceneKit自定义几何纹理错误

标签 ios swift opengl-es scenekit arkit

在 SceneKit 中,我试图创建一个带有纹理的简单自定义几何体。我只是想制作一个立方体,但每个面的方向都正确,因此 SCNBox 将无法工作。

形状非常好,但纹理映射完全错误。

这是几何图形的样子:

enter image description here

根据我的理解,每个面的纹理坐标应该是相同的。但是,当我以相同的顺序为每张脸添加点时,它会以类似的方式绘制错误。所以我玩弄了坐标的顺序,并得到了正面、背面和一侧的面,大部分都正确地绘制了。不过,我一直无法找出导致这种情况的模式或逻辑。

正面是每张脸的样子。

这是我的做法:

public struct face {
     var topLeft: SCNVector3
     var topRight: SCNVector3
     var bottomLeft: SCNVector3
     var bottomRight: SCNVector3
}

 func createCube(startFace: face, endFace: face) -> SCNGeometry {
    var vertices: [SCNVector3] = []
    var indices: [Int32] = []
    var textCords: [vector_float2] = []

    // Add table of contents to indices array because we're using polygons
    let polygons = 6 // cube so 6 faces
    indices.append(4) // front face
    indices.append(4) // left face
    indices.append(4) // right face
    indices.append(4) // top face
    indices.append(4) // bottom face
    indices.append(4) // back face

    //  - Convenience Values -

    // Indices offsets
    let startFaceVertex: Int32 = 0
    let endFaceVertex: Int32 = 4

    // Vertex indices
    let bottomLeftPos: Int32 = 0
    let bottomRightPos: Int32 = 1
    let topRightPos: Int32 = 2
    let topLeftPos: Int32 = 3

    // Texture Coordinates
    let topLeft = CGPoint(x: 0, y: 1)
    let topRight = CGPoint(x: 1, y: 1)
    let bottomLeft = CGPoint(x: 0, y: 0)
    let bottomRight = CGPoint(x: 1, y: 0)


    // Add vertices

    vertices.append(startFace.bottomLeft)
    vertices.append(startFace.bottomRight)
    vertices.append(startFace.topRight)
    vertices.append(startFace.topLeft)

    vertices.append(endFace.bottomLeft)
    vertices.append(endFace.bottomRight)
    vertices.append(endFace.topRight)
    vertices.append(endFace.topLeft)


    // Front Face

    indices.append(startFaceVertex + bottomLeftPos)
    indices.append(startFaceVertex + bottomRightPos)
    indices.append(startFaceVertex + topRightPos)
    indices.append(startFaceVertex +  topLeftPos)

    textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
    textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
    textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
    textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))

    // Left Face

    indices.append(startFaceVertex + bottomLeftPos)
    indices.append(endFaceVertex + bottomLeftPos)
    indices.append(endFaceVertex + topLeftPos)
    indices.append(startFaceVertex + topLeftPos)

    textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
    textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
    textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))
    textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))

    // Top Face

    indices.append(endFaceVertex + topLeftPos)
    indices.append(endFaceVertex + topRightPos)
    indices.append(startFaceVertex + topRightPos)
    indices.append(startFaceVertex + topLeftPos)

    textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
    textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
    textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
    textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))

    // Right Face

    indices.append(endFaceVertex + bottomRightPos)
    indices.append(startFaceVertex + bottomRightPos)
    indices.append(startFaceVertex + topRightPos)
    indices.append(endFaceVertex + topRightPos)

    textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
    textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
    textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))
    textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))

    // Bottom Face

    indices.append(startFaceVertex + bottomLeftPos)
    indices.append(startFaceVertex + bottomRightPos)
    indices.append(endFaceVertex + bottomRightPos)
    indices.append(endFaceVertex + bottomLeftPos)

    textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
    textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
    textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
    textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))


    // Back Face

    indices.append(endFaceVertex + bottomLeftPos)
    indices.append(endFaceVertex + bottomRightPos)
    indices.append(endFaceVertex + topRightPos)
    indices.append(endFaceVertex + topLeftPos)

    textCords.append(vector_float2(Float(topLeft.x), Float(topLeft.y)))
    textCords.append(vector_float2(Float(topRight.x), Float(topRight.y)))
    textCords.append(vector_float2(Float(bottomRight.x), Float(bottomRight.y)))
    textCords.append(vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)))


    // Create geometry

    let verticesSource = SCNGeometrySource(vertices: vertices)

    let uvData = Data(bytes: textCords, count: textCords.count * MemoryLayout<vector_float2>.size)
    let textureSource = SCNGeometrySource(data: uvData,
                                          semantic: .texcoord,
                                          vectorCount: textCords.count,
                                          usesFloatComponents: true,
                                          componentsPerVector: 2,
                                          bytesPerComponent: MemoryLayout<Float>.size,
                                          dataOffset: 0,
                                          dataStride: MemoryLayout<vector_float2>.size)

    let indexData = Data(bytes: indices,
                         count: indices.count * MemoryLayout<Int32>.size)
    let elements = SCNGeometryElement(data: indexData,
                                      primitiveType: .polygon,
                                      primitiveCount: polygons,
                                      bytesPerIndex: MemoryLayout<Int32>.size)

    return SCNGeometry(sources: [verticesSource, textureSource], elements: [elements])
}

解决方案

开始工作了,这是我的工作版本代码:

public struct face {
    var topLeft: SCNVector3
    var topRight: SCNVector3
    var bottomLeft: SCNVector3
    var bottomRight: SCNVector3
}

let topLeft = CGPoint(x: 0, y: 1)
let topRight = CGPoint(x: 1, y: 1)
let bottomLeft = CGPoint(x: 0, y: 0)
let bottomRight = CGPoint(x: 1, y: 0)

func createCube(startFace: face, endFace: face) -> SCNGeometry {
    var vertices: [SCNVector3] = []
    var indexTable: [Int32] = []
    var indices: [Int32] = []
    var textCords: [vector_float2] = []

    // Front Face
    addFace(face: startFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)

    // Left Face

    let leftFace = face(topLeft: endFace.topLeft, topRight: startFace.topLeft, bottomLeft: endFace.bottomLeft, bottomRight: startFace.bottomLeft, center: SCNVector3Zero, originOffset: startFace.originOffset)
    addFace(face: leftFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)

    // Top Face

    //let topFace = face(topLeft: startFace.topLeft, topRight: endFace.topLeft, bottomLeft: startFace.topRight, bottomRight: endFace.topRight, center: SCNVector3Zero, originOffset: startFace.originOffset)
    let topFace = face(topLeft: startFace.topLeft, topRight: endFace.topLeft, bottomLeft: startFace.topRight, bottomRight: endFace.topRight, center: SCNVector3Zero, originOffset: startFace.originOffset)
    addFace(face: topFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)

    // Right Face

    let rightFace = face(topLeft: startFace.topRight, topRight: endFace.topRight, bottomLeft: startFace.bottomRight, bottomRight: endFace.bottomRight, center: SCNVector3Zero, originOffset: startFace.originOffset)
    addFace(face: rightFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)

    // Bottom Face

    let bottomFace = face(topLeft: endFace.bottomLeft, topRight: startFace.bottomLeft, bottomLeft: endFace.bottomRight, bottomRight: startFace.bottomRight, center: SCNVector3Zero, originOffset: startFace.originOffset)
    addFace(face: bottomFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)

    // Back Face

    addFace(face: endFace, textureOffset: CGPoint.zero, textureSize: CGSize(width: 1, height: 1), toVertices: &vertices, indexTable: &indexTable, indices: &indices, textCords: &textCords)

    // Create geometry

    let verticesSource = SCNGeometrySource(vertices: vertices)

    let uvData = Data(bytes: textCords, count: textCords.count * MemoryLayout<vector_float2>.size)
    let textureSource = SCNGeometrySource(data: uvData,
                                          semantic: .texcoord,
                                          vectorCount: textCords.count,
                                          usesFloatComponents: true,
                                          componentsPerVector: 2,
                                          bytesPerComponent: MemoryLayout<Float>.size,
                                          dataOffset: 0,
                                          dataStride: MemoryLayout<vector_float2>.size)

    var finalIndices: [Int32] = []
    finalIndices.append(contentsOf: indexTable)
    finalIndices.append(contentsOf: indices)

    let indexData = Data(bytes: finalIndices,
                         count: finalIndices.count * MemoryLayout<Int32>.size)
    let elements = SCNGeometryElement(data: indexData,
                                      primitiveType: .polygon,
                                      primitiveCount: indexTable.count,
                                      bytesPerIndex: MemoryLayout<Int32>.size)

    return SCNGeometry(sources: [verticesSource, textureSource], elements: [elements])
}
fileprivate func addFace(face: face, textureOffset: CGPoint, textureSize: CGSize, toVertices: inout [SCNVector3], indexTable: inout [Int32], indices: inout [Int32], textCords: inout [vector_float2]) {
    toVertices.append(face.topRight)
    toVertices.append(face.topLeft)
    toVertices.append(face.bottomLeft)
    toVertices.append(face.bottomRight)

    let polygonPointCount: Int32 = 4
    indexTable.append(polygonPointCount)
    for _ in 0..<polygonPointCount {
        indices.append(Int32(indices.count))
    }

    textCords.append(vector_float2(Float(bottomRight.x + textureOffset.x + textureSize.width), Float(bottomRight.y + textureOffset.y)))
    textCords.append(vector_float2(Float(bottomLeft.x + textureOffset.x + textureSize.width), Float(bottomLeft.y + textureOffset.y)))
    textCords.append(vector_float2(Float(topLeft.x + textureOffset.x + textureSize.width), Float(topLeft.y + textureOffset.y)))
    textCords.append(vector_float2(Float(topRight.x + textureOffset.x + textureSize.width), Float(topRight.y + textureOffset.y)))
}

最佳答案

一个顶点位置和所有相关的属性组成一条记录。这意味着,如果必须使用不同的纹理坐标多次使用顶点位置,则必须为每个纹理坐标将顶点位置添加到缓冲区一次。不可能将元素数组与纹理坐标相关联。
您必须为立方体的每一侧创建一个具有 4 个顶点的顶点位置缓冲区,并为立方体的每一侧创建一个具有 4 个纹理坐标的纹理坐标缓冲区:

texureCoordsSide =[
    vector_float2(Float(bottomLeft.x), Float(bottomLeft.y)),
    vector_float2(Float(bottomRight.x), Float(bottomRight.y)),
    vector_float2(Float(topRight.x), Float(topRight.y)),
    vector_float2(Float(topLeft.x), Float(topLeft.y))]

// Front Face
vertices += [startFace.bottomLeft, startFace.bottomRight, startFace.topRight, startFace.topLeft];
textCords += texureCoordsSide

// Left Face
vertices += [endFace.bottomLeft, startFace.bottomLeft, startFace.topLeft, endFace.topLeft];
textCords += texureCoordsSide

// Top Face
vertices += [startFace.topLeft, startFace.topRight, endFace.topRight, endFace.topLeft];
textCords += texureCoordsSide

// Right Face
vertices += [startFace.bottomRight, endFace.bottomRight, endFace.topRight, startFace.topRight];
textCords += texureCoordsSide

// Bottom Face
vertices += [endFace.bottomRight, endFace.bottomLeft, startFace.bottomLeft, startFace.bottomRight];
textCords += texureCoordsSide

// Back Face
vertices += [endFace.bottomRight, endFace.bottomLeft, endFace.topLeft, endFace.topRight];
textCords += texureCoordsSide

元素数组 (indices) 必须包含从 0 到 23 的连续顺序的 24 个索引(立方体的 6 个边各有 4 个)。

关于ios - SceneKit自定义几何纹理错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46383685/

相关文章:

ios - Swift 3 和 backendless - 我如何上传图片?

ios - Alamofire 参数未正确序列化

opengl-es - 处理 OpenGL ES 中缺少 glDrawElementsBaseVertex

ios - Protocol Buffers Swift 生成图像未找到

ios - 保存字符串以快速解析数组

java - OpenGL ES 3.0 Java用颜色数组绘制顶点(每个顶点有不同的颜色)

ios - opengles在iphone中显示人脸

ios - 如何使动画 subview 中的按钮在该 subview 移动期间起作用?

ios - 在整个应用程序中禁用 UITextField 的自动更正

ios - 谁能识别这个对象?