C++ 为球体生成顶点

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4405787/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:19:11  来源:igfitidea点击:

Generating vertices for a sphere

c++3ddirectx

提问by Gayan

In the DirectX mobile lighting sample, a cylinder is generated in the following manner:

在 DirectX 移动照明示例中,圆柱体以下列方式生成:

for( DWORD i=0; i<50; i++ )
            {
                FLOAT theta = (2*D3DMX_PI*i)/(50-1);
                pVertices[2*i+0].position = D3DMXVECTOR3( (float)sin(theta),-1.0f, (float)cos(theta) );
                pVertices[2*i+0].normal   = D3DMXVECTOR3( (float)sin(theta), 0.0f, (float)cos(theta) );
                pVertices[2*i+1].position = D3DMXVECTOR3( (float)sin(theta), 1.0f, (float)cos(theta) );
                pVertices[2*i+1].normal   = D3DMXVECTOR3( (float)sin(theta), 0.0f, (float)cos(theta) );
            }

Is there a similar way to generate vertices for a sphere in DirectX Mobile(as a triangle strip or otherwise)? (AFAIK there's no D3DMXCreateSphere method)

是否有类似的方法可以在 DirectX Mobile 中为球体生成顶点(作为三角形条或其他形式)?(AFAIK 没有 D3DMXCreateSphere 方法)



The final solution .Thanks to quarternion for all his help.

最终的解决方案。感谢 quynion 的所有帮助。

void CreateSphere()
{
    const int iFactor = 20;
    int iPos = 0;

    arr_Vertices = new CUSTOMVERTEX[ui_VCount];
    ui_ShapeCount = iFactor *iFactor * 2; // use when rendering

    float arrV[iFactor* iFactor][3];

    for (DWORD j= 0; j < iFactor; j ++)
    {
        FLOAT theta = (D3DMX_PI*j)/(iFactor);

        for( DWORD i=0; i<iFactor; i++ )
        {
            iPos = j*iFactor+i;
            FLOAT phi = (2*D3DMX_PI*i)/(iFactor);
            arrV[iPos][0] = (float)(sin(theta)*cos(phi));
            arrV[iPos][1] = (float)(sin(theta)*sin(phi));
            arrV[iPos][2] = (float)(cos(theta));

            /*std::cout << "[" << j <<"][" << i << "] = " << arrV[iPos][0]  
                << "," << arrV[iPos][1] << "," << arrV[iPos][2] <<std::endl;*/
        }
    }

    int iNext = 0;

    for (DWORD j= 0; j < iFactor; j ++)
    { 

        for( DWORD i=0; i<iFactor; i++ )
        {
            if (i == iFactor - 1)
                iNext = 0;
            else iNext = i +1;

            iPos = (j*iFactor*6)+(i*6);
            arr_Vertices[iPos].position = D3DMXVECTOR3( arrV[j*iFactor+i][0], arrV[j*iFactor+i][1], arrV[j*iFactor+i][2]);
            arr_Vertices[iPos + 1].position = D3DMXVECTOR3( arrV[j*iFactor+iNext][0], arrV[j*iFactor+iNext][1], arrV[j*iFactor+iNext][2]);


            if (j != iFactor -1)
                arr_Vertices[iPos + 2].position = D3DMXVECTOR3( arrV[((j+1)*iFactor)+i][0], arrV[((j+1)*iFactor)+i][1], arrV[((j+1)*iFactor)+i][2]);
            else
                arr_Vertices[iPos + 2].position = D3DMXVECTOR3( 0, 0, -1); //Create a pseudo triangle fan for the last set of triangles

            arr_Vertices[iPos].normal = D3DMXVECTOR3( arr_Vertices[iPos].position.x, arr_Vertices[iPos].position.y, arr_Vertices[iPos].position.z);
            arr_Vertices[iPos + 1].normal = D3DMXVECTOR3( arr_Vertices[iPos+1].position.x, arr_Vertices[iPos+1].position.y, arr_Vertices[iPos+1].position.z);
            arr_Vertices[iPos + 2].normal = D3DMXVECTOR3( arr_Vertices[iPos+2].position.x, arr_Vertices[iPos+2].position.y, arr_Vertices[iPos+2].position.z);

            arr_Vertices[iPos + 3].position = D3DMXVECTOR3( arr_Vertices[iPos+2].position.x, arr_Vertices[iPos+2].position.y, arr_Vertices[iPos+2].position.z);
            arr_Vertices[iPos + 4].position = D3DMXVECTOR3( arr_Vertices[iPos+1].position.x, arr_Vertices[iPos+1].position.y, arr_Vertices[iPos+1].position.z);

            if (j != iFactor - 1)
                arr_Vertices[iPos + 5].position = D3DMXVECTOR3( arrV[((j+1)*iFactor)+iNext][0], arrV[((j+1)*iFactor)+iNext][1], arrV[((j+1)*iFactor)+iNext][2]);
            else
                arr_Vertices[iPos + 5].position = D3DMXVECTOR3( 0,0,-1);

            arr_Vertices[iPos + 3].normal = D3DMXVECTOR3( arr_Vertices[iPos+3].position.x, arr_Vertices[iPos+3].position.y, arr_Vertices[iPos+3].position.z);
            arr_Vertices[iPos + 4].normal = D3DMXVECTOR3( arr_Vertices[iPos+4].position.x, arr_Vertices[iPos+4].position.y, arr_Vertices[iPos+4].position.z);
            arr_Vertices[iPos + 5].normal = D3DMXVECTOR3( arr_Vertices[iPos+5].position.x, arr_Vertices[iPos+5].position.y, arr_Vertices[iPos+5].position.z);

            //std::cout << "[" << iPos <<"] = " << arr_Vertices[iPos].position.x << 
            //  "," << arr_Vertices[iPos].position.y <<
            //  "," << arr_Vertices[iPos].position.z <<std::endl;

            //std::cout << "[" << iPos + 1 <<"] = " << arr_Vertices[iPos + 1].position.x << 
            //  "," << arr_Vertices[iPos+ 1].position.y <<
            //  "," << arr_Vertices[iPos+ 1].position.z <<std::endl;

            //std::cout << "[" << iPos + 2 <<"] = " << arr_Vertices[iPos].position.x << 
            //  "," << arr_Vertices[iPos + 2].position.y <<
            //  "," << arr_Vertices[iPos + 2].position.z <<std::endl;
        }
    }
}

Should be usable with only a few adjustments. This creates a TRIANGLELIST but could be altered to output a set of triangle strips

只需稍作调整即可使用。这将创建一个 TRIANGLELIST 但可以更改为输出一组三角形条

采纳答案by Quaternion

Basic way of thinking about it:

基本思路:

First method not using a continuous triangle strip...

第一种不使用连续三角形条的方法......

It's been a while so I might make a mistake...

已经有一段时间了,所以我可能会犯错误......

A unit circle defined parametrically:

参数化定义的单位圆:

Where 0 =< theta < 2pi 
x = sin(theta);
y = cos(theta);

Now that we can define a single circle, imagine concentric rings on the x,y plane. Now imagine raising the inner most circle and as you raise it it pulls up the next ring, like a slinky... This visual only works for half a sphere.

现在我们可以定义一个圆,想象 x,y 平面上的同心环。现在想象一下抬起最里面的圆圈,当你抬起它时,它会拉起下一个环,就像一个紧身……这个视觉效果只适用于半个球体。

So the form that produces the shape of a sphere from the concentric rings is of course another circle which is orthogonal to the rings, the (z,y) plane... Of course we are only interested in finding the offset of the ring (how high or low it needs to be offset from the (x,y) plane.

所以从同心环产生球体形状的形式当然是另一个与环正交的圆,(z,y)平面......当然我们只对找到环的偏移量感兴趣(它需要从 (x,y) 平面偏移多高或多低。

Because we just need the offset we only need half a circle... and further the poles will only be a single point. Use a triangle fan at the poles and strips between each ring.

因为我们只需要偏移量,所以我们只需要半个圆……而且极点只会是一个点。在每个环之间的两极和条带上使用三角扇。

After this mental exercise see http://en.wikipedia.org/wiki/Sphereand search for "The points on the sphere with radius r can be parametrized via" and you'll see the parametric form after that line.

在此心理练习之后,请参阅http://en.wikipedia.org/wiki/Sphere并搜索“半径为 r 的球体上的点可以通过参数化通过”,您将在该行之后看到参数形式。

The normals are very easy the sphere should always be built around (0,0,0) and the sphere should always be built with a radius of 1 (so you can simply scale it to the desired size) and then each vertex on the circle surface is equal to the normal.

法线非常简单,球体应始终围绕 (0,0,0) 构建,球体应始终以半径 1 构建(因此您可以简单地将其缩放到所需的大小),然后将圆上的每个顶点表面等于法线。



The above method uses two triangle fans and a series of triangle strips... another method which produces a sphere with an even distribution of vertexes and can be drawn with a single triangle strip, although at the moment I'd go crazy trying to code it involves the following idea:

上面的方法使用两个三角形扇形和一系列三角形条带……另一种方法可以产生一个顶点分布均匀的球体,并且可以用单个三角形条带绘制,尽管目前我会疯狂地尝试编码它涉及以下想法:

Imagine a tetrahedron centered about the origin (the points are 1 unit from 0,0,0). It is a pretty pathetic representation of a sphere but it is an approximation. Now imagine that we find the midpoint on each of the four faces and then push that point out until it is on the surface of the sphere. Then we find the midpoints of those faces and push them out to the surface of the sphere...

想象一个以原点为中心的四面体(点距 0,0,0 1 个单位)。这是一个非常可悲的球体表示,但它是一个近似值。现在想象我们在四个面中的每一个上找到中点,然后将该点推出,直到它位于球体的表面上。然后我们找到这些面的中点并将它们推出到球体的表面......

tetrahdralSphere(int recursions){}

四面体球体(整数递归){}

Finding the mid point is very simple it is just the average of each of the x,y,z components. Then since the sphere is a unit sphere moving them to the surface is as simple as normalizing this new vector.

找到中点非常简单,它只是每个 x、y、z 分量的平均值。然后,由于球体是一个单位球体,因此将它们移动到表面就像归一化这个新向量一样简单。



Method one produces a point distribution that looks lines of longitude and latitude and produces a non uniform distribution (it looks just like a globe if using quads and a wire frame), it is quite easy to implement. The second method requires recursion so it a little more difficult but will look more uniform. If you want to get really complicated and hurt your head... then try distributing n points and then simulate a repellent force between points which moves them apart and then normalize them across the surface. There are all kinds of headaches that need to addressed to make this work effectively but then you have rather uniformly distributed points and you can control the number of vertices's and you'll have have the very start of appreciation of what it takes for modeling tools to find the minimal geometry to represent a model.

方法一产生一个看起来像经纬度线的点分布,并产生一个非均匀分布(如果使用四边形和线框,它看起来就像一个地球),它很容易实现。第二种方法需要递归,所以它有点困难,但看起来更统一。如果你想变得非常复杂并伤到你的头......然后尝试分布 n 个点,然后模拟点之间的排斥力,将它们分开,然后在整个表面标准化它们。为了使这项工作有效,需要解决各种令人头疼的问题,但是您拥有相当均匀分布的点,您可以控制顶点的数量,并且您将开始了解建模工具需要什么找到表示模型的最小几何图形。



Going with the first method. Draw a point at (0,0,1) then you need your first concentric ring (each ring will have the same number of points for simplicity).

使用第一种方法。在 (0,0,1) 处画一个点,然后您需要第一个同心环(为简单起见,每个环将具有相同的点数)。

Lets draw 10 points per ring... so phi will step in increments of 2pi/10 and lets draw 10 concentric rings

让每个环画 10 个点……所以 phi 将以 2pi/10 的增量步进,让我们画 10 个同心环

and we will draw 10 rings + 2 poles so theta will increase in increments of pi/12.

我们将绘制 10 个环 + 2 个极点,因此 theta 将以 pi/12 的增量增加。

//this psudo code places the points
//NOT TESTED
deltaTheta = pi/12;
deltaPhi = 2pi/10;
drawVertex(0,0,1) //north pole end cap
for(int ring; ring < 10; ring++){ //move to a new z - offset 
  theta += deltaTheta;
  for(int point; point < 10; point++){ // draw a ring
    phi += deltaPhi;
    x = sin(theta) * cos(phi)
    y = sin(theta) * sin(phi)
    z = cos(theta)
    drawVertex(x,y,z)
  }
}
drawVertex(0, 0, -1) //south pole end cap

回答by Alexandre C.

The usual way to triangulate a unit sphere is to build a tetrahedronor a icosahedronand

对单位球体进行三角测量的常用方法是构建一个四面体或一个二十面体,然后

  • If precision is enough, stop
  • Else, for each existing face:
  • Add a vertex at the midpoint of each edge and normalize it so that it is on the unit sphere
  • Replace the face with four new faces. One of the faces has the three new midpoints as corners (draw it on paper, and the other three faces will become obvious)
  • Loop.
  • 如果精度足够,停止
  • 否则,对于每个现有的人脸:
  • 在每条边的中点添加一个顶点并对其进行标准化,使其位于单位球体上
  • 用四个新面孔替换这张面孔。其中一个面有三个新的中点作为角(画在纸上,其他三个面会变得明显)
  • 环形。

To avoid duplicating vertexes at the edge midpoints, you need to track the existing vertices for reuse.

为避免在边中点复制顶点,您需要跟踪现有顶点以供重用。

回答by Goz

I explain in quite a bit of detail in my post in answer to this question.

我在我的帖子中详细解释了这个问题