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

Vri*_*tra 3 opengl graphics geometry rendering 4d

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

我还听说,模型之间的变形也使用VBO来缓存两个模型,因为这两个模型都可以很好地定义并且不需要太多的中间步骤。

但是在上面提到的4D玩具中,物体会发生变形并被大量裁剪。
而且很可能没有定义的模型,并且两者之间有很多转换。
(现在可能是一个简单的正方形,一个尖刺的球在以后被切成两半)。
在这种情况下,每帧更新顶点VBO或顶点数组(我在另一个问题中看到)是合适的解决方案吗?

Spe*_*tre 5

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

  1. 结构

    为了使这一点尽可能简单,我将使用线框而不是BR渲染。因此,您需要处理4D网格(线框)。我将使用2个表:

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

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

  2. 转换

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

    为了使这一点变得更加容易,现在就避免平滑旋转(就像在4D中那样不那么容易),4x4而是计算随机旋转矩阵。因此,只需随机设置所有单元格即可<-1,+1>。将每一行作为基础向量。要使它们正交,只需使其统一并利用叉积即可。有关更多信息,请参见:

  3. 渲染

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

    (x',y',z',u',W) = rep * (x,y,z,u,1)
    
    Run Code Online (Sandbox Code Playgroud)

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

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

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

我使用了我的动态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向量和4x45x5从线性代数矩阵数学。

这两个库的大小都很大,而且法律问题也阻止了我在这里共享它们的代码。

用法很简单:

(x',y',z',u',W) = rep * (x,y,z,u,1)
Run Code Online (Sandbox Code Playgroud)

在这里预览一些rep旋转...

超立方体

这样您就可以渲染任何线框网格(即使BR渲染也可以这种方式工作)。

如果要升级到切割,则应采用每条线框线并计算其与切割超平面的交点。如果我们选择通过点的超平面

O(0,0,0,u_cut)
Run Code Online (Sandbox Code Playgroud)

并且正常

N(0,0,0,1)
Run Code Online (Sandbox Code Playgroud)

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

  1. 没有路口

    ((A.u > u_cut)&&(B.u > u_cut)) || ((A.u < u_cut)&&(B.u < u_cut))
    
    Run Code Online (Sandbox Code Playgroud)

    只是忽略这样的优势

  2. 1个路口

    ((A.u >= u_cut)&&(B.u <= u_cut)) || ((A.u <= u_cut)&&(B.u >= u_cut))
    
    Run Code Online (Sandbox Code Playgroud)

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

    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)
    
    Run Code Online (Sandbox Code Playgroud)

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

  3. 完全在里面

    (A.u == u_cut)&&(B.u == u_cut)
    
    Run Code Online (Sandbox Code Playgroud)

    只要记住两个端点并渲染该边缘即可。

以这种方式处理所有边缘之后,您需要分析记住的相交点,并根据边缘之间的连通性信息从它们创建新的边缘。我还没有这样做,所以我对此无能为力。我会尝试连接共享同一邻居的记忆点,但不确定4D是否足够。

有关更多信息,请查看我发现或回答的相关质量检查

[Edit1]具有透视图的代码

//---------------------------------------------------------------------------
//--- 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
//---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

并预览:

透视超立方体

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