【问题标题】:3D Grid for multiple shapes多种形状的 3D 网格
【发布时间】:2017-08-21 18:46:25
【问题描述】:

几个月前,我为一个学校项目制作了一个小型地形生成器,例如 Minecraft。 我这样做的方法是使用多个块。每个块都包含一个存储块的 3 维数组。 这个数组中的每个位置都对应于它所包含的块的位置。

blocks[x, y, z] = new Block();

现在我想添加不同大小的块。但是,我不能用我现在存储块的方式来做到这一点,因为更大的块必须分布在 3 维数组中的多个位置。 具有不同大小(和不同形状)的积木游戏的一个例子是 LEGO Worlds。像这样的游戏如何存储所有这些小块?

我希望有人可以帮助我。

我使用的语言是 Javascript 与 WebGL 的结合。

提前致谢!

【问题讨论】:

  • 您在寻找体素化吗?
  • 我不认为这符合体素化的条件,因为我不想只使用块。我只想知道如何将对象存储在比 1 个“位置”更宽的 3D 网格上,例如 Minecraft 中的门。因为它占用了超过 1 个区块。
  • 数据结构取决于多种因素——您是否想节省内存或计算时间,您的世界有多稀疏/密集,您是否希望它是开放世界/封闭地图
  • 描述它的最佳方式就像 Minecraft,但块的大小不同。
  • 你拥有的体素的类型并不会真正影响你使用的数据结构,后者受体素分布的影响更大(一个可能的例外是遮挡剔除 - 像我的世界块这样的良好镶嵌体素通常不需要太多技巧)

标签: javascript 3d webgl


【解决方案1】:

根据我的经验,有几种不同的方法可以解决这样的问题,但我推荐的方法取决于您必须处理此问题的时间量以及您想要制作的范围(多大)这个游戏。

你目前的方法

目前我认为您使用大多数人认为最直接的方法,将体素存储在 3D 网格中

[Source].

但是您似乎遇到的两个问题是,没有一种明显的方法可以创建大于 1x1 的块,并且世界空间的 3D 网格在内存使用方面效率相当低(至于数组您必须为每个单元格分配内存,包括空白空间。JavaScript 也不例外)。

另一种方法

使用 3D 数组的替代方法是使用不同的数据结构,全名是稀疏体素八叉树。

简单地说,这是一个树形数据结构,它通过细分空间区域来工作,直到所有内容都存储完毕。

将正方形子划分为四个较小象限的 2D 形式称为四叉树,类似地,将 3D 等效项划分为八个象限,称为八叉树。这种方法通常更可取,因为它更有效,因为树仅在绝对必要时才占用更多内存,并且它们也可以打包到 1D 数组中(技术上也可以是 3D 数组)。

在一些基于块的游戏中,四叉树/八叉树使用的一种常见策略是采用适合树的一个较大象限的相同类型体素的区域,即简单地停止细分,因为没有理由去如果所有数据都相同,则更深。

他们可以进行的另一种优化称为稀疏,其中空白空间(空气)区域被简单地删除,因为空白空间没有做任何特殊的事情并且可以推断其位置。

[SVO Source]

[Z Order Curve Source]

推荐方法

除非你有几个月的时间来完成你的游戏并且你正在上大学,否则我真的不会推荐 SVO(尽管阅读可能会给你的任何老师留下深刻印象)。相反,我建议采用与 Minecraft 明显相同的方法。例如。一扇门是 1X2 的,但方块只能是 1x1,那就做两个方块吧。

在门的示例中,您总共将有四个独特的块,上半部分和下半部分各两个,每个打开或关闭两个变体。

例如

var cubeProgram; // shader program
var cubeVBO; // vertex buffer (I recommend combining vertex & UV coords)
var gl; // rendering context

// Preset list of block ID's
var BLOCK_TYPES = {
    DOOR_LOWER_OPEN: 0,
    DOOR_UPPER_OPEN: 1,
    DOOR_LOWER_CLOSED: 2,
    DOOR_UPPER_CLOSED: 3,
}

var BLOCK_MESHES = {
    GENERIC_VBO: null,
    DOOR_UPPER_VBO: null
    DOOR_LOWER_VBO: null
}

// Declare a Door class using ES6 syntax
class Door {
    // Assume X & Y are the lower half of the door
    constructor(x,y,map) {
        if (y - 1 > -1) {
            console.error("Error: Top half of the door goes outside the map");
            return;
        }

        this.x = x;
        this.y = y;

        map[x][y  ] = BLOCK_TYPES.DOOR_LOWER_OPEN;
        map[x][y-1] = BLOCK_TYPES.DOOR_UPPER_OPEN;
    }
}

【讨论】:

  • 谢谢,这真的很有帮助!我明天要先试试这个。不过,我还有一个问题。你认为这在乐高世界中是如何处理的?因为那个游戏的积木要小得多,而且形状也不同。
  • 我不太确定,但我认为一个很好的猜测是某种被分成块的线性 SVO。
  • 您是否有一个示例说明如何做到这一点?我仍然不太了解 SVO 是什么,举个例子对我很有帮助。
  • SVO 仅表示稀疏体素八叉树。它只是一个映射到体素的八叉树,其中分支到被切掉的空白空间。我在我的原始答案link 中链接了一篇关于该主题的好论文但我会说这样做会有点矫枉过正,除非这是针对本科论文。
  • (加上 WebGL 使用的 GLSL 版本的限制使得实现 SVO 渲染器非常困难)
猜你喜欢
  • 1970-01-01
  • 2017-12-17
  • 1970-01-01
  • 2012-11-02
  • 1970-01-01
  • 1970-01-01
  • 2012-11-02
  • 2023-04-04
  • 1970-01-01
相关资源
最近更新 更多