跳到正文

SlashBlade:A Belated Gift

作者:TheFlareStar

SlashBlade 渲染重构与优化——献给每一位 SlashBlade 爱好者!

收藏

介绍

介绍由机器翻译自 CurseForge 原页面,仅供参考。

迟来的礼物 — 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 的其他作品

查看全部 →