SceneKit 自定义几何纹理错误
Posted
技术标签:
【中文标题】SceneKit 自定义几何纹理错误【英文标题】:SceneKit Custom Geometry Texture Wrong 【发布时间】:2018-03-05 03:40:01 【问题描述】:在 SceneKit 中,我试图创建一个带有纹理的简单自定义几何体。我只是想制作一个立方体,但每个面的方向都正确,所以 SCNBox 不起作用。
形状非常好,但纹理映射完全错误。
这是几何图形的样子:
根据我的理解,每个面的纹理坐标应该是相同的。但是当我以相同的顺序为每个面添加点时,它同样会错误地绘制。所以我玩弄了坐标的顺序,让正面、背面和一侧的脸大部分都正确绘制。不过,我无法找出导致这种情况的模式或逻辑。
正面是每张脸的样子。
我的做法是这样的:
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)))
【问题讨论】:
【参考方案1】:一个顶点位置和所有相关属性形成一个记录。这意味着,如果必须使用不同的纹理坐标多次使用顶点位置,则必须为每个纹理坐标将顶点位置添加到缓冲区一次。无法将元素数组与纹理坐标相关联。 您必须为立方体的每一侧创建一个具有 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 个)。
【讨论】:
非常感谢!我更新了我的代码,效果很好以上是关于SceneKit 自定义几何纹理错误的主要内容,如果未能解决你的问题,请参考以下文章
iOS 上 Swift 中的自定义 SceneKit 几何图形不起作用,但等效的 Objective C 代码可以
SceneKit:增加用于 SCNNode 材质的纹理的清晰度?