首页 文章

使用 SceneKit 在两点之间画一条线

提问于
浏览
19

我有两个类型 SCNVector3 的点(我们称之为 pointA 和 pointB)。我想在它们之间画一条线。看起来应该很容易,但找不到办法。

我看到两个选项,都有问题:

使用半径非常小的 SCNCylinder,长度为| pointA-pointB |然后定位 it/rotate 它。

使用自定义 SCNGeometry 但不确定如何;或许必须定义两个三角形才能形成一个非常薄的矩形?

似乎应该有一种更简单的方法来做到这一点,但我似乎无法找到一个。

编辑:使用三角形方法给我这个在(0,0,0)和(10,10,10)之间画一条线:

CGFloat delta = 0.1;
SCNVector3 positions[] = {  SCNVector3Make(0,0,0),
    SCNVector3Make(10, 10, 10),
    SCNVector3Make(0+delta, 0+delta, 0+delta),
    SCNVector3Make(10+delta, 10+delta, 10+delta)};
int indicies[] = {
    0,2,1,
    1,2,3
};

SCNGeometrySource *vertexSource = [SCNGeometrySource geometrySourceWithVertices:positions count:4];
NSData *indexData = [NSData dataWithBytes:indicies length:sizeof(indicies)];
SCNGeometryElement *element = [SCNGeometryElement geometryElementWithData:indexData primitiveType:SCNGeometryPrimitiveTypeTriangles primitiveCount:2 bytesPerIndex:sizeof(int)];
SCNGeometry *line = [SCNGeometry geometryWithSources:@[vertexSource] elements:@[element]];

SCNNode *lineNode = [SCNNode nodeWithGeometry:line];
[root addChildNode:lineNode];

但是有一些问题:由于法线,你只能从一边看到这条线!它从另一边看不见。此外,如果“delta”太小,则根本看不到该线。实际上,它在技术上是一个矩形,而不是我想要的线,如果我想绘制多个连接线,可能会导致小的图形故障。

6 回答

  • 14

    有很多方法可以做到这一点。

    如上所述,您的自定义几何方法有一些缺点。您应该能够通过为其材质赋予两面性属性来纠正它从一侧看不见的问题。不过,你仍然可能有 two-dimensional 的问题。

    您还可以修改自定义几何体以包含更多三角形,这样您就可以获得具有三个或更多边的管形状,而不是平面矩形。或者在几何体源中只有两个点,并使用SCNGeometryPrimitiveTypeLine几何元素类型让 Scene Kit 在它们之间绘制线段。 (虽然使用阴影线绘制渲染样式不会像使用阴影 polygons.)那样灵活

    您还可以使用您提到的SCNCylinder方法(或任何其他 built-in 基本形状)。请记住,几何是在它们自己的局部(也称为 Model)坐标空间中定义的,Scene Kit 相对于节点定义的坐标空间进行解释。换句话说,你可以在所有维度中定义一个 1.0 单位宽的圆柱体(或盒子或胶囊或平面或其他),然后使用 rotation/scale/position 或包含该几何体的SCNNode的变换使其变长,变薄和拉伸你想要两点。 (另请注意,由于你的线很细,你可以减少你正在使用的任何 built-in 几何体的segmentCount,因为那么多细节都不会 visible.)

    另一个选项是SCNShape类,它允许您从 2DBézier 路径创建拉伸 3D 对象。计算出正确的变换以获得连接两个任意点的平面听起来像一些有趣的数学,但是一旦你这样做,你可以轻松地将你的点与你选择的任何形状的线连接起来。

  • 18

    这是 Swift 中的一个简单扩展:

    extension SCNGeometry {
        class func lineFrom(vector vector1: SCNVector3, toVector vector2: SCNVector3) -> SCNGeometry {
            let indices: [Int32] = [0, 1]
    
            let source = SCNGeometrySource(vertices: [vector1, vector2])
            let element = SCNGeometryElement(indices: indices, primitiveType: .Line)
    
            return SCNGeometry(sources: [source], elements: [element])
    
        }
    }
    
  • 9

    下面的行(0,0,0)到(10,10,10)的新代码。我不确定它是否可以进一步改进。

    SCNVector3 positions[] = {
        SCNVector3Make(0.0, 0.0, 0.0),
        SCNVector3Make(10.0, 10.0, 10.0)
    };
    
    int indices[] = {0, 1};
    
    SCNGeometrySource *vertexSource = [SCNGeometrySource geometrySourceWithVertices:positions
                                                                              count:2];
    
    NSData *indexData = [NSData dataWithBytes:indices
                                       length:sizeof(indices)];
    
    SCNGeometryElement *element = [SCNGeometryElement geometryElementWithData:indexData
                                                                primitiveType:SCNGeometryPrimitiveTypeLine
                                                               primitiveCount:1
                                                                bytesPerIndex:sizeof(int)];
    
    SCNGeometry *line = [SCNGeometry geometryWithSources:@[vertexSource]
                                                elements:@[element]];
    
    SCNNode *lineNode = [SCNNode nodeWithGeometry:line];
    
    [root addChildNode:lineNode];
    
  • 9

    这是一个解决方案

    class func lineBetweenNodeA(nodeA: SCNNode, nodeB: SCNNode) -> SCNNode {
        let positions: [Float32] = [nodeA.position.x, nodeA.position.y, nodeA.position.z, nodeB.position.x, nodeB.position.y, nodeB.position.z]
        let positionData = NSData(bytes: positions, length: MemoryLayout<Float32>.size*positions.count)
        let indices: [Int32] = [0, 1]
        let indexData = NSData(bytes: indices, length: MemoryLayout<Int32>.size * indices.count)
    
        let source = SCNGeometrySource(data: positionData as Data, semantic: SCNGeometrySource.Semantic.vertex, vectorCount: indices.count, usesFloatComponents: true, componentsPerVector: 3, bytesPerComponent: MemoryLayout<Float32>.size, dataOffset: 0, dataStride: MemoryLayout<Float32>.size * 3)
        let element = SCNGeometryElement(data: indexData as Data, primitiveType: SCNGeometryPrimitiveType.line, primitiveCount: indices.count, bytesPerIndex: MemoryLayout<Int32>.size)
    
        let line = SCNGeometry(sources: [source], elements: [element])
        return SCNNode(geometry: line)
    }
    

    如果您想更新线宽或与修改绘制线的属性相关的任何内容,您将需要在 SceneKit 的渲染回调中使用其中一个 openGL 调用:

    func renderer(aRenderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: NSTimeInterval) {
        //Makes the lines thicker
        glLineWidth(20)
    }
    
  • 1

    所以在你的 ViewController.cs 里面定义你的矢量点并调用一个 Draw 函数,然后在最后一行那里 - 它只是旋转它看看点 b。

    var a = someVector3;
           var b = someOtherVector3;
           nfloat cLength = (nfloat)Vector3Helper.DistanceBetweenPoints(a, b);
           var cyclinderLine = CreateGeometry.DrawCylinderBetweenPoints(a, b, cLength, 0.05f, 10);
           ARView.Scene.RootNode.Add(cyclinderLine);
           cyclinderLine.Look(b, ARView.Scene.RootNode.WorldUp, cyclinderLine.WorldUp);
    

    创建一个静态 CreateGeomery 类并将此静态方法放在那里

    public static SCNNode DrawCylinderBetweenPoints(SCNVector3 a,SCNVector3 b, nfloat length, nfloat radius, int radialSegments){
    
             SCNNode cylinderNode;
             SCNCylinder cylinder = new SCNCylinder();
             cylinder.Radius = radius;
             cylinder.Height = length;
             cylinder.RadialSegmentCount = radialSegments;
             cylinderNode = SCNNode.FromGeometry(cylinder);
             cylinderNode.Position = Vector3Helper.GetMidpoint(a,b);
    
             return cylinderNode;
            }
    

    您可能还希望在静态助手类中使用这些实用程序方法

    public static double DistanceBetweenPoints(SCNVector3 a, SCNVector3 b)
            {
             SCNVector3 vector = new SCNVector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
             return Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z);
            }
    
        public static SCNVector3 GetMidpoint(SCNVector3 a, SCNVector3 b){
    
            float x = (a.X + b.X) / 2;
            float y = (a.Y + b.Y) / 2;
            float z = (a.Z + b.Z) / 2;
    
            return new SCNVector3(x, y, z);
        }
    

    对于我所有的 Xamarin c#homies。

  • 1

    这是一个使用三角形的解决方案,它独立于线的方向工作。它是使用叉积来构造的,以获得垂直于线的点。所以你需要一个小的 SCNVector3 扩展,但它在其他情况下也可能会派上用场。

    private func makeRect(startPoint: SCNVector3, endPoint: SCNVector3, width: Float ) -> SCNGeometry {
        let dir = (endPoint - startPoint).normalized()
        let perp = dir.cross(SCNNode.localUp) * width / 2
    
        let firstPoint = startPoint + perp
        let secondPoint = startPoint - perp
        let thirdPoint = endPoint + perp
        let fourthPoint = endPoint - perp
        let points = [firstPoint, secondPoint, thirdPoint, fourthPoint]
    
        let indices: [UInt16] = [
            1,0,2,
            1,2,3
        ]
        let geoSource = SCNGeometrySource(vertices: points)
        let geoElement = SCNGeometryElement(indices: indices, primitiveType: .triangles)
    
        let geo = SCNGeometry(sources: [geoSource], elements: [geoElement])
        geo.firstMaterial?.diffuse.contents = UIColor.blue.cgColor
        return geo
    }
    

    SCNVector3 扩展:

    import Foundation
    import SceneKit
    
    extension SCNVector3
    {
        /**
         * Returns the length (magnitude) of the vector described by the SCNVector3
         */
        func length() -> Float {
            return sqrtf(x*x + y*y + z*z)
        }
    
        /**
         * Normalizes the vector described by the SCNVector3 to length 1.0 and returns
         * the result as a new SCNVector3.
         */
        func normalized() -> SCNVector3 {
            return self / length()
        }
    
        /**
          * Calculates the cross product between two SCNVector3.
          */
        func cross(_ vector: SCNVector3) -> SCNVector3 {
            return SCNVector3(y * vector.z - z * vector.y, z * vector.x - x * vector.z, x * vector.y - y * vector.x)
        }
    }
    

相关问题