【问题标题】:Detecting if Voxel or voxel group is still connected to rest of object检测体素或体素组是否仍连接到对象的其余部分
【发布时间】:2019-11-02 23:16:00
【问题描述】:

所以我正在构建一个基于体素的物理模拟器,可以在其中破坏体素。每个体素化的对象都有一种“中心体素”来描述它,我称之为“和平 A”。
所有体素都被模拟为一个,直到它们与“和平 A”或附加到它的另一个体素分离,然后它们(和/或连接到该体素的其他体素)被放入具有自己模拟物理的新对象中。这就是我的问题,我如何检查体素是否仍然附着?以最有效的方式?

当我扩大规模时,寻路听起来会减慢分配速度,但很高兴尝试找出它是否是最佳选择。并且当一个体素被破坏时立即更新所有体素听起来也不太好。各位高手有什么想法吗?

为了清楚起见,这里有一些图纸(请原谅我的鼠标笔迹不好)

第一张图像,体素仍然连接:

第二张图像,当体素分开时:

【问题讨论】:

  • 3D 填充? A* 寻路?

标签: algorithm unity3d path physics voxel


【解决方案1】:

分割/标注是一种方式(类似于光栅 A* 和泛洪填充)。

  1. 为每个体素添加一个标志变量/空间

    这个标志将用于判断是否使用体素...并且可以在后面用作临时值...

  2. 将所有标志清零并设置实际对象ID=1

  3. flag=0选择第一个体素

  4. 用实际对象ID填充它

    所以使用 6 或 26 连接(类似于 2D 中的 4 或 8 连接)用 ID 填充体素标志。

  5. 增加 ID 并转到 #3

    当没有flag=0的体素时停止

在此之后,标志保存原始对象被拆分成的子对象 ID,ID 保存新对象的计数+1。

[edit1] C++/VCL/OpenGL 示例

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
const int n=16;     // voxel map resolution
bool map[n][n][n];  // voxel map
int  flg[n][n][n];  // (flag)
int pal[]=          // 0xAABBGGRR
    {
    0x007F0000,
    0x00007F00,
    0x0000007F,
    0x00007F7F,
    0x007F007F,
    0x007F7F00,
    0x00FF0000,
    0x0000FF00,
    0x000000FF,
    0x0000FFFF,
    0x00FF00FF,
    0x00FFFF00,
    };
const int pals=sizeof(pal)/sizeof(pals);
//---------------------------------------------------------------------------
void fill(int x,int y,int z,int id)
    {
    // inside map check
    if ((x<0)||(x>=n)) return;
    if ((y<0)||(y>=n)) return;
    if ((z<0)||(z>=n)) return;
    // is it active voxel?
    if (!map[x][y][z]) return;
    // already filled with the same id?
    if (flg[x][y][z]==id) return;
    // set flag
    flg[x][y][z]=id;
    // fill neighbors
    fill(x-1,y,z,id);
    fill(x+1,y,z,id);
    fill(x,y-1,z,id);
    fill(x,y+1,z,id);
    fill(x,y,z-1,id);
    fill(x,y,z+1,id);
    fill(x-1,y-1,z,id);
    fill(x-1,y+1,z,id);
    fill(x+1,y-1,z,id);
    fill(x+1,y+1,z,id);
    fill(x-1,y,z-1,id);
    fill(x-1,y,z+1,id);
    fill(x+1,y,z-1,id);
    fill(x+1,y,z+1,id);
    fill(x,y-1,z-1,id);
    fill(x,y-1,z+1,id);
    fill(x,y+1,z-1,id);
    fill(x,y+1,z+1,id);
    fill(x-1,y-1,z-1,id);
    fill(x-1,y-1,z+1,id);
    fill(x-1,y+1,z-1,id);
    fill(x-1,y+1,z+1,id);
    fill(x+1,y-1,z-1,id);
    fill(x+1,y-1,z+1,id);
    fill(x+1,y+1,z-1,id);
    fill(x+1,y+1,z+1,id);
    }
//---------------------------------------------------------------------------
void recolor()
    {
    int x,y,z,id;
    // clear all flags to zero and set actual object ID=1
    id=1;
    for (x=0;x<n;x++)
     for (y=0;y<n;y++)
      for (z=0;z<n;z++)
       flg[x][y][z]=0;
    // pick first voxel with flag=0
    for (x=0;x<n;x++)
     for (y=0;y<n;y++)
      for (z=0;z<n;z++)
       if (map[x][y][z])
        if (flg[x][y][z]==0)
            {
            // flood fill it with actual object ID
            fill(x,y,z,id);
            // increment ID and goto #3
            id++;
            }
    }
//---------------------------------------------------------------------------
void TForm1::draw()
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);

    // center the view around map[][][]
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0,0.0,-80.0);
    glRotatef( 15.0,1.0,0.0,0.0);
    glTranslatef(-n,-n,-n);

    // render map[][][] as cubes (very slow old api version for simplicity)
    int x,y,z,i;
    for (x=0;x<n;x++)
     for (y=0;y<n;y++)
      for (z=0;z<n;z++)
       if (map[x][y][z])
        {
        glPushMatrix();
        glTranslatef(x+x,y+y,z+z);
        glColor4ubv((BYTE*)&(pal[flg[x][y][z]%pals]));
        glBegin(GL_QUADS);
        for (i=0;i<3*24;i+=3)
            {
            glNormal3fv(vao_nor+i);
            glVertex3fv(vao_pos+i);
            }
        glEnd();
        glPopMatrix();
        }

    glFlush();
    SwapBuffers(hdc);
    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    gl_init(Handle);

    // init map[][][]
    int x,y,z,i0=2,i1=n-i0-1,xx,yy,zz;
    for (x=0;x<n;x++)
     for (y=0;y<n;y++)
      for (z=0;z<n;z++)
        {
        // clear map
        map[x][y][z]=false;
        // cube edges
        if (((x>=i0)&&(x<=i1))&&((y==i0)||(y==i1))&&((z==i0)||(z==i1))) map[x][y][z]=true;
        if (((y>=i0)&&(y<=i1))&&((x==i0)||(x==i1))&&((z==i0)||(z==i1))) map[x][y][z]=true;
        if (((z>=i0)&&(z<=i1))&&((x==i0)||(x==i1))&&((y==i0)||(y==i1))) map[x][y][z]=true;
        // ball
        xx=x-8; xx*=xx;
        yy=y-8; yy*=yy;
        zz=z-8; zz*=zz;
        if (xx+yy+zz<=16) map[x][y][z]=true;
        // X
        if ((y==i0)&&(x==  z  )&&(x>=4)&&(x<12)) map[x][y][z]=true;
        if ((y==i0)&&(x==n-z-1)&&(x>=4)&&(x<12)) map[x][y][z]=true;
        }
     recolor();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
    gl_exit();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
    {
    gl_resize(ClientWidth,ClientHeight);
    draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
    {
    draw();
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
    {
    draw();
    }
//---------------------------------------------------------------------------

你可以忽略 VCL 的东西和 OpenGL 渲染唯一重要的东西是在代码顶部直到(包括)函数void recolor();@ 987654332@ 保存我们输入地图的各个体素(在我的应用程序构造函数中硬编码,对象很少)。 flg 数组包含对象的id,相应的体素被标记为(枚举),稍后用于使用调色板pal[] 中的不同颜色为每个对象着色。为了尽可能简单,我没有进行任何优化(因此填充和渲染非常慢,并且可以优化很多)。

这里预览(调用recolor()后):

您可以看到所有连接的体素共享相同的颜色,这意味着它们在flg 数组中具有相同的id 编号(属于同一对象),因此算法和代码按预期工作。

如果您想使用它,gl_simple.h 在这里:

所以您只需要将 VCL 事件移植到您的环境样式中...

【讨论】:

  • 感谢您的回答!是的,这似乎是一个很好的解决方案,我会在对象被破坏的那一刻触发这个循环,它应该可以正常工作!可以这么说,我还将尝试向后运行您的系统,因此它将从最近不相邻的体素中填充并在找到和平时停止 A 可能会使其更有效率?再次感谢:)
  • 很抱歉造成威胁,但你能用更好的术语解释一下这个过程吗?我试图按照解释进行操作,但由于含糊不清而迷失了方向。 @Spektre
  • @Spektre 我只是指完成它所需的步骤。你给出了一个很好的清单,但它有点模糊。你说给每个体素一个子变量。然后将他们的子变量设置为0,还要将他们的id设置为1?什么是身份证?这是从哪里来的?什么是“实际对象”?然后继续;然后你通过找到第一个仍然有 0 作为变量的块来开始你的循环。开始路径是有意义的,但是然后“用实际的对象 id 填充”所以你用它所依赖的块的 id 填充变量?那它怎么知道转换家属呢?
  • @AlecHarvey 我添加了一些我刚刚拼凑起来的代码...
  • @Spektre 谢谢,但我以前从未使用过 C++,所以这对我来说就像一门外星语言。我希望有一个更详尽的列表,比如流程图来解释算法。对不起,我自己含糊不清,浪费了您的时间:(
【解决方案2】:
  1. 随机选择一个未连接的对象
  2. 递归获取上/下/左/右对象,直到没有左(始终检查对象是否已连接)
  3. 如果场景中没有未连接的对象,请停止
  4. 转到步骤 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-18
    • 2022-08-18
    • 1970-01-01
    • 2023-02-13
    相关资源
    最近更新 更多