BE教程

附加包教程:58.纹理注册表

Cat_A***Lv.4 高级会员 发表于 2025-10-19 IP:0 3,281 0
AI 速览2026-09-26 00:33 生成

附加包教程第58期:详解 terrain_texture.json 纹理注册表字段

这是附加包教程系列的第五十八期,主题是 terrain_texture.json 纹理注册表。帖子先介绍纹理短名与路径关联的基本写法,再逐一讲解根对象中的 resource_pack_name、texture_name、num_mip_levels、padding 等字段,以及纹理叠加、纹理变种、quad 裁剪、tint_color 与 overlay_color 染色等进阶用法。适合已经会做方块、想深入了解纹理注册表细节的附加包开发者阅读。

  • 引用纹理短名处若为 minecraft:material_instances 组件,方块格式版本需不低于 1.21.110,否则纹理变种不生效
  • num_mip_levels 增大会显著延长加载时间,原版默认 4 较为合理
  • padding 建议取 2 的 num_mip_levels 次方减 1,如层级为 4 时取 8
本卡片由 AI 阅读本帖后生成,可能存在疏漏,请以帖子正文为准。
本帖最后由 Cat_Anchor 于 2025-10-19 21:54 编辑

前言
制作新方块时,我们经常需要给方块定义纹理。这时候,我们不能直接在相关组件中定义纹理路径,而是需要先为纹理注册一个短名,然后在相关组件中引用这个短名。

这就是 terrain_texture.json 的作用了,它相当于纹理的注册表,把纹理短名与纹理本身关联起来。但是除了这个功能,我们还可以在纹理注册表中指定更多字段,实现一些其他功能吗?我们首先来看最基本的文件结构。
基本数据
  1. {
  2.   "texture_data": {
  3.     "example": {
  4.       "textures": "textures/blocks/custom_block"
  5.     }
  6.   }
  7. }
复制代码


以上就是这个文件最基本的样子了,首先是根对象,其中有 texture_data,而 texture_data 里面就有纹理短名与纹理路径的关系,比如 example 指向 textures/blocks/custom_block。当然,这样写就能让这个文件最基本的也是最常用的功能发挥出来了,但是如果我们打开原版的 terrain_texture.json……

  1. {
  2.   "resource_pack_name": "vanilla",
  3.   "texture_name": "atlas.terrain",
  4.   "padding": 8,
  5.   "num_mip_levels": 4,
  6.   "texture_data": {
  7.     // ...
  8.   }
  9. }
复制代码


就会发现里面除了 texture_data 还有其他字段,比如 resource_pack_name。它们都有什么作用?

| resource_pack_name
目前,我们一般认为此字段没有任何作用,它似乎不会被游戏读取,修改这个值也不会导致什么变化;也许这只是用于表示注册表所在资源包名称的面向开发者的字段。

| texture_name
要解释这个字段,我们首先需要知道,那就是游戏加载时会将物品纹理、方块纹理等尺寸比较小的纹理拼接成一张大纹理,称为纹理图集,Texture Atlas。

(图片来自 wiki.bedrock.dev)
texture_name 就指定纹理图集的名称,atlas.terrain 即方块的纹理图集,其中 atlas 是图集的意思,terrain 是地形的意思。
纹理图集还有 atlas.items(物品纹理图集)、atlas.banner(旗帜纹理图集)、atlas.shield(盾牌纹理图集)等。

| num_mip_levels
游戏经常要渲染很多很多纹理,比如视野距离比较大的时候,方块纹理很容易到达十万数量级。为了优化渲染过多纹理带来的卡顿,游戏渲染远处的纹理时会降低纹理的分辨率,我们称为多级渐远纹理,Mipmaps。
每超过一定距离,游戏就会将纹理分辨率减半,纹理可以减半的次数就称为层级,也就是 Mip Levels。num_mip_levels 用于指定层级的数量,也就是游戏可以将纹理减半多少次。原版默认值为 4,那么对于 16 * 16 的纹理,游戏会自动产生 8 * 8,4 * 4 和 2 * 2 版本的纹理。(左边是 num_mip_levels = 1,右边是 num_mip_levels = 4。)
增大 num_mip_levels 的值反而会导致加载时间大幅延长,因为要制作纹理图集的很多低分辨率版本,所以 4 是比较合理的值。

| padding
我们会降低纹理的分辨率,而降低分辨率会导致纹理图集中两个紧挨着的纹理之间互相溢出,如何解决这个问题?可以用 padding 来解决。
注意纹理图集的图片,书的纹理之间有多余的像素,那就是 padding 所带来的效果。通过在纹理之间加入额外的像素,我们可以保证降低分辨率时不让多个纹理融合在一起。
padding 的值代表纹理在每个方向上拉伸出的像素数量,padding 为 4 会导致 16 * 16 的纹理在每个方向上拉伸出 8 个像素,它们的颜色和原本纹理最边缘一排的相同,最终导致纹理变成 32 * 32 的。
为了获得最好的效果,我们需要把 padding 的值设为 2num_mip_levels - 1。比如 num_mip_levels 是 4,那么 padding 是 24 - 1,也就是 8。

对于物品纹理图集,我们还可以通过在根对象使用 default_leather_color 和 default_leather_horse_armor_color 指定默认皮革服装颜色和默认皮革马铠颜色。

这就是根对象中字段的作用了。除了根对象,每个纹理短名与纹理路径的关系中是否还有其他字段?
纹理处理
我们可以先从简单的地方开始。

  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": "textures/blocks/test"
  5.     }
  6.   }
  7. }
复制代码


这是最基本的定义,纹理短名是 test,路径通过 textures 字段指定,是 textures/blocks/test。注意到 textures 是复数形式,我们可以把上面的定义写成:
  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": [
  5.         "textures/blocks/test"
  6.       ]
  7.     }
  8.   }
  9. }
复制代码

其中 textures 现在是一个数组,里面有刚才的路径。上面两种写法等效。

既然现在 textures 是数组,我们就可以再多加几个纹理路径了。
  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": [
  5.         "textures/blocks/test",
  6.         "textures/blocks/test2",
  7.         "textures/blocks/test3",
  8.         "textures/blocks/test4",
  9.         "textures/blocks/test5"
  10.       ]
  11.     }
  12.   }
  13. }
复制代码

但是无论加多少纹理路径,数组中只有第一个路径指向的纹理会渲染。那么为什么纹理路径可以写成数组?答案是——
  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "additive": true,
  5.       "textures": [
  6.         "textures/blocks/test",
  7.         "textures/blocks/test2",
  8.         "textures/blocks/test3",
  9.         "textures/blocks/test4",
  10.         "textures/blocks/test5"
  11.       ]
  12.     }
  13.   }
  14. }
复制代码

通过在 textures 字段旁边引入 additive 属性,并将其设为 true,我们指定的所有纹理就都会叠加在一起,从上到下。数组中,越靠前的纹理图层越高,它们会覆盖图层低的纹理。如果半透明像素要覆盖另一个像素,那么半透明像素将完全取代那个像素,不会有颜色的混合。

除了纹理叠加,我们还可以实现纹理变种。
  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": {
  5.         "variations": [
  6.           "textures/blocks/test",
  7.           "textures/blocks/test2",
  8.           "textures/blocks/test3"
  9.         ]
  10.       }
  11.     }
  12.   }
  13. }
复制代码

这是最基本的纹理变种,每个纹理被选择的概率都是一样的。这些选择基于方块在世界中的位置,位置不变,选择不变,纹理也不会变。
为了修改每个纹理出现的概率,我们可以把每个路径字符串改成包含原本路径字符串和权重值的对象。
  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": {
  5.         "variations": [
  6.           {
  7.             "path": "textures/blocks/test",
  8.             "weight": 1
  9.           },
  10.           {
  11.             "path": "textures/blocks/test2",
  12.             "weight": 2
  13.           },
  14.           {
  15.             "path": "textures/blocks/test3",
  16.             "weight": 2
  17.           }
  18.         ]
  19.       }
  20.     }
  21.   }
  22. }
复制代码

纹理出现概率的计算很简单,把所有 weight 值加到一起,再用单个纹理的 weight 值除以总值,就可以得到概率。比如 textures/blocks/test 的概率是 20%,另外两个纹理的概率都是 40%。

如果引用这个纹理短名的地方是 minecraft:material_instances 组件内,那么方块的格式版本需要 ≥ 1.21.110,否则不会有纹理变种的效果。

回到一开始的纹理定义,我们还可以这样写:
  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": {
  5.         "path": "textures/blocks/test"
  6.       }
  7.     }
  8.   }
  9. }
复制代码

现在 textures 数组中有一个对象,其中的 path 字段指定了路径。既然有 path,那么在 path 旁边就可能有其他字段。

  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": {
  5.         "path": "textures/blocks/test",
  6.         "quad": true
  7.       }
  8.     }
  9.   }
  10. }
复制代码

这样的字段确实存在,比如上面演示的 quad。设为 true 后,它会将渲染全部纹理改为只渲染左上角四分之一个纹理。

但是这显然很无趣,所以我们还可以……
  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": {
  5.         "path": "textures/blocks/test",
  6.         "quad": false,
  7.         "tint_color": "#D3FFF2"
  8.       }
  9.     }
  10.   }
  11. }
复制代码

给纹理染色!

加入 tint_color 属性并指定颜色后,我们就可以给纹理染色了。染色的算法是相乘,也就是“正片叠底”,Multiply。假设原本纹理的红色通道值为 r,绿色通道值为 g,蓝色通道值为 b,不透明度通道值为 a,染色后的红色通道值为 R,绿色通道值为 G,蓝色通道值为 B,不透明度通道值为 A,且以上所有值的取值范围是 [0.0, 1.0],使用上面提供的颜色 #D3FFF2 作为染色数值,那么有
R = r * (211 / 255)
G = g * (255 / 255)
B = b * (242 / 255)
A = a


染色当然很好,但是上面会给整个纹理都染色。如果只想给部分纹理染色呢?
  1. {
  2.   "texture_data": {
  3.     "test": {
  4.       "textures": {
  5.         "path": "textures/blocks/test",
  6.         "quad": false,
  7.         "overlay_color": "#D3FFF2"
  8.       }
  9.     }
  10.   }
  11. }
复制代码

这就是 overlay_color 字段的用处了,它会根据原本纹理的不透明度数值以不同强度应用染色效果。继续上面的假设,这次的颜色计算公式是:
R = r * (211 / 255) + r * (1 - a)
G = g * (255 / 255) + g * (1 - a)
B = b * (242 / 255) + b * (1 - a)
A = 1
总结
这一期讲解了 terrain_texture.json 的相对不那么常用的字段。


您需要登录后才可以回帖 登录 | 立即注册
本版积分规则