在一个球体,Scenekit,四元数IOS两点之间的圆柱方向球体、圆柱、两点、方向

2023-09-08 01:07:19 作者:拿根辣条砸死你

我一直想画两个点之间的圆柱体上使用SceneKit球体的外缘。我已经产生这两个点之间的线路使用基本的几何形状和OpenGL与SCNRendering代表,但现在我需要制作(这两者之间的圆桶,不是两个,而是任意两个3D矢量坐球的表面上)。我一直对这个约3天直现在,我已经通过了所有我能找到实现四元数要做到这一点了,但因为它的立场,我不能让它的工作。学术文章,科学研究,仅此而已,没有什么工作重新调整两个固定点之间的圆柱体。我需要一个算法来做到这一点。

I've been trying to draw a cylinder between two points on the outer edge of a sphere using SceneKit. I have already produced a line between these two points using primitive geometry and openGL with SCNRendering Delegate, but now I need to produce a cylinder between these two (well, not just two, but any two 3D vectors that sit on the surface of the sphere). I've been working on this for about 3 days straight now, and I've gone through everything I could find on implementing Quaternions to make this happen, but as it stands, I can't get it to work. Academic articles, scientific studies, and nothing, nothing is working to realign a cylinder between two fixed points. I need an algorithm to do this.

总之,这是我最近的code不工作,但是这只是一个小片段的近2K线code,我经历了这么远没有预期的结果工作。我知道我可以移动的东西更先进的像建设自己的SCNProgram和/或SCNRenderer来然后访问GLSL,OpenGL和金属的复杂性,但是这似乎喜欢的事,应该使用Scenekit和GLKit矢量结构之间的转换和从SCNVector有可能结构,但到目前为止,这是不可能的:

Anyway, here's my most recent code that doesn't work, but this is just a small snippet of nearly 2k lines of code I've worked through so far without the intended result. I know I can move to something more advanced like building my own SCNProgram and/or SCNRenderer to then access GLSL, OpenGL, and Metal complexity, but this seems like something that should be possible using Scenekit and converting between GLKit vector structs to and from SCNVector structs, but so far it's impossible:

code:

下面code摄取的经度和纬度坐标,它们投射到3D球体的表面。这些坐标是通过一个专有的功能,我建,我收到的SCNVector3 {X,Y,Z坐标}的显示精确在我的3D球体返回。我画了两套经度和纬度之间的线坐标,其中正在使用的原语绘制的线通过球心的拍摄。所以,正如我上面提到的,我想这相同的功能,但用气瓶,未行(顺便说一下,经度和纬度坐标此处所列均为假的,它们是随机生成的,但无论落在地球表面)。

The following code ingests Longitude and Latitude coordinates and projects them onto the surface of a 3D sphere. These coordinates are returned through a proprietary function I build where I received a SCNVector3 of {x,y,z} coordinates that display accurately on my 3D sphere. I draw a line between two sets of Longitude and Latitude coordinates where the lines that are drawn using primitives shoot through the center of the sphere. So, as I mentioned above, I want this same functionality but with cylinders, not lines (by the way, the longitude and latitude coordinates listed here are bogus, they are randomly generated but both fall on the Earth's surface).

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));
q.w += GLKQuaternionLength(q);
q = GLKQuaternionNormalize(q);
SCNVector4 final = SCNVector4FromGLKVector4(GLKVector4Make(q.x, q.y, q.z, q.w));

test.orientation = final;

其他code我试过,包括同样的排序方法,其实,我甚至建立了自己的SCNVector3和SCNVector4数学库在Objective-C,看看我的数学方法产生不同的值比使用GLKit数学,但我得到这两种方法的结果相同。任何帮助将是真棒,但现在,我不希望跳进什么比SceneKit更加复杂。我不会深入到金属和/或OpenGL的一两个月。谢谢!

Other code I've tried includes this same sort of method, in fact, I even built my own SCNVector3 and SCNVector4 Math libraries in Objective-C to see if my math methods produced different values than using GLKit maths, but I get the same results with both methods. Any help would be awesome, but for now, I'm not looking to jump into anything more complicated than SceneKit. I won't be diving into Metal and/or OpenGL for another month or two. Thanks!

编辑:

变量cooridnateSetOne和cooridnateSetTwo是由另一个函数,迫使原线几何这个节点产生SCNNodes,然后返回到子类实现SCNScene的。

The variables "cooridnateSetOne" and "cooridnateSetTwo" are SCNNodes that are produced by another function that forces a primitive line geometry into this node and then returns it to a subclass implementation of SCNScene.

推荐答案

下面是使用Objective-C的整个方法

Here's an entire method using Objective-C

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

First, here's how you use it:

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

输入:

1rst位置 LAT1 =纬度1rst位置 lon1 =东经1rst位置 height1 =距离地球为1rst位置 LAT2 =纬度第二位置 lon2 =纬度第二位置 身高2 =距离地球为第二位置

1rst location lat1 = latitude of 1rst location lon1 = longitude of 1rst location height1 = distance from earth for 1rst location lat2 = latitude of 2nd location lon2 = latitude of 2nd location height2 = distance from earth for 2nd location

第二种方法创建SCNVector3点问题每个位置上面的:

The second method creates the SCNVector3 points for each location in question above:

-(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;
}

这第二种方法是使用地球的半径和曲率硬codeD数字,我展示这只是为了显示所需的总准确率100%的数字,这是它是如何工作的。你会想更改为正确的尺寸为场景,很明显,但这里的方法。这是使用的 http://www.gdal.org/index.html 的方法的改编。一种解释是在这里找到: http://www.gdal.org/osr_tutorial.html 。我把这个一起非常快,但它的工作原理和准确,随意更改数字格式以自己的喜好。

This second method uses hard coded numbers for earth's radius and curvature, I'm showing this just to show the numbers required for total 100% accuracy, this is how it works. You'll want to change this to the correct dimensions for your scene, obviously, but here's the method. This is an adaptation of methods used by http://www.gdal.org/index.html. An explanation an be found here: http://www.gdal.org/osr_tutorial.html. I put this together very quickly but it works and is accurate, feel free to change the number formats to your liking.

-(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);
}