介绍
迟来的礼物 — SlashBlade 渲染优化模组
📖 这是什么?
这是一个面向 SlashBlade: Resharped 的客户端渲染优化模组,旨在消除手持拔刀剑、挥砍、渲染掉落拔刀剑、使用光影或加载 YSM 模型时出现的严重帧率下降。
一句话总结:手持帧率翻倍,挥砍帧率三倍,掉落的高面数拔刀剑提升 30 倍。
📊 性能对比
模组版本:1.0.2
测试配置:AMD Ryzen 7 9700X + NVIDIA RTX 5070 Ti
第三人称视角,BSL 光影,拔刀剑:炼金王国
无光影
| 场景 | 原版 | 加速渲染 | 迟来的礼物 | 提升(对比原版) |
|---|---|---|---|---|
| 手持拔刀剑 | 359 | 412 | 810 | +126% |
| 挥砍 | 220 | 248 | 620 | +182% |
| 17 把高面数掉落拔刀剑 | 61 | 425 | 730 | +1097% |
有光影(BSL)
| 场景 | 原版 | 加速渲染 | 迟来的礼物 | 提升(对比原版) |
|---|---|---|---|---|
| 手持拔刀剑 | 208 | 330 | 381 | +83% |
| 挥砍 | 115 | 153 | 365 | +217% |
| 17 把高面数掉落拔刀剑 | 29 | 240 | 258 | +1134% |
YSM 兼容性
| 场景 | 原版 | 加速渲染 | 迟来的礼物 | 提升(对比原版) |
|---|---|---|---|---|
| 无光影 · 手持 | 374 | 398 | 724 | +94% |
| 无光影 · 挥砍 | 208 | 362 | 605 | +191% |
| 有光影 · 手持 | 215 | 301 | 374 | +74% |
| 有光影 · 挥砍 | 172 | 261 | 345 | +101% |
⚙️ 它是如何工作的?
不再“每帧重绘”
原版渲染会在每一帧遍历 OBJ 模型的每个组、面和顶点。本模组在加载时将数据“烘焙”为 GPU 友好的格式,之后每帧只需复用即可。
静态 VBO 缓存
刀身、刀鞘、物品图标、刀架上的拔刀剑以及掉落的拔刀剑都共享一个 96 MiB 的 LRU 缓存。同一把拔刀剑第二次出现时,会复用已上传至显存的数据,而不是重新提交顶点。
附魔光效也有专属的静态缓存,因此不再拖累性能。
光影下的“智能节省”
- 在阴影渲染阶段使用简化的 32 段代理网格,而非完整的高面数模型
- 透明特效(SlashEffects、Judgement Cut 等)完全跳过阴影贴图阶段
- 发光的刀身不会重复投射阴影
挥砍动画 — “只做必要的”
原版会为每次挥砍计算完整的 PMD 顶点蒙皮,即使实际只用到两个骨骼锚点。本模组会跳过无用的顶点计算,只保留骨骼姿态。
刀光效果 LOD
近距离完整细节,中距离减少层数,远距离只保留核心主体——在光影下还会进一步简化。
无首次使用卡顿
模型、纹理和 RenderType 会在你进入游戏前预先预热。挥砍刀光或首次打开物品栏时不会突然卡顿。
自动缓存清理
退出世界或重载资源时会自动清除缓存——性能长期保持稳定。
兼容性与安全回退
保留 RenderOverrideEvent。如果检测到动态 UV、动态透明度或无法缓存的网格,会自动回退到原版渲染——不会崩溃。
⚠️ 不兼容
与 [Accelerated Render] 不兼容——两个模组功能重叠。同时安装可能导致渲染故障或性能下降。
刀光效果渲染 API
此 API 面向需要替换刀光效果模型或纹理的 SlashBlade 附属模组。附属模组只负责选择资源,而 A Belated Gift 继续处理基于距离的 LOD、光影兼容、网格优化和资源预热。
采用此 API 后,附属模组不得使用 Mixin 取消 SlashEffectRenderer.render。取消该方法会导致无法访问注册表。
仅替换刀光纹理
在排队的 FMLClientSetupEvent 任务中注册规则:
import cn.star.a_belated_gift.api.client.SlashEffectRenderRegistry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
private static final ResourceLocation RULE_ID =
new ResourceLocation("example", "special_blade_slash");
private static final ResourceLocation SLASH_TEXTURE =
new ResourceLocation("example", "model/util/special_slash.png");
private static void onClientSetup(FMLClientSetupEvent event) {
event.enqueueWork(() -> SlashEffectRenderRegistry.registerTexture(
RULE_ID,
100,
effect -> effect.getOwner() instanceof Player player
&& player.getMainHandItem().is(EXAMPLE_BLADE.get()),
SLASH_TEXTURE
));
}
registerTexture 会继续使用 SlashBlade 默认的 model/util/slash.obj 模型。
同时替换 OBJ 和纹理
import cn.star.a_belated_gift.api.client.SlashEffectRenderDefinition;
import cn.star.a_belated_gift.api.client.SlashEffectRenderRegistry;
private static final SlashEffectRenderDefinition SPECIAL_SLASH =
new SlashEffectRenderDefinition(
new ResourceLocation("example", "model/util/special_slash.obj"),
new ResourceLocation("example", "model/util/special_slash.png")
);
private static void onClientSetup(FMLClientSetupEvent event) {
event.enqueueWork(() -> SlashEffectRenderRegistry.register(
new ResourceLocation("example", "special_blade_slash"),
100,
effect -> effect.getOwner() instanceof Player player
&& player.getMainHandItem().is(EXAMPLE_BLADE.get()),
SPECIAL_SLASH
));
}
此 register 重载会自动将定义中的 OBJ 和纹理加入预热列表。
动态提供器
当需要根据效果状态动态选择定义时,请实现 SlashEffectRenderProvider:
import cn.star.a_belated_gift.api.client.SlashEffectRenderDefinition;
import cn.star.a_belated_gift.api.client.SlashEffectRenderProvider;
import mods.flammpfeil.slashblade.entity.EntitySlashEffect;
import java.util.Collection;
import java.util.List;
public final class ExampleSlashProvider implements SlashEffectRenderProvider {
private static final SlashEffectRenderDefinition BLUE =
SlashEffectRenderDefinition.withTexture(
new ResourceLocation("example", "model/util/blue_slash.png"));
private static final SlashEffectRenderDefinition RED =
SlashEffectRenderDefinition.withTexture(
new ResourceLocation("example", "model/util/red_slash.png"));
@Override
public SlashEffectRenderDefinition resolve(EntitySlashEffect effect) {
if (!(effect.getOwner() instanceof Player player)
|| !player.getMainHandItem().is(EXAMPLE_BLADE.get())) {
return null;
}
return effect.getIsCritical() ? RED : BLUE;
}
@Override
public Collection<SlashEffectRenderDefinition> warmupDefinitions() {
return List.of(BLUE, RED);
}
}
event.enqueueWork(() -> SlashEffectRenderRegistry.register(
new ResourceLocation("example", "dynamic_slash"),
200,
new ExampleSlashProvider()
));
从 resolve 返回 null 表示当前规则不匹配。注册表随后会检查下一个提供器。所有可能返回的资源都应通过 warmupDefinitions 声明。
匹配顺序
- 数值优先级较高的规则会优先评估。
- 优先级相同的规则按注册 ID 排序,因此结果不受模组加载顺序影响。
- 第一个返回非
null定义的提供器将胜出。 - 如果没有规则匹配,则使用 SlashBlade 的默认模型和材质。
- 再次注册相同 ID 会替换先前的规则。
- 提供器发生异常时会被隔离并记录日志,其余规则仍会继续匹配。
取消注册规则
boolean removed = SlashEffectRenderRegistry.unregister(
new ResourceLocation("example", "special_blade_slash")
);
取消注册只会影响后续的斩击特效选择。已经预热的资源由 Minecraft 的资源生命周期管理。
常见问题
SlashBlade:A Belated Gift 支持哪些 Minecraft 版本?
SlashBlade:A Belated Gift 支持 Minecraft Java 版 1.20.1–1.21.1,共 2 个版本,可用的加载器:Forge / NeoForge。下载时注意选择和你游戏版本一致的文件。
SlashBlade:A Belated Gift 怎么安装?
先用 PCL2 或 HMCL 启动器安装对应版本的 Forge / NeoForge,再把下载的 .jar 文件放进游戏目录的 mods 文件夹,重新启动游戏即可。开启了版本隔离的,mods 文件夹在 versions/版本名 下面。
TheFlareStar 的其他作品
查看全部 →Optimization of Campfire Smoke
优化原版营火的烟雾粒子效果
Citadel Fix
这个模组修复了 Citadel 启动时拉取赞助商名单导致的卡顿,并优化了启动速度。
Toms Storage Star Optimized
Tom的存储的轻量优化模组。
Shipwreck Fix
这个模组修复了1.19.2/1.20.1中沉船群组(MC-249136)的漏洞,避免其长时间卡顿。

评论
登录后评论和评分