用等距顶点创建一个球体

8
我正在尝试制作一个球形光线爆炸,以检查碰撞,并根据每个光线击中的内容或位置发生特定交互。这就是为什么我使用光线而不是一些更简单的东西,例如OverlapSphere的原因。
我寻找如何制作球体的原因是因为我可以使用相同的数学公式来处理我的光线,让它们到达球体的顶点。但是,我找到的制作球体的方法都会使线条在接近极点处变得更加密集,这很容易理解。但是,正如您可以想象的那样,对于我的当前项目来说并没有太大用处。
简而言之:如何制作具有等距顶点的球体?如果不是完全等距的也可以,只要它足够接近即可。如果发生这种情况,如果适用的话,能否告诉我差异有多大以及在哪里。

额外说明: 我已经查看了thisthis,但其中的数学公式非常难懂,所以我一直在寻找的东西可能一直就在眼前。


让它们完全等距可能会很困难。但如果你只需要一个相当接近的东西,我在这里的答案中提供的一些方法可能适合你:http://stackoverflow.com/questions/24137198/opengl-es-2-0-sphere. - Reto Koradi
为什么你要这样做,完全可以在Unity中内置完成呢? - Fattie
只是为了明确起见,Unity内置了解决您要实现的目标的解决方案。而且,如果出于任何原因您想要“均匀间隔的顶点”...那也是内置在Unity中的!嘿! - Fattie
你的解决方案看起来相当不错。不幸的是,在我处理这个问题时,你提到的功能并不存在。那是在4.6版本之前,当时没有icosphere或所需版本的PhysX支持。无论如何,感谢你的建议。 - Matrixian
6个回答

7
你可以使用一个球形网格。由于顶点分布在等边三角形上,所以你的顶点保证是等距的。

enter image description here

构建正二十面体球体的方法是,首先制作一个正二十面体,然后按照文章中所述的方法递归地将其面分割成更小的三角形。


(请注意,著名文章中有几个著名的错误。)只是 FYI 注意,您绝对不必这样做,因为它完全内置于 Unity 中。 - Fattie
这正是我正在寻找的。您能看一下这个问题吗?https://dev59.com/K1YN5IYBdhLWcg3w27cv - Aaron Franke

5
你是否知道Unity提供给你的球体实际上是为了这个目的而设计的?
也就是说,Unity内置的球体的整个存在意义在于,点相对平滑地分布...大致等距,正如你所描述的那样。

enter image description here

要在Unity中生成这样的球体,只需执行以下操作:

enter image description here

您可以立即访问顶点,正如您所知道的那样

Mesh mesh = GetComponent<MeshFilter>().mesh;
Vector3[] vv = mesh.vertices;
int kVerts=vv.Length
for (int i=0; i<kVerts; ++i)
  Debug.Log ... vv[i] 

请注意,您可以通过以下方法轻松检查它们所在的“球的哪个部分”(例如)检查它们距离您的“城市”(或其他内容)有多远,或者只需检查(例如)z值以查看它们位于哪个半球等。

此外...

请注意。关于您想要做这件事的总体原因:

但根据每个光线击中的内容或位置发生特定的交互

请注意,使用PhysX(Unity中完全内置的游戏物理引擎)可以轻松实现这一点。确实,我从来没有看到过碰撞而不根据“击中位置”做出“具体”的反应!

例如,您可以使用http://docs.unity3d.com/ScriptReference/RaycastHit-point.html获取接触点。

值得注意的是,在随意编程中,几乎不可能编写接近于PhysX性能的东西。

希望这会让事情变得更容易!


4
  1. 将球体切割成N个圆
  2. 计算其周长
  3. 将其除以创建切片的相同角度
    • 这会给你顶点的数量
    • 也会给你圆内的角度步进
  4. 发射射线

这是我在C++ + OpenGL中编写它的方式:

// draw unit sphere points (r=1 center=(0,0,0)) ... your rays directions
int ia,na,ib,nb;
double x,y,z,r;
double a,b,da,db;
na=16;                                  // number of slices
da=M_PI/double(na-1);                   // latitude angle step
for (a=-0.5*M_PI,ia=0;ia<na;ia++,a+=da) // slice sphere to circles in xy planes
    {
    r=cos(a);                           // radius of actual circle in xy plane
    z=sin(a);                           // height of actual circle in xy plane
    nb=ceil(2.0*M_PI*r/da);
    db=2.0*M_PI/double(nb);             // longitude angle step
    if ((ia==0)||(ia==na-1)) { nb=1; db=0.0; }  // handle edge cases
    for (b=0.0,ib=0;ib<nb;ib++,b+=db)   // cut circle to vertexes
        {
        x=r*cos(b);                     // compute x,y of vertex
        y=r*sin(b);
        // this just draw the ray direction (x,y,z) as line in OpenGL
        // so you can ignore this
        // instead add the ray cast of yours
        double w=1.2;
        glBegin(GL_LINES);
        glColor3f(1.0,1.0,1.0); glVertex3d(x,y,z);
        glColor3f(0.0,0.0,0.0); glVertex3d(w*x,w*y,w*z);
        glEnd();
        }
    }

这就是它的样子:
R、G、B 线表示球坐标系的三个轴 X、Y、Z。
白色线条是你的顶点(白色)加方向(灰色)。
请注意:
- 不要忘记包含 math.h。 - 使用自己的 OpenGL 替换代码中的相关内容。

只是重申一下 - Spektre,这是针对Unity3D的。在Unity3D中,绝对内置了执行此操作的功能,似乎OP只是不知道而已。顺便提一下,Unity与C++或OpenGL编程完全没有关系。(再次强调,您只需“点击一个按钮”,即可获得OP想要的结果。) - Fattie
@Spektre 你知道如何将生成的顶点连接成三角形吗?我已经连续尝试了两天,到目前为止,我得到了一种不包括极点的方法:for( var i = 0; i < N; ++i ) { int b = i + 8 , c = b + 13; if( c < N ) Triangle( i, b, c ); b = i + 13; c = b + 8; if( c < N ) Triangle( c, b, i ); }之后,我会手动关闭极点(有时会导致奇怪的角度),例如:index < 21 && index >= count - 21。 - Vlad
如果您想要更好的网格外观,那么您需要将极点作为顶点,这仅适用于特定数量的点...对于球体网格化来说,更容易的方法是使用细分,例如这样,但是要从二十面体开始。还有更多疯狂的方法,比如这个这个 - Spektre
谢谢,我也会尝试一下。此外,我有点连接了这些点,请看:https://i.imgur.com/kMRCCSA.mp4。 - Vlad

2
如果您需要4、6、8、12或20个顶点,那么您可以像Platonic solid一样拥有完全等距的顶点,这些立体图形都适合放在一个球内。这些点的实际坐标应该很容易获得。对于其他数量的顶点,您可以使用其他多面体,并缩放顶点使其位于一个球上。如果您需要大量的点,则geodesic dome可能是一个很好的基础。C60 bucky-ball 可能是一个具有60个点的良好基础。对于大多数情况,您应该能够找到3D模型,从中提取坐标。

只是重申一下 - Salix,这是针对Unity3D的。在Unity3D中,绝对内置了执行此操作的功能,似乎OP并不知道这一点。 - Fattie

0

我认为控制球面上的点最简单的方法是使用球坐标。然后,您可以通过使用两个角度(rho和phi)和半径来控制球面上的点的位置。

填充旋转球面周围点的示例代码(仅供娱乐):

var time = 1; // Increment this variable every frame to see the rotation
var count = 1000;
for (int i = 0; i < count; i++)
{
    var rho = time + i; 
    var phi = 2 * Math.PI * i / count;
    var x = (float)(radius * Math.Sin(phi) * Math.Cos(rho));
    var z = (float)(radius * Math.Sin(phi) * Math.Sin(rho));
    var y = (float)(radius * Math.Cos(phi));
    Draw(x, y, z); // your drawing code for rendering the point
}

这会像在球体周围画一个曲线环一样吗? - Fattie
不,它应该画整个球体。如果设置“rho”为固定值,则曲线循环会达到此目的。例如,如果您使用“rho = i%2”,则只会绘制2个循环,依此类推。 - glopes

0

正如一些答案已经建议的那样,使用基于二十面体的解决方案。这个源代码非常容易获取(我自己也写了好几次),但是我发现优秀的Primitives Pro插件在许多其他情况下非常方便,所以我总是使用他们的球体而不是内置的Unity球体。

链接到Primitives Pro组件

Primitives Pro选项


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接