圆球上的两个点之间的圆柱方向,Scenekit,四元数IOS

我一直在使用SceneKit在球体的外边缘上的两点之间画一个圆柱体。 我已经使用原始几何和使用SCNRendering Delegate的openGL在这两点之间产生了一条界线,但是现在我需要在这两个点之间产生一个圆柱(好吧,不只是两个,而是任意两个坐在球表面上的3Dvector)。 我已经为此工作了大约3天了,而且我已经完成了四元数的实现,但是现在我无法实现。 学术文章,科学研究,什么也没有,没有什么工作是在两个固定点之间重新排列一个圆柱体。 我需要一个algorithm来做到这一点。

无论如何,这是我最近的代码不起作用,但是这只是我已经完成的近2千行代码的一小段代码,没有预期的结果。 我知道我可以移动到更高级的东西,比如构build我自己的SCNProgram和/或SCNRenderer,然后访问GLSL,OpenGL和Metal复杂性,但是这似乎是应该可以使用Scenekit和GLKit向量结构之间的转换和从SCNVector结构,但到目前为止,这是不可能的:

码:

以下代码获取经度和纬度坐标并将其投影到3D球体的表面上。 这些坐标是通过一个专有函数返回的,我在这里构build了一个在我的3D球体上精确显示的{x,y,z}坐标的SCNVector3。 我在两组经度和纬度坐标之间绘制一条线,用原始线绘制线条穿过球体的中心。 所以,正如我上面提到的,我想要这个function,但是用圆柱体,而不是线(顺便提一下,这里列出的经度和纬度坐标是假的,它们是随机生成的,但都落在地球表面上)。

drawLine = [self lat1:37.76830 lon1:-30.40096 height1:tall lat2:3.97620 lon2:63.73095 height2:tall]; float cylHeight = GLKVector3Distance(SCNVector3ToGLKVector3(cooridnateSetOne.position), SCNVector3ToGLKVector3(coordinateSetTwo.position)); SCNCylinder * cylTest = [SCNCylinder cylinderWithRadius:0.2 height:cylHeight]; SCNNode * test = [SCNNode nodeWithGeometry:cylTest]; SCNMaterial *material = [SCNMaterial material]; [[material diffuse] setContents:[SKColor whiteColor]]; material.diffuse.intensity = 60; material.emission.contents = [SKColor whiteColor]; material.lightingModelName = SCNLightingModelConstant; [cylTest setMaterials:@[material]]; GLKVector3 u = SCNVector3ToGLKVector3(cooridnateSetOne.position); GLKVector3 v = SCNVector3ToGLKVector3(cooridnateSetTwo.position); GLKVector3 w = GLKVector3CrossProduct(u, v); GLKQuaternion q = GLKQuaternionMakeWithAngleAndVector3Axis(GLKVector3DotProduct(u,v), GLKVector3Normalize(w)); qw += GLKQuaternionLength(q); q = GLKQuaternionNormalize(q); SCNVector4 final = SCNVector4FromGLKVector4(GLKVector4Make(qx, qy, qz, qw)); test.orientation = final; 

我尝试过的其他代码包括了这种方法,事实上,我甚至在Objective-C中构build了自己的SCNVector3和SCNVector4math库,以查看我的math方法是否产生了不同于使用GLKitmath的值,但是我得到了相同的结果用这两种方法。 任何帮助都会很棒,但是现在我不想跳入比SceneKit更复杂的任何东西。 我不会在一两个月内钻入Metal和/或OpenGL。 谢谢!

编辑:

variables“cooridnateSetOne”和“cooridnateSetTwo”是由另一个函数产生的SCNNode,该函数强制原始线几何体进入该节点,然后将其返回给SCNScene的子类实现。

这是一个使用Objective-C的完整方法

首先,这是你如何使用它:

 SCNNode * testNode = [self lat1:-35 lon1:108 height1:tall lat2:-35 lon2:30 height2:0]; 

input:

第一个位置lat1 =第一个位置的纬度lon1 =第一个位置的经度height1 =第一个位置的距离地球lat2 =第二个位置的纬度lon2 =第二个位置的纬度height2 =第二个位置的距离地球的距离

第二种方法为上述每个位置创buildSCNVector3点:

 -(SCNNode *)lat1:(double)lat1 lon1:(double)lon1 height1:(float)height1 lat2:(double)lat2 lon2:(double)lon2 height2:(float)height2 { SCNVector3 positions[] = {[self lat:lat1 lon:lon1 height:height1], [self lat:lat2 lon:lon2 height:height2]}; float cylHeight = GLKVector3Distance(SCNVector3ToGLKVector3(positions[0]), SCNVector3ToGLKVector3(positions[1]))/4; SCNCylinder * masterCylinderNode = [SCNCylinder cylinderWithRadius:0.05 height:cylHeight]; SCNMaterial *material = [SCNMaterial material]; [[material diffuse] setContents:[SKColor whiteColor]]; material.lightingModelName = SCNLightingModelConstant; material.emission.contents = [SKColor whiteColor]; [masterCylinderNode setMaterials:@[material]]; SCNNode *mainLocationPointNodeTestA = [mainLocationPointNode clone]; SCNNode *mainLocationPointNodeTestB = [mainLocationPointNode clone]; mainLocationPointNodeTestA.position = positions[0]; mainLocationPointNodeTestB.position = positions[1]; SCNNode * mainParentNode = [SCNNode node]; SCNNode * tempNode2 =[SCNNode nodeWithGeometry:masterCylinderNode]; [mainParentNode addChildNode:mainLocationPointNodeTestA]; [mainParentNode addChildNode:mainLocationPointNodeTestB]; [mainParentNode addChildNode:tempNode2]; [mainParentNode setName:@"parentToLineNode"]; tempNode2.position = SCNVector3Make((positions[0].x+positions[1].x)/2, (positions[0].y+positions[1].y)/2, (positions[0].z+positions[1].z)/2); tempNode2.pivot = SCNMatrix4MakeTranslation(0, cylHeight*1.5, 0); GLKVector3 normalizedVectorStartingPosition = GLKVector3Make(0.0, 1.0, 0.0); GLKVector3 magicAxis = GLKVector3Normalize(GLKVector3Subtract(GLKVector3Make(positions[0].x/2, positions[0].y/2, positions[0].z/2), GLKVector3Make(positions[1].x/2, positions[1].y/2, positions[1].z/2))); GLKVector3 rotationAxis = GLKVector3CrossProduct(normalizedVectorStartingPosition, magicAxis); CGFloat rotationAngle = GLKVector3DotProduct(normalizedVectorStartingPosition, magicAxis); GLKVector4 rotation = GLKVector4MakeWithVector3(rotationAxis, acos(rotationAngle)); tempNode2.rotation = SCNVector4FromGLKVector4(rotation); return mainParentNode; } 

第二种方法使用硬编码的数字来表示地球的半径和曲率,我只是为了显示总共100%准确度所需的数字,这就是它的工作原理。 显然,您需要将其更改为适合您的场景的正确尺寸,但以下是方法。 这是http://www.gdal.org/index.html使用的方法的改编。 在这里可以find一个解释: http : //www.gdal.org/osr_tutorial.html 。 我把它放在一起非常快,但它的工作原理和准确性,随时可以根据您的喜好更改数字格式。

 -(SCNVector3)lat:(double)lat lon:(double)lon height:(float)height { double latd = 0.0174532925; double latitude = latd*lat; double longitude = latd*lon; Float64 rad = (Float64)(6378137.0); Float64 f = (Float64)(1.0/298.257223563); double cosLat = cos(latitude); double sinLat = sin(latitude); double FF = pow((1.0-f), 2); double C = 1/(sqrt(pow(cosLat,2) + FF * pow(sinLat,2))); double S = C * FF; double x = ((rad * C)*cosLat * cos(longitude))/(1000000/(1+height)); double y = ((rad * C)*cosLat * sin(longitude))/(1000000/(1+height)); double z = ((rad * S)*sinLat)/(1000000/(1+height)); return SCNVector3Make(y+globeNode.position.x, z+globeNode.position.y, x+globeNode.position.z); } 

下面是一个使用节点层次结构的快速演示(使圆柱体位于一个点上,其长度沿着局部z轴)和一个约束(使z轴看另一个点)。

 let root = view.scene!.rootNode // visualize a sphere let sphere = SCNSphere(radius: 1) sphere.firstMaterial?.transparency = 0.5 let sphereNode = SCNNode(geometry: sphere) root.addChildNode(sphereNode) // some dummy points opposite each other on the sphere let rootOneThird = CGFloat(sqrt(1/3.0)) let p1 = SCNVector3(x: rootOneThird, y: rootOneThird, z: rootOneThird) let p2 = SCNVector3(x: -rootOneThird, y: -rootOneThird, z: -rootOneThird) // height of the cylinder should be the distance between points let height = CGFloat(GLKVector3Distance(SCNVector3ToGLKVector3(p1), SCNVector3ToGLKVector3(p2))) // add a container node for the cylinder to make its height run along the z axis let zAlignNode = SCNNode() zAlignNode.eulerAngles.x = CGFloat(M_PI_2) // and position the zylinder so that one end is at the local origin let cylinder = SCNNode(geometry: SCNCylinder(radius: 0.1, height: height)) cylinder.position.y = -height/2 zAlignNode.addChildNode(cylinder) // put the container node in a positioning node at one of the points p2Node.addChildNode(zAlignNode) // and constrain the positioning node to face toward the other point p2Node.constraints = [ SCNLookAtConstraint(target: p1Node) ] 

对不起,如果你正在寻找一个特定于ObjC的解决scheme,但是我在OS X Swift操场上创build这个解决scheme的速度更快。 (另外,在iOS中需要更less的CGFloat转换,因为SCNVector3的元素types在SCNVector3就是Float 。)

谢谢,Rickster! 我已经采取了一些进一步,并从中取得了一个阶级:

 class LineNode: SCNNode { init( parent: SCNNode, // because this node has not yet been assigned to a parent. v1: SCNVector3, // where line starts v2: SCNVector3, // where line ends radius: CGFloat, // line thicknes radSegmentCount: Int, // number of sides of the line material: [SCNMaterial] ) // any material. { super.init() let height = v1.distance(v2) position = v1 let ndV2 = SCNNode() ndV2.position = v2 parent.addChildNode(ndV2) let ndZAlign = SCNNode() ndZAlign.eulerAngles.x = Float(M_PI_2) let cylgeo = SCNCylinder(radius: radius, height: CGFloat(height)) cylgeo.radialSegmentCount = radSegmentCount cylgeo.materials = material let ndCylinder = SCNNode(geometry: cylgeo ) ndCylinder.position.y = -height/2 ndZAlign.addChildNode(ndCylinder) addChildNode(ndZAlign) constraints = [SCNLookAtConstraint(target: ndV2)] } override init() { super.init() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } } 

我已经在iOS应用程序中成功testing了这个类,使用这个函数绘制了100行(oops cylinders:o)。

  func linesTest3() { let mat = SCNMaterial() mat.diffuse.contents = UIColor.whiteColor() mat.specular.contents = UIColor.whiteColor() for _ in 1...100 // draw 100 lines (as cylinders) between random points. { let v1 = SCNVector3( x: Float.random(min: -50, max: 50), y: Float.random(min: -50, max: 50), z: Float.random(min: -50, max: 50) ) let v2 = SCNVector3( x: Float.random(min: -50, max: 50), y: Float.random(min: -50, max: 50), z: Float.random(min: -50, max: 50) ) // Just for testing, add two little spheres to check if lines are drawn correctly: // each line should run exactly from a green sphere to a red one: root.addChildNode(makeSphere(v1, radius: 0.5, color: UIColor.greenColor())) root.addChildNode(makeSphere(v2, radius: 0.5, color: UIColor.redColor())) // Have to pass the parentnode because // it is not known during class instantiation of LineNode. let ndLine = LineNode( parent: scene.rootNode, // ** needed v1: v1, // line (cylinder) starts here v2: v2, // line ends here radius: 0.2, // line thickness radSegmentCount: 6, // hexagon tube material: [mat] ) // any material root.addChildNode(ndLine) } } 

100条随机线 问候。 (顺便说一句,我只能看到三维物体..我从来没有见过一个“线”在我的生活中:o)