Minecraft 模组开发:方块模型的渲染修正与任意角度旋转实现
酒石酸菌
2026年06月10日 21:53

在做 Minecraft Java 版模组开发时,给方块设置多个朝向是很常见的需求。一般来说,我们会在 BlockState 的 JSON 文件里给不同朝向指定模型,再顺手加上旋转参数:

代码块
JSON
自动换行
复制代码
 "facing=south": {
     "model": "xxx:block/xxxx",
     "y": 180
 }
复制成功

不过,这里的 y 参数有一个比较硬的限制:只能写 0、90、180、270 这四个值。如果你的方块想像原版头颅那样支持 16 个方向,甚至更多更细的旋转角度,这种写法就不够用了。

遇到这个限制时,有些作者可能会干脆放弃普通方块模型,转而用 BlockEntityRenderer 来画。这样当然能实现效果,但代价也比较明显:普通方块的顶点计算通常只会在区块刷新时发生,而附加实体相关的渲染则要每一帧实时计算。所以如果大量依赖 BlockEntityRenderer,多少会给游戏的渲染效率带来一些压力。

如果想在开发便利和运行效率之间取得一个比较好的平衡,可以利用 Forge 或 NeoForge 提供的扩展模型字段。它们在原版方块、物品模型的基础上,额外提供了 transformrender_type 等很实用的字段。

Render Type 字段

这个字段经常用来修复模型透明度导致的显示问题。

比如,贴图里本该透明的部分在游戏中变成了奇怪的黑块,这时通常只要配置合适的渲染类型就能解决。

Forge/NeoForge 为不同场景提供了几种常用的渲染策略:

  • solid

    • 特点:不处理任何透明材质。

    • 用途:适合石头、木板这类完全不透明的方块。

    • 注意事项:不需要混合或排序,渲染性能最好。

  • cutout

    • 特点:要么全透明,要么完全不透明,没有半透明过渡。

    • 用途:适合带有透明边缘的方块,比如铁栏杆或普通玻璃板。

    • 注意事项:不生成多级渐远纹理(Mipmap),远距离观察时可能出现边缘闪烁或摩尔纹。

  • cutout_mipped

    • 特点:保留边缘硬切效果,同时开启渐远纹理。

    • 用途:适合草丛、蕨类植物这类需要远处抗锯齿的方块。

    • 注意事项:通过更平滑的过渡,可以有效缓解远景闪烁问题。

  • cutout_mipped_all

    • 特点:强制物品栏里的微缩模型也应用渐远效果。

    • 用途:主要用于对物品栏显示有特殊渐远需求的情况。

    • 注意事项:因为容易让物品图标看起来发糊,实际开发中很少会用到。

  • translucent

    • 特点:支持完整的可见度通道混合(Alpha 混合),可以实现真正的半透明。

    • 用途:适合水面、冰块、染色玻璃这类带有混合渐变效果的半透明物体。

    • 注意事项:因为需要进行顶点排序,性能开销会更高一些。

  • tripwire

    • 特点:比较特化的多阶段渲染类型,结合了 Mipmap 和专用着色器。

    • 用途:主要服务于极细模型(如绊线)以及特殊天气下的视觉表现。

    • 注意事项:普通模组开发里基本很少需要用它。

顺带一提,早期 1.6.4 版本曾经有玩家通过矿透材质包作弊。后来 Mojang 为了避免这类问题,把相关遮挡逻辑(noOcclusion)的参数写死进了方块的构造参数里。

下面这张图可以帮助理解部分相关参数之间的关系:

Transform 字段

解决了渲染类型的问题之后,实现任意角度旋转的重点就落在了 transform 字段上。

这个字段可以对整个方块模型应用全局矩阵变换,包括平移、缩放和旋转。

代码块
JSON
自动换行
复制代码
 {
     "translation": [0, 0, 0],
     "scale": [1, 1, 1] // 也可以写成 "scale": 1,此时代表 x y z 缩放都是 1
 }
复制成功

旋转部分一般有两种写法。

最直观的是欧拉角模式,只需要指定旋转中心,以及围绕 X、Y、Z 轴分别旋转多少度即可。旋转中心既可以使用预设位置,也可以自己指定空间坐标:

代码块
JSON
自动换行
复制代码
 {
     "origin": "center", // 还可以填 corner 或者 opposing-corner,分别指代 (0,0,0) 和 (1,1,1)
     "rotation": [0, 0, 0] // 分别表示 x y z 旋转角度
 }
复制成功

如果想自定义旋转中心,可以写成这样:

代码块
JSON
自动换行
复制代码
 {
     "origin": [0.5, 0.5, 0.5],
     "rotation": [0, 0, 0]
 }
复制成功

另一种写法是基于四元数结构的定义方式。它通常需要传入左侧和右侧的旋转矩阵向量,再结合平移或缩放来构建完整矩阵(和 26.1 版本 items 里的变换写法完全一致):

代码块
JSON
自动换行
复制代码
 {
     "left_rotation": [0.0, -0.7, 0.0, 0.7],
     "right_rotation": [0.0, 0.0, 0.0, 1.0],
     "scale": [1, -1, -1],
     "translation": [0.5, 1.5, 0.5]
 }
复制成功

虽然这种格式看起来没那么直观,但它可以避开欧拉角在某些情况下容易遇到的万向节死锁问题。

Data Generator

实际写模组的时候,手动维护十几个旋转变种显然不太现实。遇到这种批量生成 JSON 的需求,Data Generator 就是最省心的方案。

比如在 1.20/1.21 的开发环境下,可以在对应的 BlockModelProvider 类里这样写:

代码块
Java
自动换行
复制代码
 // 读取基础的、没有矩阵变换的父级模型
 ResourceLocation parent = modLoc("block/%s".formatted(name));
 ​
 // 根据需要的变种数量确定循环范围。以原版头颅为例,一共划分出 16 个方向,每次变化 22.5 度
 int max = RotationSegment.getMaxSegmentIndex();
 ​
 for (int i = 0; i <= max; i++) {
     // 把这些变种模型放在 name 目录下
     ResourceLocation file = modLoc("block/%s/rot_%d".formatted(name, i));
     
     withExistingParent(file.toString(), parent)
             .rootTransforms()
             // 将旋转中心设为方块中心
             .origin(TransformationHelper.TransformOrigin.CENTER)
             // 每个变种对应的角度
             .rotation(0, -i * 22.5f, 0, true)
             .end();
 }
复制成功

最后,只需要在 BlockState 里给每个旋转状态指定对应的模型变种,就可以实现方块的任意角度旋转了。