球体上两点之间的圆柱体方向,Scenekit,四元数 IOS [英] Cylinder Orientation between two points on a sphere, Scenekit, Quaternions IOS

查看:20
本文介绍了球体上两点之间的圆柱体方向,Scenekit,四元数 IOS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试使用 SceneKit 在球体外边缘的两点之间绘制圆柱体.我已经使用原始几何和带有 SCNRendering Delegate 的 openGL 在这两个点之间生成了一条线,但是现在我需要在这两个点之间生成一个圆柱体(嗯,不仅仅是两个,而是位于球体表面的任何两个 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.

无论如何,这是我最近的代码,但它不起作用,但这只是我迄今为止完成的近 2k 行代码的一小部分,没有预期的结果.我知道我可以转向更高级的东西,比如构建我自己的 SCNProgram 和/或 SCNRenderer,然后访问 GLSL、OpenGL 和 Metal 复杂性,但这似乎应该可以使用 Scenekit 并在 GLKit 矢量结构与 SCNVector 之间进行转换structs,但到目前为止还不可能:

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:

代码:

以下代码提取经度和纬度坐标并将它们投影到 3D 球体的表面上.这些坐标是通过我构建的专有函数返回的,在该函数中我收到了一个 {x,y,z} 坐标的 SCNVector3,该坐标准确地显示在我的 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;

我尝试过的其他代码也包括这种方法,事实上,我什至在 Objective-C 中构建了自己的 SCNVector3 和 SCNVector4 数学库,以查看我的数学方法产生的值是否与使用 GLKit 数学不同,但我得到了两种方法的结果相同.任何帮助都会很棒,但就目前而言,我不想跳入比 SceneKit 更复杂的东西.再过一两个月,我不会深入研究 Metal 和/或 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"是由另一个函数生成的 SCNNode,该函数将原始线几何图形强制进入该节点,然后将其返回给 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];

输入:

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

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

第二种方法使用硬编码数字来表示地球的半径和曲率,我展示这个只是为了显示总 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);
}

这篇关于球体上两点之间的圆柱体方向,Scenekit,四元数 IOS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆