生成球体顶点

14

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

在DirectX Mobile中是否有一种类似的方法来生成球体的顶点(以三角带或其他方式)? (据我所知,没有D3DMXCreateSphere方法)


最终解决方案。感谢quarternion提供的所有帮助。

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

只需要进行少量调整即可使用。此代码创建了一个TRIANGLELIST但可以修改为输出三角形带。


你是否有任何关于两个顶点之间最小距离的要求? - Beta
没有这样的要求。我一直在尝试想出一个公式,但是失败了。如果我能掌握基础知识,我认为我就能调整它以提高性能。 - Gayan
使用 D3DXCreateSphere() 有什么问题吗?如果您不想要 ID3DXMesh 对象,至少可以将其创建的顶点复制到其他地方。MSDN 文档在这里:http://msdn.microsoft.com/en-us/library/bb172795(VS.85).aspx - tbridge
жҠұжӯүtbridgeгҖӮжҲ‘еә”иҜҘжҸҗеҲ°иҝҷжҳҜз”ЁдәҺdirectx移еҠЁAPIзҡ„гҖӮжІЎжңүзұ»дјјдәҺD3DXCreateSphereпјҲпјүзҡ„ж–№жі•гҖӮ - Gayan
不幸的是,我无法使用 .net 框架中的方法,因此 Mesh.Sphere 不是一个选择。我正在尝试查看是否有替代方法来建模球体并使用导出的网格,以便保持简单。 - Gayan
显示剩余2条评论
2个回答

13

基本思路:

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

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

用参数定义的单位圆:

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

现在我们可以定义一个单独的圆,想象一下在x、y平面上的同心环。现在想象一下抬高最内层的圆,随着你抬高它,它会像弹簧一样拉起下一个环……这个视觉只适用于半球。
因此,产生从同心环形成球形的形式当然是另一个圆,它与环垂直,即(z,y)平面……当然,我们只对找到环的偏移量感兴趣(需要从(x,y)平面上偏移多高或低)。
因为我们只需要偏移量,所以只需要半个圆……而且极点只会是一个点。在极点使用三角扇和每个环之间使用条带。
完成这个思维练习后,请参阅http://en.wikipedia.org/wiki/Sphere并搜索“具有半径r的球体上的点可以通过参数化”,然后您将在该行之后看到参数化形式。
法线非常容易,球体应始终围绕(0,0,0)构建,并且球体应始终具有半径1(因此您可以简单地将其缩放到所需大小),然后圆表面上的每个顶点都等于法线。
上述方法使用了两个三角形扇和一系列三角形条带...另一种方法可以产生顶点均匀分布的球体,并且可以用一个三角形条带绘制,尽管目前我试图编写它会让我发疯,但涉及以下想法:
想象一个以原点为中心的四面体(点距离0,0,0为1个单位)。这是一个非常糟糕的球体表示,但它是一种近似。现在,想象我们找到每个面的中点,然后将该点推出直到它在球体表面上。然后我们找到那些面的中点并将它们推向球体表面...
tetrahdralSphere(int recursions){}
找到中点非常简单,只需要每个x、y、z分量的平均值。然后,由于球体是一个单位球体,将它们移动到表面就像归一化这个新向量一样简单。

方法一生成的点分布看起来像经纬线,产生不均匀分布(如果使用四边形和线框架,则看起来像地球仪),实现相当容易。第二种方法需要递归,所以有点困难,但看起来更加均匀。如果你想变得非常复杂并且让自己头疼......那么尝试在n个点之间分配并模拟排斥力,将它们分开,然后在表面上进行归一化。需要解决各种问题才能有效地使其工作,但这样你就可以获得相当均匀分布的点,并且可以控制顶点的数量,从而开始欣赏建模工具找到表示模型所需的最小几何形状的过程。


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

让我们每个圆画10个点...所以phi将以2pi/10的增量步进 并且让我们画10个同心圆

我们将画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

1
谢谢Quarternion。关于第一种方法,维基百科页面是我在寻找答案时访问的第一个页面之一。困扰我的问题是我们需要有两个3个变量,r、theta和phi。我没能想出一种从r和theta推导phi的方法。我们可以假设theta=phi(假设r=1)吗?还是有其他方法可以推导phi?我现在真的很困惑。 - Gayan
噢,好吧,让你先在纸上写出那个方程式...首先,你要将其围绕原点居中,因为你总可以将其平移到你想要的位置...所以去掉x0、y0和z0组件(它们只是平移分量),接下来我们已经同意你可以根据需要缩放你的球体,所以在每行中去掉"r"...好了,应该更清洁了。接下来知道theta和phi是给定范围内的任何值。我知道有一种方法可以让这变得非常容易...我会回来的。 - Quaternion
好的...回到那个方程(在维基百科上没有不必要的变量)...这个参数化形式最容易可视化为同心圆环,看起来就像是从(x,y)平面或屏幕表面向内观察,z朝屏幕内部,就像从北极向下看地球。我们可以看到z = cos(theta),因此一个球体有两个轴,并且由于z轴已经被考虑进去了,这意味着sin()分量(一个环的宽度)由sin()确定,这就是为什么x和y变量中有额外的sin()混入到圆的等式中(不考虑z)。 - Quaternion
谢谢。我按照你的伪代码生成了一个由均匀分布点组成的美丽球体。你能给我一些关于如何将这个点列表正确地转换成三角形带或三角形扇形的建议吗?我知道我应该添加额外的点来组成三角形,但不确定具体该怎么做。 - Gayan
然后访问http://www.riemers.net/eng/Tutorials/DirectX/Csharp/Series3/Triangle_Strip.php,首先将顶点分配到内存中,可能需要为极点分配几个变量,并为环形分配一个数组,然后在另一步中绘制它们。 - Quaternion

7
通常三角化一个单位球的方法是构建一个四面体二十面体,然后:
  • 如果精度足够,则停止
  • 否则,对于每个现有的面:
  • 在每条边的中点添加一个顶点并将其归一化,使其位于单位球上
  • 用四个新面替换原来的面。其中一个面具有三个新中点作为角(在纸上画出来,另外三个面就会变得明显)
  • 循环。
为了避免在边缘中点重复顶点,需要跟踪现有的顶点以便重复使用。

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