首页 > 解决方案 > 在 OpenGL 中编程特定的 3d(星形)模型?

问题描述

如何创建以下模型:

图像

从第一张图开始。它可以完全在 OpenGL 中编程,还是应该使用 3d Studio Max 或 Unity 等其他软件?是否应该使用一些特定的算法?

标签: c++opengl3d

解决方案


是的,这可以在C++/OpenGL中完成

  1. 创建从中心发射的随机曲线

    简单的 3D 二次多项式曲线将符合要求。

  2. 将曲线转换为圆锥

    只需沿每条曲线插入点并将其用作圆锥切片的中心。方向由曲线上的上一个或下一个点设置。插入圆锥切片并将它们的点添加到某个点列表中。看:

  3. 创建面

    只需使用任何原语连接计算点以形成锥体...我建议GL_QUAD...

  4. 如果您还想添加核心(核?),可以将其作为球体完成,在其表面添加一些噪声,并可能进行一些过滤以使其平滑一点...

这里简单的曲线生成 C++ 示例:

List<double> pnt;
void spicule_init()
    {
    double t,tt,x,y,z;
    double a0[3],a1[3],a2[3];
    int ix0,ix,i,j;
    Randomize();
    for (i=0;i<20;i++)                      // cones
        {
        // random quadratic 3D curve coeff
        for (j=0;j<3;j++)
            {
            a0[j]=0.0;                      // center (0,0,0)
            a1[j]=2.0*(Random()-0.5);       // main direction
            a2[j]=1.0*(Random()-0.5);       // curvature
            }
        // curve interpolation
        ix0=pnt.num;
        for (t=0.0;t<=1.0;t+=0.04)
         for (tt=t*t,j=0;j<3;j++)
          pnt.add(a0[j]+(a1[j]*t)+(a2[j]*tt));
        }
    }

生成点预览:

曲线

[Edit1]添加圆锥、法线和面时,它看起来像这样:

锥体

它远非完美,但我认为这是一个很好的起点。只需调整半径r和曲线系数a1[],a2[]即可获得所需的形状......并且可能会添加核心或检查自相交,我懒得这样做......

这里更新的 C++/GL 代码:

//---------------------------------------------------------------------------
List<double> pnt,nor;   // points, normals
List<int> fac;          // QUAD faces
//---------------------------------------------------------------------------
void Circle3D(List<double> &pnt,List<double> &nor,double *p0,double *n0,double r,int N)
    {
    int i;
    double a,da=divide(pi2,N),p[3],dp[3],x[3],y[3];
    vector_ld(x,1.0,0.0,0.0); if (fabs(vector_mul(x,n0)>0.7)) vector_ld(x,0.0,1.0,0.0);
    vector_mul(x,x,n0); vector_one(x,x);
    vector_mul(y,x,n0); vector_one(y,y);
    for (a=0.0,i=0;i<N;i++,a+=da)
        {
        vector_mul( p,x,cos(a));
        vector_mul(dp,y,sin(a));
        vector_add(p,p,dp); nor.add(p[0]); nor.add(p[1]); nor.add(p[2]);
        vector_mul(p,p,r);
        vector_add(p,p,p0); pnt.add(p[0]); pnt.add(p[1]); pnt.add(p[2]);
        }
    }
//---------------------------------------------------------------------------
void spicule_init() // generate random spicule mesh
    {
    const int N=36;                         // points/circle
    const int N3=3*N;
    double t,tt,x,y,z,r;
    double a0[3],a1[3],a2[3];
    double p[3],n[3];
    int e,i,j,i00,i01,i10,i11;
    Randomize();
    pnt.num=0; nor.num=0; fac.num=0;
    for (i=0;i<20;i++)                      // cones
        {
        // random quadratic 3D curve coeff
        for (j=0;j<3;j++)
            {
            a0[j]=0.0;                      // center (0,0,0)
            a1[j]=2.0*(Random()-0.5);       // main direction and size
            a2[j]=1.0*(Random()-0.5);       // curvature
            }
        // curve interpolation
        vector_ld(n,0.0,0.0,0.0);
        for (e=0,t=0.05;t<=1.0;t+=0.05)
            {
            // points,normals
            for (tt=t*t,j=0;j<3;j++) p[j]=a0[j]+(a1[j]*t)+(a2[j]*tt);
            r=0.15*(1.0-pow(t,0.1));        // radius is shrinking with t
            vector_sub(n,p,n);              // normal is p(t)-p(t-dt)
            Circle3D(pnt,nor,p,n,r,N);      // add circle to pnt (N points)
            vector_copy(n,p);               // remember last point
            // faces
            if (!e){ e=1; continue; }       // ignore first slice of cone
            i00=pnt.num- 3; i10=i00-N3;
            i01=pnt.num-N3; i11=i01-N3;
            for (j=0;j<N;j++)
                {
                fac.add(i00);
                fac.add(i01);
                fac.add(i11);
                fac.add(i10);
                i00=i01; i01+=3;
                i10=i11; i11+=3;
                }
            }
        }
    }
//---------------------------------------------------------------------------
void spicule_draw() // render generated spicule
    {
    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CCW);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    int i,j;
    glColor3f(1.0,1.0,1.0);
    glBegin(GL_QUADS);
    for (i=0;i<fac.num;i++)
        {
        j=fac.dat[i];
        glNormal3dv(nor.dat+j);
        glVertex3dv(pnt.dat+j);
        }
    glEnd();
    }
//---------------------------------------------------------------------------

如果您不知道如何计算交叉/点积或绝对值等矢量运算,请参阅:

// cross product: W = U x V
W.x=(U.y*V.z)-(U.z*V.y)
W.y=(U.z*V.x)-(U.x*V.z)
W.z=(U.x*V.y)-(U.y*V.x)
// dot product: a = (U.V)
a=U.x*V.x+U.y*V.y+U.z*V.z
// abs of vector a = |U|
a=sqrt((U.x*U.x)+(U.y*U.y)+(U.z*U.z))


vector_mul(a[3],b[3],c[3])是叉积a = b x c
a = vector_mul(b[3],c[3])是点积a = (b.c)
vector_one(a[3],b[3])是单位向量a = b/|b|
vector_copy(a[3],b[3])只是复制a = b
vector_add(a[3],b[3],c[3])是加a = b + c
vector_sub(a[3],b[3],c[3])是减a = b - c
vector_neg(a[3],b[3])是否定a = -b
vector_ld(a[3],x,y,z)只是加载a = (x,y,z)

也可以在这里找到一些(如果不是全部)使用的向量数学:

我也使用我的动态列表模板,所以:


List<double> xxx;double xxx[];
xxx.add(5);添加5到列表末尾 相同
xxx[7]访问数组元素(安全)
xxx.dat[7]访问数组元素(不安全但快速直接访问)
xxx.num是数组的实际使用大小
xxx.reset()清除数组并为项目设置xxx.num=0
xxx.allocate(100)预分配空间100


推荐阅读