从零实现一个3D建模软件

Dust3D是Jeremy HU 个人开发的一款3D建模软件,主要用于游戏模型的快速制作。虽然作者已经在 某种程度上放弃了该项目,但其分享的Dust3D从起心到技术选择到最终实现的整个心路过程,很有 借鉴意义。

1、起心动念

自2015年以来我就想写一个3D建模软件,那时我正在开发2.5D的MMORPG游戏。我在YouTube自学了一段时间 的Blender软件,Blender其实相当不错,但是,这个过程中的工作量让我意识到,一个人不可能完成大量的 模型、制作纹理、动画,然后在游戏中使用,因为只是建立一个简单的恐龙模型就花了我半天时间。

我看了很多关于如何快速制作游戏模型的教程,试图找出一个统一的方式,一个可重复的模式,可以在 编程语言中简化。我总结了制作模型的最常见步骤:首先,为前视图、侧视图和后视图设置参考样张, 其次,制作平面,然后细分为六边形,通过遵循参考仰仗挤压此六边形,调整面部大小,以不同角度微调, 来回调整,最后获得基本模型。

2、最初的实验

看起来我可以写一个软件来自动完成这些步骤,输入参考样张,它输出一个模型。让我们实现这一点, 我做了一个非常粗糙的测试程序,以识别图像中的每个视图,提取边界,根据边界挤压面孔,但它太小了, 不能在游戏中使用。

3、来自Jimmy的新想法

有一天,我搜索了一些有关怪物生成的关键字,并找到了Jimmy Gunawan的博客, 我被他的文章震惊了, 这就是我要找的,这就是答案,我非常兴奋,当读到博客上关于Blender的皮肤修改器背后的技术时, 我发现了论文:B-Mesh: A Fast Modeling System for Base Meshes of 3D Articulated Shapes, 这一刻我就知道,是时候将我最初的想法变成真实的产品了。

4、启动Dust3D项目

我启动了 Dust3D 项目, 并在reddit贴出了我的计划,虽然这是我还没有做很多事情。我这样做是因为, 作为游戏行业的新手,我不想在一开始错过一些事情。感谢令人赞叹的reddit用户,我学到了很多新的 软件名称和建模术语,如Meshmixer,CGAL,等等。

5、重新发明车轮

重新发明车轮很有趣,所以我没有严格遵循尽量使用已有库的建议,我想从头开始制作一个3D软件。我想 逐点逐行绘制3D世界。这是 Dust3D 的第一个屏幕截图,使用原始 OpenGL,除了 OpenGL 环境,没有任何 其他依赖:

很快,我发现调试绘图问题花了这么多时间,所以我小心谨慎地引入了GLU库,现在看起来是这样:

过了一段时间,我认为Qt非常容易使用,所以我小心地引入了Qt。然后实现了 Bmesh 算法:

Catmull-Clark 细分:

现在,是时候做一个更正式的用户界面了。

6、再次重造车轮

你可以看到,dust3d是从零依赖开始,然后不可避免地引入一些东西。如果继续这样下去,一切顺利。 但有些事情发生了。由于没有复杂的UI,我使用Blender建立Bmesh球之间的关系,我发现Blender软件 在Callada输出器中的一个bug,我试图自己修复它,所以我下载了Blender的源代码,修复并提交了一个 补丁。在此过程中,我厌倦了折腾C++的不同版本的问题,因此我决定从 Dust3D 代码库中删除所有C++代码。

Qt是C++,因此Qt被删除。我试图找到一些替代的UI库,ImGui看起来很有前途,但因为它是C++写的, 所以被放弃了。我又从零开始实现UI,这就是它的外观:

啊哈!

7、休整与重新思考

当我在澳大利亚启动Dust3D 项目时, 我使用的是工作和度假签证。有很多事情阻止了我继续开发 这个项目,那段时间相当繁忙。这让我重新思考所做的决定。删除所有的依赖并不好,我正在做的是 一个3D建模软件,而不是GUI库。我也开始考虑建模过程的一些细节。在Bmesh论文中,作者指出了 存在的一些限制,即它不适合制做锐利的边缘。我们都知道,当我们为游戏做模型时,不可避免地 会需要做出一些锋利的形状。

8、技术路线再调查

我利用YouTube上的视频教程梳理了几乎所有的建模软件,试图找出他们的实现机制。这些软件包括 Houdini,它的以节点为基础的建模技术让我感到震惊。我想这就是我想要的, 这就是答案, 看起来很熟悉, 对吗?:-)

9、重新开始Dust3D

我创建了一个名为poc的新分支来验证概念。我没有完全实现基于节点的建模,而是尝试定义一个新的 建模脚本语言,它可以很容易地嵌入到命令行中。当时,我构建了许多基本的mesh操作算法,如斜切计算、 布尔计算等。

9、上手Rust

我不记得确切的原因,也许是项目名称?无论如何,我被Rust语言分心了。我试着用 Rust重写 所有基本mesh算法来练习语言技能。这就是meshlite库的来由。

10、完成Dust3D

现在,我对mesh有了更好的理解,并且知道如何生成我想要的mesh,无论它光滑还是锋利。完成mesh库后, 我尝试再次构建UI。在rust世界中,没有那么多的UI框架可供选择。我做了一些调查,并尝试了许多GUI 解决方案,如bgfx,我甚至修复了bgfx的一个微不足道的问题, 并且合并进主分支。但最后,我仍然决定 使用Qt。这一次,整个编码进度非常顺利, UI用Qt,算法用Rust,配合起来很爽,而且Rust从来没有在 正常用例中崩溃,我的意思是说,Rust在建立像双向链表这样的数据结构时有一些固有的问题,所以我 需要一些不安全的代码或基于索引的系统来支持多重链接数据,如mesh处理中著名的half-edge结构,因为 基于索引的系统不受Rust语言的保护,有时,它会因一些逻辑错误崩溃。我发现Rust、C++11和Qt新的信号 插槽用起来很顺手,我也高兴地引入了Carve和CGAL库来实现mesh union操作。

11、Dust3D现状

今天,我决定分享我的故事,我已经完成了第一阶段的Dust3D。它并不完美,但这是我多年前就设想的产品。 这就是我想要的,这是我过去几年付出的答案。

12、Dust3D的未来

目前还没有实现没有自动拆解纹理,没有自动刚性处理,没有自动动画生成。还有很长的路要走,我很期待。 谢谢你的阅读。

PS:自动拆解纹理和自动刚性已经完成,自动动画正在开发中。(2018年5月30日)

原文链接:从零实现一个3D建模软件 — BimAnt