我应该如何处理(变形)opengl中的4D对象?

时间:2017-07-06 04:36:22

标签: opengl graphics geometry rendering 4d

我想尝试写一个类似于4D toys的游乐场, 所以我开始学习opengl 根据我目前的理解,人们使用VBO和均匀变换矩阵来处理大多数静态对象  (像立方体,骨骼动画等,通常只涉及转换)

我还听说模型之间的变形也使用VBO来缓存两个模型,因为它们都将被很好地定义而不是中间体。

但是在上面提到的4D玩具中,物体变形和剪裁很多 并且很可能没有定义的模型,并且之间存在许多转换 (现在可能是一个简单的方形,后面夹着一个尖刺的球) 在这种情况下,更新顶点-VPO-每帧或顶点阵列(我在另一个问题中看到)是一个合适的解决方案?

1 个答案:

答案 0 :(得分:4)

对于初学者,我会使用4D -> 3D投影而不是超平面切割。结果不尽相同,但会让你更接近你的目标(所以你可以将后者升级为切入)。所以类似于在图形中使用的3D -> 2D转换,您有2个选择,一个是使用透视投影,第二个是在渲染时忽略第四个维度坐标。我将使用后者,因为它更简单。

  1. <强>结构

    为了尽可能简单,我将使用线框而不是 BR 渲染。所以你需要处理4D网格(线框)。我会用2个表:

    double pnt[];   // 4D point list (x,y,z,u)
    int  lin[];     // lines point indexes (i0,i1)
    

    第一个存储网格的所有顶点,第二个存储索引线对以线框表示的线连接。

  2. <强>变换

    如果我只忽略第4个坐标,那么我们就无法获得所需的功能。因此,为了使第4维工作,我们需要添加 4D 变换以在渲染之前将网格定向在 4D 中。因此,请使用homogenous transform matrix并调用ir rep。在 4D 中,它应为5x5正交矩阵,4x4旋转部分rot

    为了使这更容易避免现在的平滑旋转(如在4D中那样不那么容易)并且计算随机旋转4x4矩阵。因此,只需随机设置所有单元格<-1,+1>。将每一行作为基础向量处理。为了使它们正交,只需使它们成为单位并利用交叉产品。有关详细信息,请参阅:

  3. <强>呈现

    只需通过变换矩阵转换点表

    (x',y',z',u',W) = rep * (x,y,z,u,1)
    

    然后取(x ,y,z`)并渲染......

  4. 这里是4D超立方体的简单OpenGL / C ++示例:

    //---------------------------------------------------------------------------
    //--- Mesh 4D: ver 0.000 ----------------------------------------------------
    //---------------------------------------------------------------------------
    #ifndef _mesh4D_h
    #define _mesh4D_h
    //---------------------------------------------------------------------------
    #include <math.h>
    #include "nd_math.h"
    #include "list.h"
    //---------------------------------------------------------------------------
    const double pi   =    M_PI;
    const double pi2  =2.0*M_PI;
    const double pipol=0.5*M_PI;
    const double deg=M_PI/180.0;
    const double rad=180.0/M_PI;
    //---------------------------------------------------------------------------
    class mesh4D
        {
    public:
        matrix<5> rep;  // 4D uniform 5x5 transform matrix
    
        List<double> pnt;   // 4D point list (x,y,z,u)
        List<int>    lin;   // lines point indexes (i0,i1)
    
        mesh4D()    {}
        mesh4D(mesh4D& a)   { *this=a; }
        ~mesh4D()   {}
        mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
        //mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
    
        void set_randomrep();               // random oriented uniform 4D transform matrix with origin (0,0,0,0)
        void set_hypercube(double a);
    
        void draw();
        };
    //---------------------------------------------------------------------------
    void mesh4D::set_randomrep()
        {
        int i,j;
        matrix<4> rot;
        rep.unit();
        rot.rnd();
        rot.orthonormal();
        for (i=0;i<4;i++)
         for (j=0;j<4;j++)
          rep[i][j]=rot[i][j];
        }     
    void mesh4D::set_hypercube(double a)
        {
        rep.unit(); // reset orientation
        pnt.num=0;  // clear point list
        lin.num=0;  // clear line list
    
        pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
        pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
        pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
        pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
        pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
        pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
        pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
        pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
    
        pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
        pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
        pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
        pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
        pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
        pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
        pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
        pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
    
        // A0
        lin.add( 0+0); lin.add( 0+1);
        lin.add( 0+1); lin.add( 0+3);
        lin.add( 0+3); lin.add( 0+2);
        lin.add( 0+2); lin.add( 0+0);
        // A1
        lin.add( 4+0); lin.add( 4+1);
        lin.add( 4+1); lin.add( 4+3);
        lin.add( 4+3); lin.add( 4+2);
        lin.add( 4+2); lin.add( 4+0);
        // A=A0+A1
        lin.add( 0+0); lin.add( 4+0);
        lin.add( 0+1); lin.add( 4+1);
        lin.add( 0+2); lin.add( 4+2);
        lin.add( 0+3); lin.add( 4+3);
    
        // B0
        lin.add( 8+0); lin.add( 8+1);
        lin.add( 8+1); lin.add( 8+3);
        lin.add( 8+3); lin.add( 8+2);
        lin.add( 8+2); lin.add( 8+0);
        // B1
        lin.add(12+0); lin.add(12+1);
        lin.add(12+1); lin.add(12+3);
        lin.add(12+3); lin.add(12+2);
        lin.add(12+2); lin.add(12+0);
        // B=B0+B1
        lin.add( 8+0); lin.add(12+0);
        lin.add( 8+1); lin.add(12+1);
        lin.add( 8+2); lin.add(12+2);
        lin.add( 8+3); lin.add(12+3);
    
        // hyper cube = A+B
        lin.add( 0+0); lin.add( 8+0);
        lin.add( 0+1); lin.add( 8+1);
        lin.add( 0+2); lin.add( 8+2);
        lin.add( 0+3); lin.add( 8+3);
        lin.add( 0+4); lin.add( 8+4);
        lin.add( 0+5); lin.add( 8+5);
        lin.add( 0+6); lin.add( 8+6);
        lin.add( 0+7); lin.add( 8+7);
        }
    //---------------------------------------------------------------------------
    void mesh4D::draw()
        {
        int i,j;
        double _zero=1e-3;
        vector<5> a,b;
        glBegin(GL_LINES);
        for (i=0;i<lin.num;)
            {
            // extrac first point
            j=lin[i]*4; i++;
            a.a[0]=pnt[j]; j++;
            a.a[1]=pnt[j]; j++;
            a.a[2]=pnt[j]; j++;
            a.a[3]=pnt[j]; j++;
            a.a[4]=1.0; // W=1
            // extrac second point
            j=lin[i]*4; i++;
            b.a[0]=pnt[j]; j++;
            b.a[1]=pnt[j]; j++;
            b.a[2]=pnt[j]; j++;
            b.a[3]=pnt[j]; j++;
            b.a[4]=1.0; // W=1
            // transform
            a=rep*a;
            b=rep*b;
            // render
            glVertex3dv(a.a);   // use just x,y,z
            glVertex3dv(b.a);   // use just x,y,z
            }
        glEnd();
        }
    //---------------------------------------------------------------------------
    #endif
    //---------------------------------------------------------------------------
    

    我使用了我的动态list.h模板:


    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项目预分配空间

    nd_math.h是我的lib用于N维计算。你需要的只是来自线性代数的 4D,5D 向量和4x45x5矩阵数学。

    两个库的大小都有点大,而法律问题也阻止我在这里分享他们的代码。

    用法很简单:

    // globals and init
    mesh4D mesh
    double animx=-50.0,danimx=0.0;
    double animy=  0.0,danimy=2.0;
    mesh.set_hypercube(0.5);
    
    // render
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D( -2.0, 2.0, -2.0, 2.0 );
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRotated(animx,1.0,0.0,0.0);
    glRotated(animy,0.0,1.0,0.0);
    mesh.draw();
    glFlush();
    SwapBuffers(hdc);
    
    // some timer
    animx+=danimx; if (animx>=360.0) animx-=360.0;
    animy+=danimy; if (animy>=360.0) animy-=360.0;
    call_render_here();
    
    // on key press or mouse wheel or what ever
    mesh.set_randomrep();
    

    此处预览了一些rep轮换......

    hypercube

    所以这样你可以渲染任何线框网格(甚至 BR 渲染都应该这样工作)。

    如果您想升级到剪切,那么您应该拍摄每个线框线并计算其与切割超平面的交点。如果我们选择经过点的超平面

    O(0,0,0,u_cut)
    

    并且正常

    N(0,0,0,1)
    

    然后任务将简化很多。有3个选项。让我们考虑带有端点A,B的边线:

    1. 没有交集

      ((A.u > u_cut)&&(B.u > u_cut)) || ((A.u < u_cut)&&(B.u < u_cut))
      

      忽略这样的边缘

    2. 1个交叉点

      ((A.u >= u_cut)&&(B.u <= u_cut)) || ((A.u <= u_cut)&&(B.u >= u_cut))
      

      所以通过线性插值计算交点

      x = A.x + (B.x-A.x)*(u_cut-A.u)/(B.u-A.u)
      y = A.y + (B.y-A.y)*(u_cut-A.u)/(B.u-A.u)
      z = A.z + (B.z-A.z)*(u_cut-A.u)/(B.u-A.u)
      

      并记住这一点以及它所属的边缘。

    3. 完全在

      (A.u == u_cut)&&(B.u == u_cut)
      

      只需记住两个端点,并渲染此边缘。

    4. 在以这种方式处理完所有边之后,您需要分析记住的交叉点并根据边之间的连接信息从它们创建新边。我还没有这样做,所以我无法帮助解决这个问题。我会尝试连接共享相同邻居的记忆点,但不确定 4D 中是否足够。

      有关详细信息,请查看我刚刚找到的相关 QA

      [Edit1]带透视的代码

      //---------------------------------------------------------------------------
      //--- Mesh 4D: ver 0.001 ----------------------------------------------------
      //---------------------------------------------------------------------------
      #ifndef _mesh4D_h
      #define _mesh4D_h
      //---------------------------------------------------------------------------
      #include <math.h>
      #include "nd_math.h"
      #include "list.h"
      //---------------------------------------------------------------------------
      const double pi   =    M_PI;
      const double pi2  =2.0*M_PI;
      const double pipol=0.5*M_PI;
      const double deg=M_PI/180.0;
      const double rad=180.0/M_PI;
      //---------------------------------------------------------------------------
      class mesh4D
          {
      public:
          matrix<5> rep;  // 4D uniform 5x5 transform matrix
      
          List<double> pnt;   // 4D point list (x,y,z,u)
          List<int>    lin;   // lines point indexes (i0,i1)
      
          mesh4D()    {}
          mesh4D(mesh4D& a)   { *this=a; }
          ~mesh4D()   {}
          mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
          //mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
      
          void set_randomrep();               // random oriented uniform 4D transform matrix with origin (0,0,0,0)
          void set_hypercube(double a);
      
          void draw();
          };
      //---------------------------------------------------------------------------
      void mesh4D::set_randomrep()
          {
          int i,j;
          matrix<4> rot;
          rot.rnd();
          rot.orthonormal();
          for (i=0;i<4;i++)
           for (j=0;j<4;j++)
            rep[i][j]=rot[i][j];
          }
      //---------------------------------------------------------------------------
      void mesh4D::set_hypercube(double a)
          {
          rep.unit();     // reset orientation
          rep[0][4]=0.0;  // set position
          rep[1][4]=0.0;
          rep[2][4]=0.0;
          rep[3][4]=3.0*a;
          pnt.num=0;  // clear point list
          lin.num=0;  // clear line list
      
          pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
          pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(-a);
          pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
          pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(-a);
          pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
          pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(-a);
          pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
          pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(-a);
      
          pnt.add(-a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
          pnt.add(+a); pnt.add(-a); pnt.add(-a); pnt.add(+a);
          pnt.add(-a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
          pnt.add(+a); pnt.add(+a); pnt.add(-a); pnt.add(+a);
          pnt.add(-a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
          pnt.add(+a); pnt.add(-a); pnt.add(+a); pnt.add(+a);
          pnt.add(-a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
          pnt.add(+a); pnt.add(+a); pnt.add(+a); pnt.add(+a);
      
          // A0
          lin.add( 0+0); lin.add( 0+1);
          lin.add( 0+1); lin.add( 0+3);
          lin.add( 0+3); lin.add( 0+2);
          lin.add( 0+2); lin.add( 0+0);
          // A1
          lin.add( 4+0); lin.add( 4+1);
          lin.add( 4+1); lin.add( 4+3);
          lin.add( 4+3); lin.add( 4+2);
          lin.add( 4+2); lin.add( 4+0);
          // A=A0+A1
          lin.add( 0+0); lin.add( 4+0);
          lin.add( 0+1); lin.add( 4+1);
          lin.add( 0+2); lin.add( 4+2);
          lin.add( 0+3); lin.add( 4+3);
      
          // B0
          lin.add( 8+0); lin.add( 8+1);
          lin.add( 8+1); lin.add( 8+3);
          lin.add( 8+3); lin.add( 8+2);
          lin.add( 8+2); lin.add( 8+0);
          // B1
          lin.add(12+0); lin.add(12+1);
          lin.add(12+1); lin.add(12+3);
          lin.add(12+3); lin.add(12+2);
          lin.add(12+2); lin.add(12+0);
          // B=B0+B1
          lin.add( 8+0); lin.add(12+0);
          lin.add( 8+1); lin.add(12+1);
          lin.add( 8+2); lin.add(12+2);
          lin.add( 8+3); lin.add(12+3);
      
          // hyper cube = A+B
          lin.add( 0+0); lin.add( 8+0);
          lin.add( 0+1); lin.add( 8+1);
          lin.add( 0+2); lin.add( 8+2);
          lin.add( 0+3); lin.add( 8+3);
          lin.add( 0+4); lin.add( 8+4);
          lin.add( 0+5); lin.add( 8+5);
          lin.add( 0+6); lin.add( 8+6);
          lin.add( 0+7); lin.add( 8+7);
          }
      //---------------------------------------------------------------------------
      void mesh4D::draw()
          {
          int i,j;
          const double _zero=1e-3;
          double focal_length=1.0;
      
          vector<5> a,b;
          glBegin(GL_LINES);
          for (i=0;i<lin.num;)
              {
              // extrac first point
              j=lin[i]*4; i++;
              a.a[0]=pnt[j]; j++;
              a.a[1]=pnt[j]; j++;
              a.a[2]=pnt[j]; j++;
              a.a[3]=pnt[j]; j++;
              a.a[4]=1.0; // W=1
              // extrac second point
              j=lin[i]*4; i++;
              b.a[0]=pnt[j]; j++;
              b.a[1]=pnt[j]; j++;
              b.a[2]=pnt[j]; j++;
              b.a[3]=pnt[j]; j++;
              b.a[4]=1.0; // W=1
              // transform
              a=rep*a;
              b=rep*b;
              // perspective: camera projection plane u=0, focus at (0,0,0,-focal_length)
              if (a[3]>=0.0) a*=divide(focal_length,a[3]+focal_length); else a.zero();
              if (b[3]>=0.0) b*=divide(focal_length,b[3]+focal_length); else b.zero();
              // render
              glVertex3dv(a.a);   // use just x,y,z
              glVertex3dv(b.a);   // use just x,y,z
              }
          glEnd();
          }
      //---------------------------------------------------------------------------
      #endif
      //---------------------------------------------------------------------------
      

      预览:

      hypercube with perspective

      [Edit2]实体网格和横截面

      所以我改变了架构。我将 4D 5x5同质变换矩阵(reper4D)移动到单独的文件中,并通过4D单面(4点4边四面体)添加颜色和网格定义。切割简单地计算单面和切割超平面的交点(如上所述),得到3个点(三角形),4个点(四面体)或0个点。哪个可以轻松渲染(无需分析边缘之间的连接)。有关详细信息,请参阅:

      顺便说一下。我认为这就是Miegakure的工作原理。这里更新了代码:

      //---------------------------------------------------------------------------
      //--- Mesh 4D: ver 1.000 ----------------------------------------------------
      //---------------------------------------------------------------------------
      #ifndef _mesh4D_h
      #define _mesh4D_h
      //---------------------------------------------------------------------------
      #include "list.h"
      #include "reper4D.h"
      //---------------------------------------------------------------------------
      class mesh4D
          {
      public:
          reper4D rep;        // 4D uniform 5x5 transform matrix
      
          List<double> pnt;   // 4D point list (x,y,z,w)
          List<int>    lin;   // 4D wireframe (i0,i1)
          List<int>    fac;   // 4D simplexes (i0,i1,i2,i3)
          List<DWORD>  col;   // simplex colors (RGB)
      
          mesh4D()    {}
          mesh4D(mesh4D& a)   { *this=a; }
          ~mesh4D()   {}
          mesh4D* operator = (const mesh4D *a) { *this=*a; return this; }
          //mesh4D* operator = (const mesh4D &a) { ...copy... return this; }
      
          void set_hypercube(double a);
          void draw_cut(double w_cut);                                        // render cross section by w=w_cut hyperplane
          void draw          (double focal_length=-1.0,double w_near=-1.0);   // render mesh      (focal_length<0) -> no perspective, else perspective view in W+ direction
          void draw_wireframe(double focal_length=-1.0,double w_near=-1.0);   // render wireframe (focal_length<0) -> no perspective, else perspective view in W+ direction
          };
      //---------------------------------------------------------------------------
      void mesh4D::set_hypercube(double a)
          {
          const double tab_pnt[]=
              {
              -a, -a, -a, -a,
              +a, -a, -a, -a,
              -a, +a, -a, -a,
              +a, +a, -a, -a,
              -a, -a, +a, -a,
              +a, -a, +a, -a,
              -a, +a, +a, -a,
              +a, +a, +a, -a,
              -a, -a, -a, +a,
              +a, -a, -a, +a,
              -a, +a, -a, +a,
              +a, +a, -a, +a,
              -a, -a, +a, +a,
              +a, -a, +a, +a,
              -a, +a, +a, +a,
              +a, +a, +a, +a,
              };
          const int tab_lin[]=
              {
              // A0
               0+0,  0+1,
               0+1,  0+3,
               0+3,  0+2,
               0+2,  0+0,
              // A1
               4+0,  4+1,
               4+1,  4+3,
               4+3,  4+2,
               4+2,  4+0,
              // A=A0+A1
               0+0,  4+0,
               0+1,  4+1,
               0+2,  4+2,
               0+3,  4+3,
              // B0
               8+0,  8+1,
               8+1,  8+3,
               8+3,  8+2,
               8+2,  8+0,
              // B1
              12+0, 12+1,
              12+1, 12+3,
              12+3, 12+2,
              12+2, 12+0,
              // B=B0+B1
               8+0, 12+0,
               8+1, 12+1,
               8+2, 12+2,
               8+3, 12+3,
              // hyper cube = A+B
               0+0,  8+0,
               0+1,  8+1,
               0+2,  8+2,
               0+3,  8+3,
               0+4,  8+4,
               0+5,  8+5,
               0+6,  8+6,
               0+7,  8+7,
              };
          // 5x simplex per cube
          #define _cube(a0,a1,a2,a3,a4,a5,a6,a7) a1,a2,a4,a7, a0,a1,a2,a4, a2,a4,a6,a7, a1,a2,a3,a7, a1,a4,a5,a7
          // 4D hypercube = 8 cubes
          const int tab_fac[]=
              {
              _cube( 0, 1, 2, 3, 4, 5, 6, 7),
              _cube( 0, 1, 2, 3, 8, 9,10,11),
              _cube( 4, 5, 6, 7,12,13,14,15),
              _cube( 8, 9,10,11,12,13,14,15),
              _cube( 0, 1, 4, 5, 8, 9,12,13),
              _cube( 0, 2, 4, 6, 8,10,12,14),
              _cube( 1, 3, 5, 7, 9,11,13,15),
              _cube( 2, 3, 6, 7,10,11,14,15),
              };
          #undef _cube
          const DWORD tab_col[]=
              {
              //  BBGGRR,    BBGGRR,    BBGGRR,    BBGGRR,    BBGGRR,
              0x00FF0000,0x00FF0000,0x00FF0000,0x00FF0000,0x00FF0000,
              0x0000FF00,0x0000FF00,0x0000FF00,0x0000FF00,0x0000FF00,
              0x000000FF,0x000000FF,0x000000FF,0x000000FF,0x000000FF,
              0x0000FFFF,0x0000FFFF,0x0000FFFF,0x0000FFFF,0x0000FFFF,
              0x00FF00FF,0x00FF00FF,0x00FF00FF,0x00FF00FF,0x00FF00FF,
              0x00FFFF00,0x00FFFF00,0x00FFFF00,0x00FFFF00,0x00FFFF00,
              0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,0x00FFFFFF,
              0x004080FF,0x004080FF,0x004080FF,0x004080FF,0x004080FF,
              };
      
          int i,n;
          vector<4> p;
          rep.reset();
          pnt.num=0; for (i=0,n=sizeof(tab_pnt)/sizeof(tab_pnt[0]);i<n;i++) pnt.add(tab_pnt[i]);
          lin.num=0; for (i=0,n=sizeof(tab_lin)/sizeof(tab_lin[0]);i<n;i++) lin.add(tab_lin[i]);
          fac.num=0; for (i=0,n=sizeof(tab_fac)/sizeof(tab_fac[0]);i<n;i++) fac.add(tab_fac[i]);
          col.num=0; for (i=0,n=sizeof(tab_col)/sizeof(tab_col[0]);i<n;i++) col.add(tab_col[i]);
          }
      //---------------------------------------------------------------------------
      void mesh4D::draw_cut(double w_cut)
          {
          const double _zero=1e-6;
          const int edge2[]={0,1,0,2,0,3,1,2,2,3,3,1,-1}; // simplex wireframe i0,i1
          const int edge3[]={0,1,2,3,0,1,3,1,2,3,2,0,-1}; // simplex triangles i0,i1,i2
          int e,i,j,k,k0,k1,k2,inside[4];
          DWORD rgb;
          vector<4> p[4],q[4];
          vector<3> xyz[4],nor,a,b;
          for (i=0;i<fac.num;)
              {
              rgb=col[i>>2];
              // extrac points (x,y,z,w)
              for (k=0;k<4;k++)
                  {
                  j=fac[i]*4; i++;
                  p[k].a[0]=pnt[j]; j++;
                  p[k].a[1]=pnt[j]; j++;
                  p[k].a[2]=pnt[j]; j++;
                  p[k].a[3]=pnt[j]; j++;
                  // transform
                  rep.l2g(p[k],p[k]);
                  inside[k]=1;
                  }
              // process edge2 and compute cross section cut intersection points
              for (e=0,k=0;edge2[e]>=0;)
                  {
                  k0=edge2[e]; e++;
                  k1=edge2[e]; e++;
                  // fully inside
                  if (fabs(p[k0][3]-w_cut)+fabs(p[k1][3]-w_cut)<=_zero)
                      {
                      if ((k<4)&&(inside[k0])){ q[k]=p[k0]; k++; inside[k0]=0; }
                      if ((k<4)&&(inside[k1])){ q[k]=p[k1]; k++; inside[k1]=0; }
                      continue;
                      }
                  // no intersection
                  if (((p[k0][3]> w_cut)&&(p[k1][3]> w_cut))||((p[k0][3]< w_cut)&&(p[k1][3]< w_cut))) continue;
                  // 1 intersection
                  if (k<4)
                      {
                      q[k]=p[k1]-p[k0];
                      q[k]*=divide(w_cut-p[k0][3],p[k1][3]-p[k0][3]);
                      q[k]+=p[k0];
                      q[k][3]=w_cut;
                      k++;
                      continue;
                      }
                  }
              // 4D -> 3D vector
              for (k0=0;k0<k;k0++) for (k1=0;k1<3;k1++) xyz[k0][k1]=q[k0][k1];
              // render triangle
              if (k==3)
                  {
                  // normal
                  a=xyz[1]-xyz[0];
                  b=xyz[2]-xyz[1];
                  nor.cross(a,b);
                  nor.unit();
                  // render
                  glBegin(GL_TRIANGLES);
                  glNormal3dv(nor.a);
                  glColor4ubv((BYTE*)(&rgb));
                  glVertex3dv(xyz[0].a);
                  glVertex3dv(xyz[1].a);
                  glVertex3dv(xyz[2].a);
                  glEnd();
                  }
              // render simplex
              if (k==4)
               for (e=0;edge3[e]>=0;)
                  {
                  k0=edge3[e]; e++;
                  k1=edge3[e]; e++;
                  k2=edge3[e]; e++;
                  // normal
                  a=xyz[k1]-xyz[k0];
                  b=xyz[k2]-xyz[k1];
                  nor.cross(a,b);
                  nor.unit();
                  // render
                  glBegin(GL_TRIANGLES);
                  glNormal3dv(nor.a);
                  glColor4ubv((BYTE*)(&rgb));
                  glVertex3dv(xyz[k0].a);
                  glVertex3dv(xyz[k1].a);
                  glVertex3dv(xyz[k2].a);
                  glEnd();
                  }
              }
          }
      //---------------------------------------------------------------------------
      void mesh4D::draw(double focal_length,double w_near)
          {
          const int edge3[]={0,1,2,3,0,1,3,1,2,3,2,0,-1}; // simplex triangles i0,i1,i2
          int i,j,k,k0,k1,k2;
          DWORD rgb;
          vector<4> p;
          vector<3> xyz[4],nor,a,b;
      
          // 4D simplexes
          glColor3f(0.3,0.3,0.3);
          for (i=0;i<fac.num;)
              {
              rgb=col[i>>2];
              // extrac points (x,y,z,w)
              for (k=0;k<4;k++)
                  {
                  j=fac[i]*4; i++;
                  p[0]=pnt[j]; j++;
                  p[1]=pnt[j]; j++;
                  p[2]=pnt[j]; j++;
                  p[3]=pnt[j]; j++;
                  // transform
                  rep.l2g(p,p);
                  // perspective projection
                  if (focal_length>0.0)
                      {
                      p[3]-=w_near;
                      if (p[3]>=0.0) p*=divide(focal_length,p[3]+focal_length); else p.zero();
                      }
                  // 4D -> 3D vector
                  xyz[k].ld(p[0],p[1],p[2]);
                  }
              // render simplex
              for (k=0;edge3[k]>=0;)
                  {
                  k0=edge3[k]; k++;
                  k1=edge3[k]; k++;
                  k2=edge3[k]; k++;
                  // normal
                  a=xyz[k1]-xyz[k0];
                  b=xyz[k2]-xyz[k1];
                  nor.cross(a,b);
                  nor.unit();
                  // render
      //          glBegin(GL_LINE_LOOP);
                  glBegin(GL_TRIANGLES);
                  glNormal3dv(nor.a);
                  glColor4ubv((BYTE*)(&rgb));
                  glVertex3dv(xyz[k0].a);
                  glVertex3dv(xyz[k1].a);
                  glVertex3dv(xyz[k2].a);
                  glEnd();
                  }
              }
          }
      //---------------------------------------------------------------------------
      void mesh4D::draw_wireframe(double focal_length,double w_near)
          {
          int i,j,k;
          vector<4> p[4];
          // 4D wireframe
          glColor3f(1.0,1.0,1.0);
          glBegin(GL_LINES);
          for (i=0;i<lin.num;)
              {
              // extrac points (x,y,z,w)
              for (k=0;k<2;k++)
                  {
                  j=lin[i]*4; i++;
                  p[k].a[0]=pnt[j]; j++;
                  p[k].a[1]=pnt[j]; j++;
                  p[k].a[2]=pnt[j]; j++;
                  p[k].a[3]=pnt[j]; j++;
                  // transform
                  rep.l2g(p[k],p[k]);
                  // perspective projection
                  if (focal_length>0.0)
                      {
                      p[k][3]-=w_near;
                      if (p[k][3]>=0.0) p[k]*=divide(focal_length,p[k][3]+focal_length); else p[k].zero();
                      }
                  // render
                  glVertex3dv(p[k].a);    // use just x,y,z
                  }
              }
          glEnd();
          }
      //---------------------------------------------------------------------------
      #endif
      //---------------------------------------------------------------------------
      

      横截面预览呈现:

      cross section

      最糟糕的是将超立方体定义为单形集...