3

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

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

但是在上面提到的 4D 玩具中,物体变形和剪裁很多。
并且很可能没有定义的模型,并且在两者之间有许多转换。
(它现在可能是一个简单的正方形,稍后将一个尖球剪成两半)。
在这种情况下,update-vertex-VBO-per-frame 或 Vertex Arrays(我在另一个问题中看到)是一个合适的解决方案吗?

4

1 回答 1

8

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

  1. 结构

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

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

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

  2. 变换

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

    为了更容易做到这一点,暂时避免平滑旋转(因为在 4D 中这并不容易),4x4而是计算随机旋转矩阵。所以只需随机设置所有单元格<-1,+1>。将每一行作为基向量处理。要使它们正交化,只需将它们单元化并利用叉积。有关更多信息,请参阅:

  3. 使成为

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

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

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

这里是 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是用于 N 维计算的我的库。您需要的只是线性代数中的4D、5D向量和4x4矩阵5x5数学。

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

用法很简单:

// 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旋转......

超立方体

所以这样你就可以渲染任何线框网格(即使是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)
    

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

以这种方式处理所有边之后,您需要分析记住的交点并根据边之间的连接信息从它们创建新边。我还没有这样做,所以我对此无能为力。我会尝试连接共享同一个邻居的记住点,但不确定这在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
//---------------------------------------------------------------------------

并预览:

透视超立方体

[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
//---------------------------------------------------------------------------

以及横截面渲染的预览:

横截面

最糟糕的部分是将超立方体定义为一组单纯形......

于 2017-07-07T12:16:52.487 回答