Cat_Anchor 发表于 2025-11-20 22:31:57

万象添补加入纸类建材,不透明方块连接纹理的技术突破

本帖最后由 Cat_Anchor 于 2025-11-20 22:38 编辑

这里是我的第 114 篇闲聊帖。距离上次写闲聊又过去了 9 天,现在我终于来写一篇新的闲聊帖了。

---

[万象添补](https://klpbbs.com/thread-153081-1-1.html) `0.17.7` 的开发工作刚刚完成,主要是加入了三种纸质的建筑方块,纸盒、纸板和纸板口。![纸盒房子,外部视角](https://pic1.imgdb.cn/item/691f18683203f7be0019e104.jpg)

这些都是我从一个想法延伸出来的特性,它们的基础物品是“竹浆纸”,一种用竹子就能合成的纸。我在配方里加了水桶,因为听说竹浆要用水。

用 4 个竹子和一桶水合成竹浆纸之后,就可以用它合成上面提到的三个建筑方块了。其实它们的价值不仅体现在概念本身上,更体现在我实现了一种新的制作带连接纹理的方块的方法。

带连接纹理的方块的制作,一直是附加包制作的难点。以前我们没什么接口,于是只能用 `minecraft:queued_ticking` 不停检测,通过方块事件设置方块状态,再用 `bone_visibility` 修改模型骨骼的可见性。不仅如此,我们还需要定义复杂的方块模型,免不了遇到深度冲突。

> 深度冲突就是两个不同纹理的平面互相重叠并闪烁的现象,“深度”代表“Z 轴上的”。说实话,我一开始以为“深度冲突”就是冲突程度非常深的意思。

无框玻璃的制作历程很能反映我们在“带连接纹理的方块”方面的技术进步。一开始,我们只能直接修改玻璃纹理,把边框去除,这也是最原始的一种方法。它的缺点很明显,那就是一整块玻璃都没了边框,分不清它与普通方块的界限。

后来我们使用骨骼可见性,这也是无框玻璃板至今还需要的方案。很久以来的进步,无非是把方块事件升级成了自定义组件而已。

但是最近,情况有了改观。现在最新的技术是,对于无框玻璃,我们可以直接使用方块剔除,不需要任何方块状态;对于无框玻璃板,我们需要方块状态、骨骼可见性和方块剔除的综合运用。

---

相对于一开始,这已经是很大很大的,颠覆性的进步了。

然而还有一座更为艰巨的技术大山横在我们面前。不透明方块连接纹理
对于玻璃那样的方块,方块之间相互连接的纹理很简单,因为我们实际上是在渲染玻璃的边框。一个位置要么有边框,要么没有,就这么简单。

对于染色玻璃,情况会复杂一点,不过也只是一点点。一个位置要么有边框,要么带半透明的颜色,这也很简单。就算我们把半透明颜色与边框叠加渲染,看起来也不会非常奇怪,毕竟那只是一种半透明的覆盖层。

对于不透明方块,方块中间的纹理当然是恒定不变的;可是方块边框的纹理不一样,它要么是边框纹理,要么是中间的纹理。如果只是边,那也没多大问题;问题出在角上。

为了让方块边框纹理正确渲染,我们要让两个面在角落重叠。正常来说这没什么问题,因为角落那里尝试渲染的是同一个纹理;但是一条边尝试渲染方块中间的纹理,而另一条边尝试渲染边框纹理时,方块角落就会尝试渲染不同的纹理。这就会产生深度冲突。

而就是这个深度冲突,成了怎么也攻克不了的难题。

---

不过最后会解决的。

我尝试创建考虑了角落情况的方块模型。一个顶面,我们需要创建 8 个立方体,用于渲染边框;另外 8 个用于渲染内部,也就是连接时候的纹理;最后还需要 1 个立方体渲染中间,恒定不变的那部分。这样算下来,一个面就要 17 个立方体,6 个面就是 102 个立方体,而每个立方体都代表着一个骨骼可见性中的 Molang 表达式。

就算一切变换的计算得当,就算把各种方位问题搞清楚,就算克服重重困难写好了 102 条 Molang 表达式,最后我们也无法成功。

因为骨骼可见性的条件数量,被卡死在了 64 个。我们最多只能定义 64 个规则,对应 64 条 Molang 表达式。

102 大于 64。我只好放弃这条路线,研究其他方向。

---

说是其他方向,研究起来哪有那么容易?踌躇一会儿之后,我选定了第二条研究路线(是的,“第二条”说明了此路不通,还会有第三条),那就是把外层边框与内层连接纹理错开一点。结果方块上产生了难看的黑色细线,这是我不能接受的。更糟糕的是,深度冲突的问题也没有完全得到解决。

这是一个历史悠久的漏洞,我还原纪念碑谷时就遇到过。在某些方向上,某些坐标上(我没有发现明显规律),方块的某些立方体坐标会丢失精度,导致原本有一定距离的两个立方体重叠。这里的立方体在某个轴上的尺寸为 0,它们实际上指的是平面。一旦两个不同纹理的平面重叠,深度冲突就产生了。

最后,由于这个漏洞,我不得不放弃第二条路线,转而提出第三条路线。

---

而第三条路线是可行的。

我完全抛弃了旧有的那些模式。为什么边框必须是附着在纹理上的?我们完全可以把它做成立体的!于是我立刻开始制作,由于一个正方体只有 12 条棱,我们只需要 12 个代表边框的立方体,和 1 个代表方块本身的立方体。

更令我惊讶的是,写完 Molang 表达式之后,我发现它居然可以方块剔除规则化!也就是说,用 Molang 表达式能完成的,方块剔除也可以!这看似不怎么重要,而且这样做之后数据的可读性大大下降了,但是——

这样我们就不需要方块状态了!

写完整个数据定义之后,我惊叹于这个一开始笨重无比的文件现在多么简洁优美。

```json
{
"format_version": "1.21.130",
"minecraft:block": {
    "description": {
      "identifier": "complementary:paperbox"
      // ...
    },
    "components": {
      // ...
      "minecraft:geometry": {
      "identifier": "geometry.paperbox",
      "culling": "complementary:paperbox_culling"
      }
      // ...
    }
}
}
```

`format_version` 是格式版本,`identifier` 是方块 ID,除去其他基本属性不谈,这个方块使用的模型 ID 是 `geometry.paperbox`,而关于方块连接纹理的全部秘密,都藏在 `complementary:paperbox_culling` 里面……

---

但是很可惜,最后我做出来的结果,还是存在一定的深度冲突的问题,而且现在我没有时间修复它。不过我已经知道了一种修复方法,修复它也只是时间问题了。明天我们早放学,我就可以找时间修复它了。说不定,在这篇帖子核审完成之前,我就解决了这个问题(前提是核审不给力)。

对了,上个版本我还加入了“相位平面”,不过那只是我一时想出的点子,它的代码花了我一番功夫,最后结果可能还不值得我在上面花的时间。不过我最终还是把它实现出来了。

你们可能发现[万象添补](https://klpbbs.com/thread-153081-1-1.html)的版本号停留在了 `0.17.x`。这是因为,在计划中,`0.17` 之后就应该是 `1.0` 了,但是我还没准备好宣布[万象添补](https://klpbbs.com/thread-153081-1-1.html)正式发布。也许它会继续停留在 `0.17.x` 一段时间,不过这某种意义上也是好事,因为这时候发现什么重大漏洞了还可以用一用“现在还是测试版”的借口。
页: [1]
查看完整版本: 万象添补加入纸类建材,不透明方块连接纹理的技术突破