如何平滑 3D 体素世界的块?

2024-02-04

在我的(类似 Minecraft)3D 体素世界中,我想要平滑形状以获得更自然的视觉效果。我们首先以 2D 形式来看这个示例。

左边是没有任何平滑处理的世界的样子。地形数据是二进制的,每个体素被渲染为单位大小的立方体。

在中心你可以看到一个朴素的圆形平滑。它只考虑四个直接相邻的块。看起来仍然不是很自然。此外,我希望出现平坦的 45 度斜坡。

在右边你可以看到我提出的平滑算法。为了得出块的形状,它考虑了八个直接和对角邻居。我有C++代码 https://github.com/danijar/voxel-smoothing-2d在线的。下面的代码给出了绘制贝塞尔曲线所沿的控制点。

#include <iostream>

using namespace std;
using namespace glm;


list<list<dvec2>> Points::find(ivec2 block)
{
    // Control points
    list<list<ivec2>> lines;
    list<ivec2> *line = nullptr;

    // Fetch blocks, neighbours start top left and count
    // around the center block clock wise
    int center = m_blocks->get(block);
    int neighs[8];
    for (int i = 0; i < 8; i++) {
        auto coord = blockFromIndex(i);
        neighs[i] = m_blocks->get(block + coord);
    }

    // Iterate over neighbour blocks
    for (int i = 0; i < 8; i++) {
        int current = neighs[i];
        int next = neighs[(i + 1) % 8];
        bool is_side   = (((i + 1) % 2) == 0);
        bool is_corner = (((i + 1) % 2) == 1);

        if (line) {
            // Border between air and ground needs a line
            if (current != center) {
                // Sides are cool, but corners get skipped when they don't
                // stop a line
                if (is_side || next == center)
                    line->push_back(blockFromIndex(i));
            } else if (center || is_side || next == center) {
                // Stop line since we found an end of the border. Always
                // stop for ground blocks here, since they connect over
                // corners so there must be open docking sites
                line = nullptr;
            }
        } else {
            // Start a new line for the border between air and ground that
            // just appeared. However, corners get skipped if they don't
            // end a line.
            if (current != center) {
                lines.emplace_back();
                line = &lines.back();
                line->push_back(blockFromIndex(i));
            }
        }
    }

    // Merge last line with first if touching. Only close around a differing corner for air
    // blocks.
    if (neighs[7] != center && (neighs[0] != center || (!center && neighs[1] != center))) {
        // Skip first corner if enclosed
        if (neighs[0] != center && neighs[1] != center)
            lines.front().pop_front();
        if (lines.size() == 1) {
            // Close circle
            auto first_point = lines.front().front();
            lines.front().push_back(first_point);
        } else {
            // Insert last line into first one
            lines.front().insert(lines.front().begin(), line->begin(), line->end());
            lines.pop_back();
        }
    }

    // Discard lines with too few points
    auto i = lines.begin();
    while (i != lines.end()) {
        if (i->size() < 2)
            lines.erase(i++);
        else
            ++i;
    }

    // Convert to concrete points for output
    list<list<dvec2>> points;
    for (auto &line : lines) {
        points.emplace_back();
        for (auto &neighbour : line)
            points.back().push_back(pointTowards(neighbour));
    }
    return points;
}

glm::ivec2 Points::blockFromIndex(int i)
{
    // Returns first positive representant, we need this so that the
    // conditions below "wrap around"
    auto modulo = [](int i, int n) { return (i % n + n) % n; };

    ivec2 block(0, 0);
    // For two indices, zero is right so skip
    if (modulo(i - 1, 4))
        // The others are either 1 or -1
        block.x = modulo(i - 1, 8) / 4 ? -1 : 1;
    // Other axis is same sequence but shifted
    if (modulo(i - 3, 4))
        block.y = modulo(i - 3, 8) / 4 ? -1 : 1;
    return block;
}

dvec2 Points::pointTowards(ivec2 neighbour)
{
    dvec2 point;
    point.x = static_cast<double>(neighbour.x);
    point.y = static_cast<double>(neighbour.y);

    // Convert from neighbour space into
    // drawing space of the block
    point *= 0.5;
    point += dvec2(.5);

    return point;
}

然而,这仍然是二维的。如何将这个算法转化为三维空间?


你可能应该看看行进立方体算法 http://en.wikipedia.org/wiki/Marching_cubes并从那里开始工作。您可以轻松控制生成的斑点的平滑度:

  1. 想象一下,每个体素定义了一个场,其中心具有高密度,当您远离中心时慢慢消失到零。例如,您可以使用一个函数,该函数在体素内为 1,并在两个体素之外变为 0。无论您选择什么具体函数,请确保它仅在有限(最好是小)区域内非零。
  2. 对于每个点,将所有场的密度相加。
  3. 对这些字段的总和使用移动立方体算法
  4. 使用高分辨率网格进行算法

为了改变外观/平滑度,您可以更改密度函数和行进立方体算法的阈值。行进立方体以创建更平滑的网格的可能扩展是以下想法:想象一下,您在立方体的边缘上遇到两个点,其中一个点位于体积内部(阈值之上),另一个点位于体积之外(阈值之下)。在这种情况下,许多行进立方体算法将边界恰好放置在边缘的中间。人们可以计算出精确的边界点——这消除了混叠。

我还建议您在此之后运行网格简化算法。使用行进立方体会导致网格中包含许多不必要的三角形。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何平滑 3D 体素世界的块? 的相关文章

  • Karasuba算法递归过多

    我正在尝试用 c 实现 Karasuba 乘法算法 但现在我只是想让它在 python 中工作 这是我的代码 def mult x y b m if max x y lt b return x y bm pow b m x0 x bm x1
  • 如何使用双重调度来分析图形基元的交集?

    我正在分析图形基元 矩形 直线 圆形等 的交互并计算重叠 相对方向 合并等 这被引用为双重调度的一个主要示例 例如维基百科 http en wikipedia org wiki Double dispatch 自适应碰撞算法通常要求 不同的
  • 从三点求圆心的算法是什么?

    我在圆的圆周上有三个点 pt A A x A y pt B B x B y pt C C x C y 如何计算圆心 在Processing Java 中实现它 我找到了答案并实施了一个可行的解决方案 pt circleCenter pt A
  • 0-1背包算法

    以下 0 1 背包问题是否可解 浮动 正值和 浮动 权重 可以是正数或负数 背包的 浮动 容量 gt 0 我平均有 这是一个相对简单的二进制程序 我建议用蛮力进行修剪 如果任何时候你超过了允许的重量 你不需要尝试其他物品的组合 你可以丢弃整
  • 包围一组点的多边形

    我有一组 S 点 2D 由 x 和 y 定义 我想找到 P 包围该组所有点的最小 含义 具有最少数量的点 多边形 P 是S 有没有已知的算法来计算这个 我在这个领域缺乏文化令人惊讶 感谢您的帮助 对于这个问题有很多算法 它被称为 最小边界框
  • 在常数空间中创建 1..N 的随机排列

    我正在寻找枚举固定空间中数字 1 N 的随机排列 这意味着我无法将所有数字存储在列表中 原因是 N 可能非常大 超过可用内存 我仍然希望能够一次遍历这样一个数字的排列 只访问每个数字一次 我知道对于某些 N 可以这样做 许多随机数生成器随机
  • 通过三点的贝塞尔曲线

    我已经阅读了类似的主题以找到解决方案 但没有成功 我想做的是使该工具与 CorelDraw 中的工具相同 名为 钢笔工具 我通过连接贝塞尔三次曲线来做到这一点 但仍然缺少一个功能 即拖动曲线 而不是控制点 以编辑其形状 我可以成功确定曲线上
  • shell脚本中关联数组的时间复杂度

    我想知道在 shell 脚本中使用关联数组时如何构造 实现 另外 我想知道基于 shell 脚本的关联数组的时间复杂度是否是最佳的 因为我们可以使用字母和数字作为它们各自的键 编辑 他们使用什么哈希函数 如果您使用关联数组 则不能通过 使用
  • 给定一个具有多个重复条目的数组,找到一个重复条目 O(N) 时间和常数空间

    我们得到了一个大小为 N 的数组 其中包含 0 到 N 2 范围内的整数 包括 0 和 N 2 该数组可以有多个重复的条目 我们需要在 O N 时间和常量空间中找到重复条目之一 我正在考虑取数组中所有条目的乘积和总和 以及 0 到 N 2
  • 如何用约束标记一大组“传递群”?

    在 NealB解决方案之后进行编辑 与以下解决方案相比 NealB的解决方案非常非常快任何另一个 https stackoverflow com q 18033115 answers and 提出了关于 添加约束以提高性能 的新问题 Nea
  • 照片马赛克算法。如何在给定基本图像和瓷砖列表的情况下创建马赛克照片?

    Hy 我要做的是创建一个程序 使用 C 或 C 它将 24 位 像素位图和图像集合作为输入 我必须创建一个马赛克图像 类似于使用库的输入图像给定的图像 创建与输入类似的马赛克照片 到目前为止 我可以访问输入的图像像素及其颜色 但我有点卡住了
  • 需要解释搜索最小大和的算法

    我正在解决 Codility 问题作为练习 但无法回答其中一个问题 我在互联网上找到了答案 但我不明白这个算法是如何工作的 有人可以引导我逐步完成它吗 这是问题 You are given integers K M and a non em
  • 如何对对象进行排序? (画家算法)

    所以我有 4 个矩形形状 我正在尝试应用排序算法 画家算法 https en wikipedia org wiki Painter 27s algorithm 来知道我需要先绘制哪些形状 在 3d 中 然后绘制哪个形状 Note 相机位于右
  • 为什么这个 R ggplot2 代码会显示一个空白的显示设备?

    虽然 SO 通常不用于帮助解决错误 但这个显示了特别简单且特别烦人的行为 如果你是一个ggplot2用户 您可以在 10 秒或更短的时间内重现它 正如这个 GitHub 问题 ggplot gtable 创建空白显示 https githu
  • 高效列出目录中的所有子目录

    请参阅迄今为止所采取的建议的编辑 我正在尝试使用 WinAPI 和 C 列出给定目录中的所有目录 文件夹 现在我的算法又慢又低效 使用 FindFirstFileEx 打开我正在搜索的文件夹 然后我查看目录中的每个文件 使用 FindNex
  • 数量重新分配逻辑 - 具有外部数据集的 MapGroups

    我正在研究一种复杂的逻辑 需要将数量从一个数据集重新分配到另一个数据集 在例子中我们有Owner and Invoice 我们需要从数量中减去Invoice准确地Owner匹配 在给定汽车的给定邮政编码处 减去的数量需要重新分配回同一辆车出
  • 如何计算排列? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个关于 Java 排列的问题 Suppose I have five different elements in an arra
  • 选择一组数字以达到最小总数的算法

    给定 一组数字n 1 n 2 n 3 n x 还有一个数字M 我想找到最好的组合 n a n b n c n gt M 该组合应达到达到或超过 M 所需的最小值 没有其他组合可以提供更好的结果 将在 PHP 中执行此操作 因此可以使用 PH
  • 列出所有 k 元组,其条目总和为 n,忽略旋转

    有没有一种有效的算法来查找所有序列k总和为的非负整数n 同时避免旋转 如果可能的话 完全避免 顺序很重要 但对于我正在解决的问题来说 轮换是多余的 例如 与k 3 和n 3 我想要得到一个如下所示的列表 3 0 0 2 1 0 2 0 1
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方

随机推荐

  • Xcode4,iOS:复制了目标,为什么我无法更改标识符?

    这让我抓狂 我已经在 Xcode4 中为我的 iOS 项目复制了一个目标 然而 我的目标的摘要页面上 iOS 应用程序目标 下的目标标识符字段是不可编辑的 如何更改目标的标识符 它从产品名称中获取标识符 进入构建设置 然后向下滚动到包装部分
  • java.lang.OutOfMemoryError:带有 tomcat7 windows 服务的 PermGen 空间

    我在跑步雄猫7 0 33 on 视窗服务器 2008 R2 我安装了tomcat作为Windows服务 JDK版本 jdk1 6 0 25 64 bit 汤姆猫选项 Java虚拟机 C Program Files Java jre6 bin
  • Java中的基数方向算法

    这个周末 我花了几分钟时间研究一个算法 该算法将接收一个航向 以度为单位 并返回一个基本方向的字符串 我在我正在使用的 Android 指南针应用程序中使用它 我最终得到的是这样的 private String headingToStrin
  • 如何合并属于另一个可观察量属性的可观察量

    我有一个假期列表 每个假期都附有一个用户 ID 我想将检索到的用户数据合并到每个假期记录中 以便它返回单个可观察值 我创建了这个函数 getAllHolidaysAndUsers return this af database list H
  • Mysql函数调用

    如果我多次调用一个函数 那么它会每次都执行还是只执行一次 然后多次使用该值 例子 select my function filed my function filed field2 my function filed field1 fiel
  • 使用 Python 求解四分位数和十分位数

    Python 2 7 是否有一个可以解决四分位数和十分位数的库 numpy 似乎没有任何功能 如果有的话可以给我一个链接吗 提前致谢 D Using np百分位数 http docs scipy org doc numpy dev refe
  • 多线程curl同时处理多个连接

    curl multi 接口是否在内部生成新线程以同时处理多个请求 它是否等于手动生成线程并仅使用curl easy句柄 什么是性能更高 我需要发出最多 1000 个并发请求 https curl haxx se libcurl c mult
  • 按多对多关系的计数排序 - SQLAlchemy

    我正在使用 Flask SQLAlchemy 来查询我的 Postgres 数据库 我目前正在尝试使用以下查询来查询标题建议 res Title query filter Titles name ilike searchstring lim
  • 如何配置 playframework 服务器以支持 ssl

    如何配置 playframework 服务器以支持 ssl 例子https 本地主机 9000 https localhost 9000 如果您更喜欢使用集成方式 请参阅发行说明 http www playframework org doc
  • 启动进程并监听退出事件

    我有一些代码启动一个进程并连接一个事件处理程序来处理进程退出时的情况 我的代码是用 C 编写的 我想知道 Delphi 是否可以实现类似的功能 System Diagnostics Process myProcess new System
  • YouTube 数据 API v3 评论列表

    我正在使用 YouTube V3 0 API 调用来下载特定 YouTube 频道 例如 Apple YouTube 频道 的 JSON 信息源 现在 我已经设法拥有视频列表和所有内容 但如果可能的话 我还想获得对视频的评论或其中一些评论
  • 有没有办法将外部 URL 分配给超链接而不附加 http:// 或 https://(即协议)?

    我有一个像这样定义的超链接
  • 如何更改我的计算机的文化信息

    我正在 IIS 上运行一个站点 该站点从 Thread CurrentThread CurrentCulture 读取区域性信息 这来自我所理解的系统文化信息 我需要将其设置为某个值 但我无法在操作系统级别更改它 我的 Windows 7
  • Laravel 5:如何将本地文件复制到 Amazon S3?

    我正在 Laravel 5 中编写代码来定期备份 MySQL 数据库 到目前为止我的代码如下所示 filename database backup date G a m d y sql destination storage path ba
  • slim3 中的控制器单元测试

    首先 我想说 我是 PHP phpunit 单元测试的新手 在我的新项目 slim3 框架 中 我想测试我的控制器 例如 LoginController 我的想法是 在单元测试方法中 创建实例LoginController 模拟控制器中的一
  • 比较两个字符串并得到差异

    如何比较 C 中的两个字符串并获得差异 例如 String1 我有一辆车 string2 我有一辆新车宝马 结果 新车 宝马 您需要确保较大的一组位于左侧Except 不确定是否有纯粹的 Linq 方法来实现这一点 static void
  • 获取输入文件的目录(Applescript)

    我很困惑 我已经在谷歌上搜索了一个小时 并且尝试了大约十种不同的形式set posixDirectory to POSIX path of parent of path to aFile as string 但我似乎无法做对 我通过执行以下
  • 异步方法跳过等待

    我有以下代码 public async Task
  • Akka-http:如何从请求中获取自定义标头?

    我在请求中将以下标头发送到我的 akka http api Content type application json Accept application json AppId some id 如何在我的 akka http 路由中获取
  • 如何平滑 3D 体素世界的块?

    在我的 类似 Minecraft 3D 体素世界中 我想要平滑形状以获得更自然的视觉效果 我们首先以 2D 形式来看这个示例 左边是没有任何平滑处理的世界的样子 地形数据是二进制的 每个体素被渲染为单位大小的立方体 在中心你可以看到一个朴素