我们先盲猜一下模型应该由几个部分组成。现实生活中,模型必要部分是结构与绘图。在计算机中,渲染并不是由天然的光线进行的,而是由计算的光线组成的。这就需要我们为模型提供有关渲染的信息。因此,游戏中的模型大致分为3个部分,即结构、绘图以及渲染。在minecraft使用的模型中,还包含了模型间的继承关系等。
模型文件放置在assets/namespace/models/下,但是,根据我们第一节讲到的,models/item下的模型有独特的性质。这种性质类似于blockstates下的方块状态文件,用于引导物品模型的渲染。
物品模型文件和方块模型文件是共通的,但又有所区分。其主要原因除上述因素,还有一点是,游戏中的物品与方块有截然不同的性质。
首先,物品分为两个应用场景和多个渲染场景。物品会在物品栏中以"item"形式存在以至于接受/give,/replaceitem(1.17以后为/item),/clear,/data等命令,在掉落物中,以"minecraft:item"这一实体形式存在。除此之外,物品可以被以放入物品展示框的方式渲染出来,相同的效果还有盔甲架的各个位置,实体的头部、左右手工具等。渲染上,还有营火上以及物品栏GUI和第一第三人称左右手。
其次,方块的渲染与方块间的位置关系密切相关。这种位置关系决定了一些面剔除,即面进行渲染的情况,同时决定了方块渲染的阴影、面亮度等。同时方块状态很大程度上的改变是由于方块位置关系决定的,因此方块位置关系及其影响方块模型的构造。而物品渲染则仅仅局限于上述应用情景,不需要考虑模型间的位置关系等。
最后,方块类型的物品模型和方块模型可以相互继承,但是在文件夹中必须至少提供两个文件,即blockstates/状态文件和models/item/物品模型文件。
一个物品模型分为6个键值对,即6个属性,其中的"parent":""即继承关系,"textures"以及"elements":[{"faces":{}}]为绘图部分,"elements":[{元素对象}]为结构部分,而剩下的都为渲染部分。
{
"parent":"minecraft:block",
"textures":{
"纹理变量1":"minecraft:block/stone",
"纹理变量2":"纹理的地址",
"particle":"minecraft:block/stone"
},
"elements":[
{元素对象1},
{元素对象2}
],
"display":{
"gui": {模型显示对象},
"ground": {模型显示对象},
"fixed": {模型显示对象},
“head”: {模型显示对象},
"thirdperson_righthand": {模型显示对象},
"thirdperson_righthand": {模型显示对象},
"firstperson_righthand": {模型显示对象},
"firstperson_lefthand": {模型显示对象}
},
"gui_light":"front",
"override":[
{覆写模型对象1},
{覆写模型对象2}
]
}
当然如果使用blockbench的话,会再添加两个对象,下文会提到。
-
"parent":""前面已经提到过,即父模型的地址,标记本模型继承的对象。物品的继承关系将在下文提到。
-
"textures":{}是纹理引用地址的对象。里面填充着后续"elements":[]列表内元素使用到的"#纹理变量",但是填写在这里时要去掉#号,其对应的键值是你所要使用的"纹理地址"。其中不要忘记了"particle":"粒子纹理地址"这一项,这一项将是你破坏方块、掉落物品等产生的粒子效果的纹理来源。元素使用到的对应纹理变量地址在本文件中可以省略,但一定要在后续的继承中补充上纹理变量地址。而如果父级纹理变量地址写上之后,子级可以写也可以不写。如果写,则会覆盖父级的纹理地址,如果不写,则会继续使用父级的纹理地址。如果模型中缺少了使用到的纹理变量地址,那么在游戏中会渲染出紫黑色的缺失纹理样式。
-
"elements":[]是构成模型的元素列表。其值为{元素对象}。就像搭积木一样,我们的整体的模型就是由这样的元素搭建而成。{元素对象}如下
assets/minecraft/block/grass_block.json/"elements":[]
{
"from": [0, 0, 0],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 16, 16], "texture": "#side", "cullface": "north"},
"east": {"uv": [0, 0, 16, 16], "texture": "#side", "cullface": "east"},
"south": {"uv": [0, 0, 16, 16], "texture": "#side", "cullface": "south"},
"west": {"uv": [0, 0, 16, 16], "texture": "#side", "cullface": "west"},
"up": {
"uv": [0, 0, 16, 16],
"texture": "#top",
"cullface": "up",
"tintindex": 0
},
"down": {"uv": [0, 0, 16, 16], "texture": "#bottom", "cullface": "down"}
}
}
其实还包括旋转条目,完整的对象如下
{
"from": [x1, y1, z1],
"to": [x2, y2, z2],
"faces": {
"north": {纹理渲染对象},
"east": {纹理渲染对象},
"south": {纹理渲染对象},
"west": {纹理渲染对象},
"up": {
"uv": [x3, y3, x4, y4],
"texture": "#纹理变量",
"cullface": "up",
"tintindex": 0,
"rotation":90
},
"down": {纹理渲染对象}
},
"rotation":{
"origin":[x5,y5,z5],
"axis":"x",
"angle":22.5,
"rescale":"false"
}
}
可以看出,一个元素对象拥有4个属性。但是在方块模型中,还增添了一个属性,"shader":true,但是加了"shader"这一条目后并不影响这个模型是方块模型还是物品模型。后续讲解。
-
"from"与"to"的值都是一个列表[],分别是元素起点的坐标[x1, y1, z1]和终点的坐标[x2, y2, z2]。两个坐标即可决定一个长方体元素,这也侧面说明了一个元素就是一个长方体或者长方形平面。然而,minecraft的体素模型是限制在3x3x3的方格范围内的,这意味着,"from"与"to"的坐标范围是-16~32。通过"from"与"to"的两个坐标,我们既决定了元素的坐标位置——即"from"的坐标,又决定了元素的大小——即"to"坐标与"from"坐标的差值。经过测试,x1的值最多支持4位小数。
"from"的坐标理应该小于"to"的坐标值(x,y,z都应该小于)。然而当"from"与"to"的某坐标值相同时,元素为一平面;某两个坐标值相同时,元素为一直线(无意义);三个坐标值都相同时,元素为一点(无意义)。在minecraft中,渲染是以面的形式进行的,因此面是渲染的最小单位。线与点都没有意义。当"from"中有一个坐标值小于"to"的时候,这时候模型的渲染翻转到了立方体的内部,"to"坐标与"from"坐标的差值为负数,因此被称为负模型。负模型在一些地方可以代替正面4个元素的渲染,可以减少元素个数起到优化作用;当"from"中有两个坐标值小于"to"时,这时候模型的渲染又翻转回来, 是“正”模型;当"from"中三个坐标值都小于"to"时,模型还是翻转到了立方体内部去了。
-
"faces":{}记录着六个面的渲染信息,也因此,里面直接是6个属性:"north","south","west","east","up","down"。每一个属性值都是一个{纹理渲染对象}。
{纹理渲染对象}中有5个属性。为元素的某一个面完整地贴上纹理贴图。因此会涉及到引用纹理的哪个部位啊,引用哪个纹理啊,怎么引用纹理啊等关系。
-
- "uv":[x3, y3, x4, y4] 即是引用纹理贴图的坐标位置。从贴图的左下角(x3,y3)引用到贴图的右上角(x4,y4)。注意,对于高分辨率纹理来说,是缩放的位置,坐标范围为0
16。而对于组合类型的纹理,wiki,例如两个16x16纹理横向组合为32x16,则x3、x4对于第一个纹理来说为08,即缩放了一半。如果左坐标填写了在右坐标上面的位置或者右边的位置,贴图方向将被翻转。这也是对贴图进行上下镜像操作、左右镜像操作的方法。经过测试,x3的值最多也支持4位小数。 - "texture":"#纹理变量" 这一个属性与上级"textures":{"纹理变量":"地址"}相互对应,这里填写的"#纹理变量"是属性值,而上级"纹理变量"是键名,且少了一个#号。在上级必须填写出地址,这里才能够保证纹理被渲染到模型的相应部件,否则会渲染出紫黑块,在游戏日志中报错纹理丢失。
- "rotation":90 其值为90度的倍数。这个操作是作用于贴图渲染的,但不会真正改变原纹理本身的方向,仅仅是对渲染的结果进行旋转,即将uv所指定的位置的纹理贴图旋转多少度,再贴到模型上。贴图渲染的旋转即是在这里操作的。
- "cullface"与"tintindex"将在后文介绍。
- "uv":[x3, y3, x4, y4] 即是引用纹理贴图的坐标位置。从贴图的左下角(x3,y3)引用到贴图的右上角(x4,y4)。注意,对于高分辨率纹理来说,是缩放的位置,坐标范围为0
-
-
"rotation":{}此rotation非上面那个rotation。这个{旋转对象}是作用于模型的。
-
"origin":[x5,y5,z5]枢轴点坐标。枢轴点的意思是进行旋转的中心点,这个点可以在模型外,也可以在模型内,并且坐标范围可以扩展到无限大。也就是说,枢轴点是可以突破模型限制的!
-
"axis":"x"可选值为"x","y","z"也就是绕着经过枢轴点的某个轴进行旋转。
-
"angle":22.5旋转22.5度。可选值为22.5度的倍数,范围为-45~45度。经测试,为逆时针旋转。
-
"rescale":false是否进行模型的缩放。(缩放的原理和效果有待研究)。经过测试,"rescale":true进行的模型缩放与上述三个属性都有关系。如果"axis"的属性不存在,则默认为"y"。经过测试,模型的缩放方向取决于"axis",缩放大小取决于"angle"。其中,对于一个大小为16x16的元素面"angle":22.5将缩放为接近完整方块的大小;"angle":45将缩放为完整方块的大小,也就是相当于将底边变为16*1.414的长度。如图所示。
-
override:物品模型显性的支配者
事实上,物品模型也有类似方块状态的“物品状态”,记录在物品的NBT中。所有物品共有的“物品状态”为CustomModelData,而其他的“物品状态”则为特定物品才拥有。这里的“物品状态”(NBT)在模型文件中对应的键值名被称为物品谓词,例如,CustomModelData对应"custom_model_data"。而特定的物品与谓词大概有:指南针的角度、时钟的时间、工具的损耗与损耗值、末影珍珠与紫颂果的冷却时间、弓和弩的拉与拉伸程度、鞘翅是否破损、盾是否在格挡、钓鱼竿是否抛掷。所有有效的物品谓词以及其值见物品谓词。特殊的是,"lefthanded"对应左手玩家使用的模型,但我还未知其的效用。
- "override":[]是覆写模型情况列表,其值为{覆写模型对象}。这一属性里的对象决定了物品模型什么情况渲染什么模型。{覆写模型对象}如下。
{ "predicate": { "物品谓词": 物品谓词值 }, "model": "模型地址" },
{ "predicate": { "物品谓词": 物品谓词值,"物品谓词": 物品谓词值 }, "model": "模型地址" }
当满足"predicate":{}下所有物品谓词情况时,显示对应的模型,也就是说,这些"物品谓词":""的条件是一种“且”的关系。
当然,只有规定好的models/item/下的某些物品模型文件可以填写这一列表,详见第二节。其表现为,如果列表中的对象引用的模型也有覆写模型情况列表的话,该引用的模型的列表无效。也就是说,这里规定好的物品模型文件就相当于方块状态文件,用于决定物品模型的渲染情况。也因此可以极大地拓展物品模型。
利用CustomModelData拓展物品模型
本节内容请见 森罗万象 ,例如在第二节中我们讲到
assets/minecraft/models/item/carrot_on_a_stick.json
{
"parent": "minecraft:item/handheld_rod",
"textures": {
"layer0": "minecraft:item/carrot_on_a_stick"
},
"overrides": [
{ "predicate": { "custom_model_data": 9960001 }, "model": "minecraft:ibt/magic_stick"},
{ "predicate": { "custom_model_data": 9960002 }, "model": "minecraft:ibt/magic_stick_1"},
{ "predicate": { "custom_model_data": 9960003 }, "model": "minecraft:ibt/magic_stick_2"}
]
}
我们通过对胡萝卜钓竿的物品模型的修改,添加了"custom_model_data": 9960001条目。
第一个物品magic_stick可以通过命令/give @p carrot_on_a_stick{CustomModelData:9960001}得到。也可以通过编写数据包中的战利品表以使物品通过/loot命令得到。详见原版模组入门教程。
以物品方式渲染
这里重点挑出有关模型以物品方式渲染的有关条目。
"display":{
"gui": {模型显示对象},物品栏、背包等gui中显示的模型
"ground": {模型显示对象},掉落物显示的模型
"fixed": {模型显示对象},展示框显示的模型
“head”: {模型显示对象},用命令带在头上显示的模型(含盔甲架)
"thirdperson_righthand": {模型显示对象},第三人称右手显示的模型
"thirdperson_lefthand": {模型显示对象},第三人称左手显示的模型
"firstperson_righthand": {模型显示对象},第一人称右手显示的模型
"firstperson_lefthand": {模型显示对象}第一人称左手显示的模型
},
"gui_light":"front"
- "display":{}中的属性为各种情况与该情况下的{模型显示对象},这里用于调整模型在各个情况下的缩放状况、位置和旋转角度。模型显示对象如下
assets/minecraft/models/item/generated.json/"display":{}
"ground": {
"rotation": [ 0, 0, 0 ],
"translation": [ 0, 2, 0],
"scale":[ 0.5, 0.5, 0.5 ]
}
- "rotation": [x1,y1,z1]模型分别以x,y,z为旋转轴旋转的角度,填入[x1,y1,z1]中。大小为-180~180。在这里模型突破了前面旋转中的22.5°的倍数的限制!也就是说可以旋转任意角度!
- "translation":[x2,y2,z2]模型分别在x,y,z轴上移动多少距离,填入[x2,y2,z2]中。大小为-80~80。大于80则当成80。
- "scale":[x3,y3,z3]模型分别在x,y,z轴上缩放多少倍,填入[x3,y3,z3]中。大小为-4~4其中负数为镜像后再缩放。也就是说,缩放倍数最大可以放大到原模型的4倍!这里的原模型的大小是在各个情况下渲染的大小,各个情况不尽相同。例如掉落物的模型大小的一倍与方块大小相同,而展示框的模型大小一倍为方块大小的一半。(各边长的一半)
例如原来minecraft中限制模型大小为3X3X3,经过缩放在展示框中可以为6X6X6,而掉落物直接就是12X12X12,盔甲架的头部为7.5X7.5X7.5,小盔甲架大约为6X6X6。
也就是说,利用"display":{}可以提供展示框和盔甲架中的相比方块限制中更小限制的物品模型!
- "gui_light":""在GUI中的光照渲染模式。可选值有"side"和"front"。"side"即侧置光照,光源从物品侧面照过去,留下立体的物品渲染;"front"即前置光照,光源从物品前面照过去,留下正面的物品渲染,绕y轴旋转时模型受光照的方向不变。
物品继承关系
方块的继承关系我们前面已经讲到,这里我们讲讲物品的继承关系。
物品继承关系表。(from chyx)
{
"builtin/generated":{提供拉伸模型,相当于elements
"item/generated": {提供display的样式,为item提供显示,跟block.json作用一样
"item/bow": {},弓
"item/crossbow": {},弩
"item/handheld": {工具
"item/handheld_rod": {钓鱼竿
"item/carrot_on_a_stick": {},
"item/fishing_rod": {}
}
},
"item/template_spawn_egg": {}刷怪蛋
}
}
}
首先是内建生成物品拉伸模型,就是你只要画张贴图,会给你建一个增厚一层的模型
这里的"builtin/generated"为物品模型提供了"elements":[],而且元素使用的贴图的纹理变量为"layer0"。而"item/generated"与"block/block"一样,为模型提供了"display":{}的所有属性,相当于说,如果你继承了"item/generated",你只要在你的物品文件里写上:
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "minecraft:texture_name"
}
}
就可以完成一个物品模型的构建了!绝大多数的非方块物品的模型是这样的,也是因此,你可以通过修改"item/generated"的"display":{}以达到修改大多数非方块物品的渲染样式的目的,当然,弓、弩、钓鱼竿和工具的渲染也得手动修改。修改方式将在后续章节讲述。
物品继承关系表。(from chyx)
{
"builtin/entity":{
"item/chest": {},箱子
"item/template_banner": {},旗帜
"item/template_bed": {},床
"item/template_shulker_box": {},潜影盒
"item/template_skull": {}头颅
}
}
其次是内建生成的实体渲染模型,将实体模型渲染成物品模型,这些类型是有限的,局限于上述5个大类和盾牌、三叉戟、潮涌核心,并且严格限定使用于指定名称的文件。同样地,这些物品模型中都只是提供"display",如没有下级,则还得提供"textures":{"particle":"粒子纹理地址"}
方块模型有5个属性,少了"override"。而且少了"gui_light"并且多了"ambientocclusion"。事实上两者都填写并不干扰模型的正常运作,只是渲染的时候用在的地方不一样罢了。这个将会在后文讲解。在元素列表的元素对象中多了"shader",不填写也不影响模型正常运作。
{
"parent":"minecraft:block",
"ambientocclusion":true,
"textures":{},
"elements":[
{
........,
"shader":true
}
],
"display":{}
}
以方块方式渲染
这里重点挑出有关模型以方块方式渲染的有关条目。
{
"ambientocclusion":true,
"elements":[
{
"from": [x1, y1, z1],
"to": [x2, y2, z2],
"faces": {
"up": {
"uv": [x3, y3, x4, y4],
"texture": "#纹理变量",
"cullface": "up",
"tintindex": 0
},
"down": {纹理渲染对象}
},
"shader":false
}
]
}
-
"ambientocclusion":true调整环境光遮蔽。true为开启环境光遮蔽,默认开启,且本模型被继承的时候,本模型的环境光遮蔽效果如果被开启了,后续模型再关闭也没用。有关环境光遮蔽的详细信息请看 森罗万象 相关博文。环境光遮蔽效果很大程度上决定了模型的光照渲染。
- "cullface":"up"进行面剔除的面,值可以是"up","down","north","south","west","east"。它的作用是,当本方块(注意是方块而不是元素)对应方向有[固体方块](https://wiki.biligame.com/mc/%E5%9B%BA%E4%BD%93%E6%96%B9%E5%9D%97)存在时,该元素的这个面不进行渲染,而不管元素这个面是哪个面。(事实上并不只是固体方块,[详见](https://www.mcbbs.net/forum.php?mod=redirect&goto=findpost&ptid=1079039&pid=19183273))例如"down":{......,cullface":"up"}这里元素的地面开启了对上面的面剔除,当本方块上面放置有固体方块时,本方块的本元素的地面将不会被渲染。一般情况下选择面剔除指定的面都是相应面,例如"up":{......,cullface":"up"},但是,如果面进行旋转的话,大多数情况下无法这样一一对应,这里要主义一下。
如果不进行设置的话,将不开启面剔除。
当环境光遮蔽关闭时,决定面渲染的将是"cullface"。如果没有开启面剔除,则光照会变得奇怪;如果开启面剔除,则"cullface"指定的朝向面的亮度将作为本面的亮度([WIKI如此](https://minecraft.fandom.com/wiki/Model) "It also determines the side of the block to use the light level from for lighting the face, and if unset, defaults to the side. ")。因此,如果为了将模型的奇怪光照关闭的话,记得为模型的突出部分添加"cullface"以使光线更加自然。
- "tintindex":0着色序数。有关着色的详细信息请看 [森罗万象](https://sqwatermark.gitee.io/resguide/vanilla/model/tintindex.html#%E7%A1%AC%E7%BC%96%E7%A0%81%E7%9D%80%E8%89%B2%E6%B5%81%E7%A8%8B) 。据我的观察与经验,原版中使用到着色的分为两类,而使用到的序数均为0,这一项只要存在,即使值为1也会硬编码着色。一类是植物根据生物群系染色,染色效果和原理详见森罗万象上述章节,另一类是物品的染色,包括刷怪蛋和药水等。当然还有红石粉的染色这样特殊的存在。
硬编码只能适用于特定的blockstates下指定的方块模型和models/item下特定的物品模型,其他模型即使填写该项也不会进行染色。当编辑植物的方块模型的时候,如果不填写这一项,则该元素不会被染色而是保留纹理原本的颜色。例如草方块模型中,只有顶部和边缘的贴图才会被染色,而泥土部分不会被染色。
- "shade":true开启阴影,默认开启。环境光遮蔽是相对于方块来说的差异效果;而阴影更多的还与元素有关系,对元素进行进一步的阴影渲染。如果开启环境光遮蔽的情况下关闭阴影,则不会渲染一个元素从上到下的阴影效果,而会继续渲染不同元素间的光照差异。然而如果关闭环境光遮蔽,开启阴影一定程度上可以提供较为真实的光照阴影效果。
利用“多余”的方块物品模型文件拓展物品模型
与物品模型相同的是,方块模型也可以添加"display"。这一项目对于方块模型来说没有作用。但是可以被继承。在minecraft原版文件中,方块物品的物品模型就是直接一句{"parent":"minecraft:block/stone"}。简单直接粗暴。
于此同时,我们可以看出来,方块模型和物品模型文件是可以共用的,然而对于游戏的注册来说,并没有必然联系。也就是说,同一个方块放置出来的效果和以物品显示的效果根本没有关系,仅仅只是因为原版文件中物品继承了方块模型而已。
因此,我们可以利用这一特点,利用方块物品这一“无用”的模型来制作模型的拓展包。例如ITP。
Blockbench为模型的json文件提供了组与命名的操作,而组还可以规定旋转的枢轴点以整体旋转、整体位移等。
{
"credit": "Made with Blockbench",
"texture_size": [32, 16],
"groups": [0,
{
"name": "wart_block_1",
"origin": [8, 8, 8],
"children": [
{
"name": "group",
"origin": [9, 27, 18],
"children": [
{
"name": "group",
"origin": [9, 27, 18],
"children": [1, 2]
},
{
"name": "group",
"origin": [9, 27, 18],
"children": [3, 4]
}
]
}
]
}
]
}
-
"credit":""是每次使用blockbench编辑都会产生的,你也可以改成你的昵称,例如"By Xiao2. From Petter Foliage. Made with Blockbench"。不过你用完blockbench还是会给你改掉就是了。(还没找到内置修改credit的方法)
-
"texture_size": [x, y]为纹理分辨率。用于标识Blockbench中将要使用怎么样分辨率的uv编辑器。默认为16x16。当使用组合型uv或者扩大分辨率时会用到。
-
"group":[]用于归并元素的组合,元素的组将显示在blockbench的右下方。列表的值为数字和{组对象}。这个数字是元素的组序号,也就是元素在"elements":[]列表中的序号。组对象如下:
{
"name": "组名",
"origin": [9, 27, 18],
"children": [1, 2]
}
-
- "name":""是显示在blockbench右下方的组名。
- "origin":[x,y,z]是一个有3个数字的列表,这三个数就是本组总枢轴点,用于相对于组的旋转操作。
- "children":[]本组的成员,可以是数字,也可以是{组对象},跟递归有点像,文件夹嘛。同上。
- 了解Blockbench里面的组键值对关系有助于之后手动进行组的合并以及排序等操作,手动操作有时候更加方便。