mc-texture · git:20261103.0de65ea · 2026-11-03 · sha256 8494e2658cb1b9ee

mc-texture git:20261103.0de65eaA

Immutable. This exact content never changes and is served at /api/v1/blob/8494e2658cb1b9ee.

---
name: mc-texture
description: 像素纹理、纹理动画与特效。与 pixel-art-upstream(junyudev/pixel-art,MIT,已收录)配套使用——32px 以上或画人物/立绘时同时读它。当用户要画、改或修方块纹理、物品图标、GUI 贴图、粒子贴图、烟花/爆炸/烟雾特效,或要做 .png.mcmeta 纹理动画时激活:先问清用户的风格要求,用户没说再按 MC 惯例(16×16、可平铺、材质符合游戏内逻辑,如铜的四级锈蚀);照「平涂剪影→打光→内部细节→描边→五官」分层画,渲染后按 1× 原尺寸核对并至少迭代 3 轮,动画用参数和公式生成每帧。模型 JSON 走 mc-model,目录与 pack.mcmeta 走 mc-resourcepack。不适用于游戏内渲染效果调试与 GeckoLib 几何动画。
---

# mc-texture — 像素纹理与纹理动画

## 铁律

1. **1× 认得出**是唯一硬判据。放大图好看不算数,缩小到真实尺寸认不出就是重画。
2. **用户要求优先于 MC 惯例**。用户说了风格/色数/尺寸就照办;用户没说,才按本文的 MC 默认。
3. **质量来自迭代次数**,不是第一版的想象。至少 3 轮,每轮写清改了什么、为什么。
4. **只输出文字也要闭环**:写文本网格 → 渲染 PNG → 看图 → 改数字 → 再渲染。

## 配套技能:什么时候去读 pixel-art-upstream

本技能与 `pixel-art-upstream`([junyudev/pixel-art](https://github.com/junyudev/pixel-art),MIT,已原样收录)**是一套,不是两套**。默认两个一起用:

- 本文 = **MC 特有规则**(尺寸、可平铺、材质逻辑、mcmeta 动画、接线)
- 上游 = **长篇方法论**(32px 以上的构造法、脸和手的画法、风格卡、概念图转像素)

**先读上游再动手的场合**(用 `read_knowledge_resource uri=mcskill://skill/pixel-art-upstream`,或直接读 `common_skill/pixel-art-upstream/`):

| 场合 | 读哪篇 |
|---|---|
| 画布 > 32px(实体贴图、立绘、大 GUI) | `common_skill/pixel-art-upstream/reference/large.md` — 构造法全流程,23KB |
| 要把参考图/AI 出的"像素风"图转成干净像素画 | `common_skill/pixel-art-upstream/reference/imagegen.md` |
| 需要按某款游戏的风格对齐(尺寸、共享调色板、脸型语法) | `common_skill/pixel-art-upstream/reference/small.md`;风格卡目录未随仓库分发,见本节末 |
| 想知道「1× 判据」「半尺寸放大失败」这些结论的出处 | `common_skill/pixel-art-upstream/reference/large.md` 的 Lessons 一节 |

**用它的渲染器做交叉验证**:两个渲染器各有一套校验,能互相抓错。

```bash
# 上游(需要 python + pillow + numpy),会报包围盒、贴边、孤立像素
python -I common_skill/pixel-art-upstream/tools/grid.py render sprite.txt out/ --size 16x16

# 本仓(零依赖),会报孤立像素、半透明、尺寸不符
node common_skill/mc-texture/tools/render-pixels.mjs sprite.txt --out out/sprite.png --size 16x16
```

⚠️ 上游 `grid.py` 用 `Path.read_text()` 读输入,**默认编码不是 UTF-8 时会报 UnicodeDecodeError** ⇒ 给它的 `.txt` 里不要写中文注释,注释单独放或改英文。

⚠️ 上游的 `styles/eastward*` 是 Eastward(Pixpil)风格卡,上游 README 自述「授权使用」但**未给 CC 许可** ⇒ **不要搬进工程、不要当素材分发**,只在本机当参考。

**冲突时以本文为准**:上游面向通用像素画(RPG 立绘、角色 48×64),本文面向 MC(16×16 方块必须可平铺、材质要符合游戏内逻辑)。上游讲「人物身高 52px」「头身比」的那些数值,与 MC 方块贴图无关,别硬套。

## 第 0 步 · 先问,再画

动手前必须确认(用户已明确的跳过,**不要重复问**):

| 问什么 | 用户没说时的 MC 默认 |
|---|---|
| 尺寸 | 方块/物品 16×16;实体 64×64 起 |
| 风格 | 原版像素质感,硬边、无渐变 |
| 色数 | 方块物品 4–8 色;实体 ≤16 色 |
| 描边 | 深色同系描边(不用纯黑) |
| 光源 | 左上 |
| 材质的游戏内状态 | 按该材质的 MC 逻辑,见第 6 步 |

**用户的要求和 MC 惯例冲突时,永远听用户的**,并在交付时一句话说明哪里偏离了原版(用户可能是故意的)。

## 第 1 步 · 定尺寸与落点

| 用途 | 尺寸 | 落点 |
|---|---|---|
| 方块纹理 | 16×16,**必须可平铺** | 资源包 textures/block/ 下的 `<name>.png` |
| 物品图标 | 16×16 | 资源包 textures/item/ 下的 `<name>.png` |
| 粒子 | 8×8 / 16×16 | 图集行优先 |
| GUI | 256×256 图集内切 | 按控件格子对齐 |
| 实体 | 64×64 起 | 按 UV 展开 |
| 纹理动画 | 帧宽 × (帧数 × 帧高) | 竖排图集 + `.png.mcmeta` |

**可平铺**是方块纹理的隐藏要求:把图往右、往下各复制一份拼起来,接缝不能看得出。做法是让纹理元素跨过边缘时从对侧接回来。

## 第 2 步 · 规划(先写版式再画像素)

按**单位格**想,不要直接想像素坐标。16×16 的方块可以当 8×8 个 2px 格来布局。

写下来再动手:

```
画布: 16×16
主体占位: 主体 x2-13, y2-13
对称轴: x=7 (或「不对称」)
大形体坐标: ...
```

**对称的东西只画一半**,另一半用镜像生成:像素 x 的镜像是 `W-1-x`,宽 `w` 位于 `x` 的部件镜像到 `W-x-w`。奇数宽最容易错一格。

## 第 3 步 · 调色板

**色数就是质量上限。** 每种材质只要 2 档(主体 + 暗部),最多给 1–3 种材质配亮部。所有材质都配三档 = 花。

**色阶生成法**(比单纯调明暗有质感):暗部往**冷**偏(加蓝紫),亮部往**暖**偏(加黄)。手动做两三个纯色即可,逐像素渐变交给抖动或换色阶。

**明度要分散**:别让所有暗色落在同一行,否则远看是一条糊带。

## 第 4 步 · 分层画(顺序不能乱)

这是保证「不是一堆气球拼起来」的关键。**严格按这个顺序**:

```
1. 平涂剪影    只用底色,确认形状对不对
2. 打光        上部/左侧提亮,下部/右侧压暗(一个光源,全局统一)
3. 内部细节    花纹、铆钉、锈斑 —— 必须在打光之后,否则会被当成洞而围一圈阴影
4. 描边        1px 深色同系描边
5. 特殊标记    眼睛、高光点、文字等,放最后保持干净
```

**线只画在形体交叠处**(甲片压住底板、帽檐压住头)。同一块材质内部不描线,否则就是「每块零件各自描边」的气球拼装。

**小图形用字符画**:4–6px 的圆用字符画比几何原语好看(原语在这个尺寸会画成方块)。

## 第 5 步 · 画法守则

- **剪影不对称**:1× 下涂黑成一块还能看出是什么。左右完全对称 = 认不出
- **特征比你以为的更大更黑**:16×16 里眼睛/铆钉至少 1–2px;单个 1px 点 = 噪点
- **一个光源**:暗部只在背光一侧,全图统一打光
- **禁渐变、禁抗锯齿**:透明度只 0 或 255。要有明暗就用纯色块分区
- **别做「半尺寸再放大」**:缩放器会把边缘磨圆,细节永远回不来

## 第 6 步 · 符合 MC 逻辑(用户没指定时)

纹理要让人一眼认出「这是 MC 里的这东西」。**先看该材质在游戏里的实际状态**,再决定画成什么样。

### 6.1 材质逻辑表

| 材质 | 画法要点 |
|---|---|
| 石头 / 矿石 | 以噪点为主,无方向性;矿石 = 石底 + 矿斑,矿斑要亮于石底 |
| 木头 | **有纹理方向**,年轮/木纹沿一个轴;六个面各不相同(顶底是年轮) |
| 金属(铁/金) | 平整,靠**大面积高光块**表现,不是噪点 |
| 铜 | **有四级氧化过程**,见下 |
| 玻璃 | 几乎全透明,只有边框和几条高光 |
| 植物 | 明度变化大,边缘参差,不用描边 |
| 羊毛 | 细密均匀的噪点,无高光 |

### 6.2 铜的锈蚀是**过程**,不是「铜块加一块绿」

这是最容易画错的一条。MC 的铜有**四个阶段**,是绿色逐渐覆盖铜面的过程:

| 阶段 | 外观 | 英文 |
|---|---|---|
| 0 | 纯铜色,无绿 | Unoxidized |
| 1 | 铜色为主,**绿色斑点开始出现** | Exposed |
| 2 | **绿色为主,棕色斑点** | Weathered |
| 3 | **蓝绿色为主,还有几处绿斑** | Oxidized |

要点:

- **绿色从边角开始**向外扩散(游戏里氧化从棱角开始蔓延)
- 阶段 1 的绿是**零星几点**,不是一大块
- 阶段 2 已经**反过来**了:底色变绿,剩铜色斑点
- 阶段 3 是**青绿(teal)**,不是草绿
- 涂蜡(waxed)不氧化;斧头可以刮回上一个阶段

画铜之前先问用户要**哪一阶段**。画「半铜半绿、绿占三分之一」这种中间态,游戏里没有对应物。

**成套交付**:如果这个铜件会跟着方块一起氧化,就该把四张都做出来(原版铜块/楼梯/门都是四套贴图)。只画一张然后说「这是铜」,等于没做。

其它材质同理,先问「它在游戏里是什么状态」,再决定画哪一档:

| 材质 | 游戏内状态 | 画法差别 |
|---|---|---|
| 铜 | 四级锈蚀 | 四套全做,绿色占比逐级上升 |
| 木头 | 原木 / 去皮 / 木板 | 原木带树皮纹、去皮是裸木、木板是平行板缝 |
| 石头 | 石头 / 圆石 / 石砖 | 圆石是不规则块面,石砖是规整分隔 |
| 泥土 | 普通 / 耕地 / 湿润 | 湿润明显更深 |
| 作物 | 生长阶段 | 每阶段高度和颜色不同 |

### 6.3 其它要遵守的 MC 惯例

- 方块纹理 16×16 且可平铺
- 同一套资源内不要混用 16 和 32 分辨率
- 发光类(荧石、海晶灯)用亮块 + 少量暗点,不要画真实光照
- 物品图标留 1px 透明边距,别贴满整格

## 第 7 步 · 渲染与核对(每轮必做)

```bash
node tools/render-pixels.mjs NAME.txt --out out/NAME.png --size 16x16 --scale 16
```

脚本会拦:行宽不一致、未定义色号、尺寸不符;并警告孤立像素与非 0/255 半透明。

**渲染后四问**,任何一问不过就回第 4 步:

1. **1× 认得出吗?** 把 1× 当真图标看,能不能立刻说出这是什么
2. **剪影清楚吗?** 全涂黑还有形状吗
3. **色数对吗?** 超预算就合并相邻档
4. **光照一致吗?** 暗部是不是都在同一侧

### 反向指认(最有效的一招)

遮住标题,问自己——或**让另一个 agent 只看图、不给任何描述**,问「这是什么?每个部件是什么?」。它可能读成完全别的东西。规则检查不出来的误读,只有这一招能抓。

### 读不到图时

把图打印成字符回读,一样能看出轮廓、明暗分区和五官位置。

## 第 8 步 · 纹理动画

MC 的纹理动画 = **竖排图集 + 同名 `.png.mcmeta`**,不是 GIF。

### 8.1 用参数生成每帧,不要手画

身体/主体只画一次,变化量做成参数:

```
frame(n) = base + 参数(n)      // 参数可以是呼吸幅度、帧序、缓动值
```

这样所有帧天然同尺寸、同锚点、风格一致。**手画每一帧一定会跳。**

### 8.2 排图集

每帧尺寸完全相同,总高必须能被帧高整除,否则游戏显示紫黑缺失纹理。竖排最不容易错:`16×48` = 3 帧 16×16。

### 8.3 写 mcmeta

`assets/<modid>/textures/block/<name>.png.mcmeta`(文件名**带 `.png`**):

```json
{
  "animation": {
    "frametime": 2,
    "interpolate": false,
    "frames": [0, 1, 2, 1]
  }
}
```

| 字段 | 含义 | 默认 |
|---|---|---|
| `frametime` | 每帧几 game tick | 1 |
| `interpolate` | 自动补中间帧 | false |
| `frames` | 帧序;可只列部分帧,或用 `{"index":N,"time":T}` 定时 | 全部按序 |
| `width` / `height` | 单帧宽高;不写按最短边推断 | 自动 |

**`frames: [0,1,2,1]` 做来回循环**,比画 4 帧省事——最常用的省帧技巧。

### 8.4 动画守则

- 帧间**只改该动的部分**,别整张重画(会闪)
- 相邻帧位移 ≤ 1–2px,否则跳
- 首尾要接得上:末帧的下一帧就是首帧
- 做呼吸/起伏时,附属部件(天线、挂饰)**滞后一拍**
- 帧数够再用 `interpolate`——2 帧补间只会变糊

### 8.5 特效类动画:用规则算,不要手画

爆炸、火焰、烟花、烟雾、魔法这种**粒子特效**,每一帧都是「同一套规则在不同时刻的结果」。手画每一帧必然跳,正确做法是写规则:

```
1. 拆元素   每个元素记 t0(出生时间)、life(寿命)、初速度、方向
2. 生成     随机必须带种子(mulberry32),否则每次跑都不一样、没法逐版对比
3. 积分     固定步长推进:速度受阻力衰减,烟受浮力上升,火花受重力下坠
4. 着色     按「已飞出的距离」或「剩余寿命」取调色板一档,不是按帧号
5. 清理     只对烟团层做开运算/去碎块;1px 的火花和余烬不能一起清理
```

**四个必踩的坑**(都是上游实测出来的):

| 症状 | 病因 | 修法 |
|---|---|---|
| 火球歪成一条斜线 / 粒子叠成一个点 | 方向完全随机会扎堆;同方向粒子速度相同会重叠 | 方向用分层抽样(黄金角或等分),同方向必须**分壳层给不同距离** |
| 中心是空的,像个环 | 按年龄着色,但粒子都从外壳出发 | 改按**已飞出距离**着色,并给球心一点持续辉光 |
| 冷却时中间一整块红盘 | 用了整体温度场,所有粒子一起变色 | 每个粒子**单独**按自己的热度冷却,时间常数 tau 各不相同 |
| 火球表面有白划痕 | 火花从中心出发却画在火球上面 | 火花画在烟团**后面**(分层绘制) |

**浅色特效必须检查亮背景**:白色、浅黄、青色在浅色背景上会消失。解法是加 1px 深色描边,或让最暗一档足够暗。

**判定要三张图一起看**:
1. 联系表(逐帧并排)——看形状和节奏
2. 三种背景(暗/中/亮)——看可读性
3. 1× 原尺寸——看游戏里的真实大小

### 8.6 MC 烟花:五种形状是确定的

做烟花前先查 MC 的机制,别自己发明。官方语料(`resources_server_loottables_lootfunctions.md`)写明:

```
shape 合法值 = "small_ball" | "large_ball" | "star" | "creeper" | "burst"
             缺省 small_ball
另有 trail(拖尾)与 flicker(闪烁)两个附加效果
```

| 形状 | 外观 | 画法 |
|---|---|---|
| `small_ball` | 小球,半径小 | 均匀球面 + 少量粒子 |
| `large_ball` | 大球,半径大 | 同球面,粒子更多、速度更大 |
| `star` | 星芒状,有放射光条 | 角度连续铺满 + 光芒方向拉长(关键:光条是**速度差**做出来的,不是画出来的) |
| `creeper` | 苦力怕脸 | 平面网格分布,正面朝观察者 |
| `burst` | 球状但密度不均 | 球面 + 随机偏置 |

**颜色是染料色**,主色 + 渐隐色(fade)。同一颗炸开的星里颜色要同步渐变,不能各变各的。

本仓库自带生成器:`common_skill/mc-texture/tools/firework.mjs`

```bash
node tools/firework.mjs out/ --shape star --fps 12 --size 32 --trail --flicker
# → firework.png(竖排图集)+ firework.png.mcmeta
```

### 8.7 预览交付:GIF 与逐帧图

图集(`.png`)在编辑器里不会动,验收时必须能看到动画。两种交付物:

```bash
# 1) GIF:给用户直接播放(零依赖,自己编码 GIF89a)
node tools/gif.mjs out/firework.png out/firework.gif --delayCs 8 --scale 6

# 2) 逐帧分解图:所有帧并排,用于核对节奏和跳帧
#    (联系表比 GIF 更能看出"哪一帧不对")
```

**GIF 延迟换算**:`delayCs` 单位是 1/100 秒。12 fps ⇒ 每帧 8.33cs,取 8;6 fps ⇒ 16cs。

**预览页要自包含**:交付给用户的 HTML 不要把 GIF 写成相对路径 `<img src="star/firework.gif">`。
本地 `file://` 打开时,浏览器常因安全策略拒绝加载同目录资源,用户看到的是空白框 —— **而且页面不会报错,只是静默空白**。

正确做法是把 GIF 直接 base64 内嵌进 HTML:

```js
const b64 = fs.readFileSync(gifPath).toString("base64");
const img = `<img src="data:image/gif;base64,${b64}">`;
```

代价是文件变大(五个 GIF 约 108 KB),换来「双击就能看、换机器也不坏」。

**注意**:GIF 在部分聊天/文档宿主里只会显示首帧(被转成静态图)。要让人真看到动画,
**同时交付 GIF + 逐帧分解图**,或做一个 HTML 播放器页。

### 8.8 拼图集

```bash
node tools/render-pixels.mjs f0.txt f1.txt f2.txt --atlas out/x.png --frametime 3
```

自动校验每帧尺寸、竖排拼接、写出 `.png.mcmeta`。

## 第 9 步 · 接线

文件放 `assets/<modid>/textures/block/` 或 `assets/<modid>/textures/item/`,命名与模型里 `textures` 引用**逐字对齐**(大小写敏感,不要空格)。

- 引用完整性 → `audit_resources`
- 模型 JSON / blockstate → `mc-model`
- 目录与 `pack.mcmeta` → `mc-resourcepack`

## 常见翻车

| 症状 | 病因 | 修法 |
|---|---|---|
| 糊 | 生成图缩小的,不是真像素画 | 回第 4 步网格法重画 |
| 透明处有黑边 | 混了半透明像素 | 全透明或全不透明 |
| 游戏里偏色 | 非 sRGB / 带色彩配置 | 存标准 8 位 sRGB PNG |
| 紫黑格 | 图集总高不能被帧高整除 | 核 `.mcmeta` 与图集尺寸 |
| 动画不播 | mcmeta 文件名少了 `.png` | 应为 `<name>.png.mcmeta` |
| 像别的游戏 | 色数过多、加了渐变 | 压到 8 色、去渐变 |
| 认不出 | 剪影对称、特征太小 | 改剪影、放大特征 1–2px |
| 方块边缘有缝 | 纹理未做平铺 | 让元素跨边接回对侧 |
| 铜不像铜 | 画成「铜块 + 一块绿」 | 按四级锈蚀选一阶段重画 |

## 边界

- 只管**贴图**。模型形状、UV、几何动画走 `mc-model`。
- 生成式出图只当草稿,商用许可去站点条款确认,确认不了就别用。
- **风格归属由用户拍板**,本文的 MC 惯例只是用户没说时的默认值。