Начинающий
- Статус
- Оффлайн
- Регистрация
- 23 Окт 2025
- Сообщения
- 79
- Реакции
- 4
Ты по моему разделом ошибсяНужные миксины:
MixinLevelRenderer.java:package ru.motionreblur.mixin; import net.minecraft.client.util.ObjectAllocator; import net.minecraft.client.render.*; import org.joml.Matrix4f; import org.joml.Vector3f; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import ru.motionreblur.MotionBlurModule; @Mixin(WorldRenderer.class) public class MixinLevelRenderer { @Unique private Matrix4f prevModelView = new Matrix4f(); @Unique private Matrix4f prevProjection = new Matrix4f(); @Unique private Vector3f prevCameraPos = new Vector3f(); @Inject(method = "render", at = @At("HEAD")) private void setMatrices(ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, CallbackInfo ci) { float tickDelta = tickCounter.getTickDelta(true); float fov = ((GameRendererAccessor) gameRenderer).invokeGetFov(camera, tickDelta, true); MotionBlurModule.getInstance().shader.setFrameMotionBlur(positionMatrix, prevModelView, gameRenderer.getBasicProjectionMatrix(fov), prevProjection, new Vector3f( (float) (camera.getPos().x % 30000f), (float) (camera.getPos().y % 30000f), (float) (camera.getPos().z % 30000f) ), prevCameraPos ); } @Inject(method = "render", at = @At("RETURN")) private void setOldMatrices(ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, CallbackInfo ci) { prevModelView = new Matrix4f(positionMatrix); float tickDelta = tickCounter.getTickDelta(true); float fov = ((GameRendererAccessor) gameRenderer).invokeGetFov(camera, tickDelta, true); prevProjection = new Matrix4f(gameRenderer.getBasicProjectionMatrix(fov)); prevCameraPos = new Vector3f( (float) (camera.getPos().x % 30000f), (float) (camera.getPos().y % 30000f), (float) (camera.getPos().z % 30000f) ); } }
GameRendererAccessor.java:package ru.motionreblur.mixin; import net.minecraft.client.render.Camera; import net.minecraft.client.render.GameRenderer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(GameRenderer.class) public interface GameRendererAccessor { @Invoker("getFov") float invokeGetFov(Camera camera, float tickDelta, boolean changingFov); }
Сам блюр:
ShaderMotionBlur.java:package ru.motionreblur; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; import net.minecraft.util.Identifier; import org.joml.Matrix4f; import org.joml.Vector3f; import org.ladysnake.satin.api.managed.ManagedShaderEffect; import org.ladysnake.satin.api.managed.ShaderEffectManager; public class ShaderMotionBlur { private final MotionBlurModule config; private final ManagedShaderEffect motionBlurShader; public ShaderMotionBlur(MotionBlurModule config) { this.config = config; motionBlurShader = ShaderEffectManager.getInstance().manage( Identifier.of(MotionReBlur.MOD_ID, "motion_blur"), shader -> shader.setUniformValue("BlendFactor", config.getStrength()) ); } private long lastNano = System.nanoTime(); private float currentBlur = 0.0f; private float currentFPS = 0.0f; public void registerShaderCallbacks() { WorldRenderEvents.END.register(context -> { long now = System.nanoTime(); float deltaTime = (now - lastNano) / 1_000_000_000.0f; float deltaTick = deltaTime * 20.0f; lastNano = now; if (deltaTime > 0 && deltaTime < 1.0f) { currentFPS = 1.0f / deltaTime; } else { currentFPS = 0.0f; } if (shouldRenderMotionBlur()) { applyMotionBlur(deltaTick); } }); } private boolean shouldRenderMotionBlur() { if (config.getStrength() == 0 || !config.isEnabled()) { return false; } if (FabricLoader.getInstance().isModLoaded("iris")) { MotionReBlur.LOGGER.warn("Motion Blur cannot work with Iris Shaders!"); config.setEnabled(false); return false; } return true; } private void applyMotionBlur(float deltaTick) { MinecraftClient client = MinecraftClient.getInstance(); MonitorInfoProvider.updateDisplayInfo(); int displayRefreshRate = MonitorInfoProvider.getRefreshRate(); float baseStrength = config.getStrength(); float scaledStrength = baseStrength; if (config.isUseRRC()) { float fpsOverRefresh = (displayRefreshRate > 0) ? currentFPS / displayRefreshRate : 1.0f; if (fpsOverRefresh < 1.0f) fpsOverRefresh = 1.0f; scaledStrength = baseStrength * fpsOverRefresh; } if (currentBlur != scaledStrength) { motionBlurShader.setUniformValue("BlendFactor", scaledStrength); currentBlur = scaledStrength; } int sampleAmount = getSampleAmountForFPS(currentFPS); int halfSampleAmount = sampleAmount / 2; float invSamples = 1.0f / sampleAmount; motionBlurShader.setUniformValue("view_res", (float) client.getFramebuffer().viewportWidth, (float) client.getFramebuffer().viewportHeight); motionBlurShader.setUniformValue("view_pixel_size", 1.0f / client.getFramebuffer().viewportWidth, 1.0f / client.getFramebuffer().viewportHeight); motionBlurShader.setUniformValue("motionBlurSamples", sampleAmount); motionBlurShader.setUniformValue("halfSamples", halfSampleAmount); motionBlurShader.setUniformValue("inverseSamples", invSamples); motionBlurShader.setUniformValue("blurAlgorithm", MotionBlurModule.BlurAlgorithm.CENTERED.ordinal()); motionBlurShader.render(deltaTick); } private int getSampleAmountForFPS(float fps) { int quality = config.getQuality(); int baseSamples = switch (quality) { case 0 -> 8; case 1 -> 12; case 2 -> 16; case 3 -> 24; default -> 12; }; if (fps < 30) { return Math.max(6, baseSamples / 2); } else if (fps < 60) { return Math.max(8, (int) (baseSamples * 0.75f)); } else if (fps > 144) { return Math.min(32, (int) (baseSamples * 1.25f)); } return baseSamples; } private final Matrix4f tempPrevModelView = new Matrix4f(); private final Matrix4f tempPrevProjection = new Matrix4f(); private final Matrix4f tempProjInverse = new Matrix4f(); private final Matrix4f tempMvInverse = new Matrix4f(); public void setFrameMotionBlur(Matrix4f modelView, Matrix4f prevModelView, Matrix4f projection, Matrix4f prevProjection, Vector3f cameraPos, Vector3f prevCameraPos) { motionBlurShader.setUniformValue("mvInverse", tempMvInverse.set(modelView).invert()); motionBlurShader.setUniformValue("projInverse", tempProjInverse.set(projection).invert()); motionBlurShader.setUniformValue("prevModelView", tempPrevModelView.set(prevModelView)); motionBlurShader.setUniformValue("prevProjection", tempPrevProjection.set(prevProjection)); motionBlurShader.setUniformValue("cameraPos", cameraPos.x, cameraPos.y, cameraPos.z); motionBlurShader.setUniformValue("prevCameraPos", prevCameraPos.x, prevCameraPos.y, prevCameraPos.z); } public void updateBlurStrength(float strength) { motionBlurShader.setUniformValue("BlendFactor", strength); currentBlur = strength; } }MotionReBlur.java:package ru.motionreblur; import net.fabricmc.api.ClientModInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MotionReBlur implements ClientModInitializer { public static final String MOD_ID = "motionreblur"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); @Override public void onInitializeClient() { LOGGER.info("Motion ReBlur initialized!"); MotionBlurModule.getInstance(); MotionBlurCommand.register(); MotionBlurKeyBinding.register(); } }MotionBlurModule.java:package ru.motionreblur; public class MotionBlurModule { private static final MotionBlurModule instance = new MotionBlurModule(); public final ShaderMotionBlur shader; private boolean enabled = false; private float strength = -0.8f; private boolean useRRC = true; private int quality = 2; private MotionBlurModule() { shader = new ShaderMotionBlur(this); shader.registerShaderCallbacks(); } public static MotionBlurModule getInstance() { return instance; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; MotionReBlur.LOGGER.info("Motion Blur " + (enabled ? "enabled" : "disabled")); } public float getStrength() { return strength; } public void setStrength(float strength) { this.strength = Math.max(-2.0f, Math.min(2.0f, strength)); shader.updateBlurStrength(this.strength); MotionReBlur.LOGGER.info("Motion Blur strength set to " + this.strength); } public boolean isUseRRC() { return useRRC; } public void setUseRRC(boolean useRRC) { this.useRRC = useRRC; MotionReBlur.LOGGER.info("Refresh Rate Scaling " + (useRRC ? "enabled" : "disabled")); } public int getQuality() { return quality; } public void setQuality(int quality) { this.quality = Math.max(0, Math.min(3, quality)); MotionReBlur.LOGGER.info("Motion Blur quality set to " + getQualityName()); } public String getQualityName() { return switch (quality) { case 0 -> "Низкое"; case 1 -> "Среднее"; case 2 -> "Высокое"; case 3 -> "Ультра"; default -> "Среднее"; }; } public enum BlurAlgorithm {BACKWARDS, CENTERED} public static BlurAlgorithm blurAlgorithm = BlurAlgorithm.CENTERED; }MotionBlurKeyBinding.java:package ru.motionreblur; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.util.InputUtil; import org.lwjgl.glfw.GLFW; public class MotionBlurKeyBinding { private static KeyBinding openGuiKey; public static void register() { openGuiKey = KeyBindingHelper.registerKeyBinding(new KeyBinding( "key.motionreblur.open_gui", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_M, "category.motionreblur" )); ClientTickEvents.END_CLIENT_TICK.register(client -> { while (openGuiKey.wasPressed()) { client.setScreen(new MotionBlurConfigScreen(client.currentScreen)); } }); } }MotionBlurConfigScreen.java:package ru.motionreblur; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.text.Text; public class MotionBlurConfigScreen extends Screen { private final Screen parent; private SliderWidget strengthSlider; private ButtonWidget toggleButton; private ButtonWidget rrcButton; private ButtonWidget qualityButton; public MotionBlurConfigScreen(Screen parent) { super(Text.literal("Motion ReBlur Settings")); this.parent = parent; } @Override protected void init() { MotionBlurModule mb = MotionBlurModule.getInstance(); int centerX = this.width / 2; int startY = this.height / 2 - 60; toggleButton = ButtonWidget.builder( Text.literal("Motion Blur: " + (mb.isEnabled() ? "§aВКЛ" : "§cВЫКЛ")), button -> { mb.setEnabled(!mb.isEnabled()); button.setMessage(Text.literal("Motion Blur: " + (mb.isEnabled() ? "§aВКЛ" : "§cВЫКЛ"))); } ).dimensions(centerX - 100, startY, 200, 20).build(); strengthSlider = new SliderWidget( centerX - 100, startY + 30, 200, 20, Text.literal("Сила: " + String.format("%.1f", mb.getStrength())), (mb.getStrength() + 2.0) / 4.0 ) { @Override protected void updateMessage() { double value = this.value * 4.0 - 2.0; this.setMessage(Text.literal("Сила: " + String.format("%.1f", value))); } @Override protected void applyValue() { float value = (float) (this.value * 4.0 - 2.0); mb.setStrength(value); } }; rrcButton = ButtonWidget.builder( Text.literal("Адаптация к частоте монитора: " + (mb.isUseRRC() ? "§aВКЛ" : "§cВЫКЛ")), button -> { mb.setUseRRC(!mb.isUseRRC()); button.setMessage(Text.literal("Адаптация к частоте монитора: " + (mb.isUseRRC() ? "§aВКЛ" : "§cВЫКЛ"))); } ).dimensions(centerX - 100, startY + 60, 200, 20).build(); qualityButton = ButtonWidget.builder( Text.literal("Качество: " + mb.getQualityName()), button -> { int newQuality = (mb.getQuality() + 1) % 4; mb.setQuality(newQuality); button.setMessage(Text.literal("Качество: " + mb.getQualityName())); } ).dimensions(centerX - 100, startY + 90, 200, 20).build(); ButtonWidget doneButton = ButtonWidget.builder( Text.translatable("gui.done"), button -> this.close() ).dimensions(centerX - 100, startY + 120, 200, 20).build(); addDrawableChild(toggleButton); addDrawableChild(strengthSlider); addDrawableChild(rrcButton); addDrawableChild(qualityButton); addDrawableChild(doneButton); } @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { this.renderBackground(context, mouseX, mouseY, delta); super.render(context, mouseX, mouseY, delta); context.drawCenteredTextWithShadow( this.textRenderer, this.title, this.width / 2, 20, 0xFFFFFF ); MotionBlurModule mb = MotionBlurModule.getInstance(); int refreshRate = MonitorInfoProvider.getRefreshRate(); String info = "Refresh Rate: " + refreshRate + " Hz"; context.drawCenteredTextWithShadow( this.textRenderer, Text.literal(info), this.width / 2, this.height / 2 + 80, 0x808080 ); if (mb.isEnabled()) { String hint = "Двигайте камеру, чтобы увидеть эффект"; context.drawCenteredTextWithShadow( this.textRenderer, Text.literal(hint), this.width / 2, this.height / 2 + 95, 0x808080 ); } } @Override public void close() { if (this.client != null) { this.client.setScreen(parent); } } }MotionBlurCommand.java:package ru.motionreblur; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.FloatArgumentType; import com.mojang.brigadier.context.CommandContext; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; public class MotionBlurCommand { public static void register() { ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { registerCommand(dispatcher); }); } private static void registerCommand(CommandDispatcher<FabricClientCommandSource> dispatcher) { dispatcher.register(ClientCommandManager.literal("motionreblur") .executes(ctx -> openGui(ctx)) .then(ClientCommandManager.literal("on") .executes(ctx -> enable(ctx))) .then(ClientCommandManager.literal("off") .executes(ctx -> disable(ctx))) .then(ClientCommandManager.literal("toggle") .executes(ctx -> toggle(ctx))) .then(ClientCommandManager.literal("strength") .then(ClientCommandManager.argument("value", FloatArgumentType.floatArg(-2.0f, 2.0f)) .executes(ctx -> setStrength(ctx, FloatArgumentType.getFloat(ctx, "value"))))) .then(ClientCommandManager.literal("rrc") .executes(ctx -> toggleRRC(ctx))) .then(ClientCommandManager.literal("status") .executes(ctx -> showStatus(ctx))) .then(ClientCommandManager.literal("help") .executes(ctx -> showHelp(ctx))) ); } private static int openGui(CommandContext<FabricClientCommandSource> ctx) { MinecraftClient client = MinecraftClient.getInstance(); client.execute(() -> { client.setScreen(new MotionBlurConfigScreen(null)); }); ctx.getSource().sendFeedback(Text.literal("§aОткрываю меню настроек...")); return 1; } private static int showStatus(CommandContext<FabricClientCommandSource> ctx) { MotionBlurModule mb = MotionBlurModule.getInstance(); ctx.getSource().sendFeedback(Text.literal("§7§m ")); ctx.getSource().sendFeedback(Text.literal("§6Motion ReBlur")); ctx.getSource().sendFeedback(Text.literal("§7Состояние: " + (mb.isEnabled() ? "§aВКЛ" : "§cВЫКЛ"))); ctx.getSource().sendFeedback(Text.literal("§7Сила: §f" + String.format("%.1f", mb.getStrength()))); ctx.getSource().sendFeedback(Text.literal("§7RRC: " + (mb.isUseRRC() ? "§aВКЛ" : "§cВЫКЛ"))); ctx.getSource().sendFeedback(Text.literal("§7Refresh Rate: §f" + MonitorInfoProvider.getRefreshRate() + " Hz")); ctx.getSource().sendFeedback(Text.literal("§7§m ")); return 1; } private static int enable(CommandContext<FabricClientCommandSource> ctx) { MotionBlurModule.getInstance().setEnabled(true); ctx.getSource().sendFeedback(Text.literal("§aMotion Blur включен")); return 1; } private static int disable(CommandContext<FabricClientCommandSource> ctx) { MotionBlurModule.getInstance().setEnabled(false); ctx.getSource().sendFeedback(Text.literal("§cMotion Blur выключен")); return 1; } private static int toggle(CommandContext<FabricClientCommandSource> ctx) { MotionBlurModule mb = MotionBlurModule.getInstance(); boolean newState = !mb.isEnabled(); mb.setEnabled(newState); ctx.getSource().sendFeedback(Text.literal("§7Motion Blur: " + (newState ? "§aВКЛ" : "§cВЫКЛ"))); return 1; } private static int setStrength(CommandContext<FabricClientCommandSource> ctx, float value) { MotionBlurModule.getInstance().setStrength(value); ctx.getSource().sendFeedback(Text.literal("§aСила установлена на §f" + String.format("%.1f", value))); return 1; } private static int toggleRRC(CommandContext<FabricClientCommandSource> ctx) { MotionBlurModule mb = MotionBlurModule.getInstance(); boolean newState = !mb.isUseRRC(); mb.setUseRRC(newState); ctx.getSource().sendFeedback(Text.literal("§7Адаптация к частоте монитора: " + (newState ? "§aВКЛ" : "§cВЫКЛ"))); return 1; } private static int showHelp(CommandContext<FabricClientCommandSource> ctx) { ctx.getSource().sendFeedback(Text.literal("§7§m ")); ctx.getSource().sendFeedback(Text.literal("§6Motion ReBlur - Команды")); ctx.getSource().sendFeedback(Text.literal("§7§m ")); ctx.getSource().sendFeedback(Text.literal("§e/motionreblur §7- открыть GUI")); ctx.getSource().sendFeedback(Text.literal("§e/motionreblur on §7- включить")); ctx.getSource().sendFeedback(Text.literal("§e/motionreblur off §7- выключить")); ctx.getSource().sendFeedback(Text.literal("§e/motionreblur toggle §7- переключить")); ctx.getSource().sendFeedback(Text.literal("§e/motionreblur strength <значение> §7- установить силу")); ctx.getSource().sendFeedback(Text.literal("§7 Диапазон: от -2.0 до 2.0")); ctx.getSource().sendFeedback(Text.literal("§e/motionreblur rrc §7- переключить адаптацию к частоте")); ctx.getSource().sendFeedback(Text.literal("§e/motionreblur status §7- показать статус")); ctx.getSource().sendFeedback(Text.literal("§7§m ")); return 1; } }MonitorInfoProvider.java:package ru.motionreblur; import net.minecraft.client.MinecraftClient; import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWVidMode; public class MonitorInfoProvider { private static long lastMonitorHandle = 0; private static int lastRefreshRate = 60; private static long lastCheckTime = 0; private static final long CHECK_INTERVAL_NS = 1_000_000_000L; public static void updateDisplayInfo() { long now = System.nanoTime(); if (now - lastCheckTime < CHECK_INTERVAL_NS) { return; } lastCheckTime = now; MinecraftClient client = MinecraftClient.getInstance(); if (client == null || client.getWindow() == null) return; long window = client.getWindow().getHandle(); long monitor = GLFW.glfwGetWindowMonitor(window); if (monitor == 0) { monitor = getMonitorFromWindowPosition(window, client.getWindow().getWidth(), client.getWindow().getHeight()); } if (monitor != lastMonitorHandle) { lastRefreshRate = detectRefreshRateFromMonitor(monitor); lastMonitorHandle = monitor; } } public static int getRefreshRate() { return lastRefreshRate; } private static long getMonitorFromWindowPosition(long window, int windowWidth, int windowHeight) { int[] winX = new int[1]; int[] winY = new int[1]; GLFW.glfwGetWindowPos(window, winX, winY); int windowCenterX = winX[0] + windowWidth / 2; int windowCenterY = winY[0] + windowHeight / 2; long monitorResult = GLFW.glfwGetPrimaryMonitor(); PointerBuffer monitors = GLFW.glfwGetMonitors(); if (monitors != null) { for (int i = 0; i < monitors.limit(); i++) { long m = monitors.get(i); int[] mx = new int[1]; int[] my = new int[1]; GLFW.glfwGetMonitorPos(m, mx, my); GLFWVidMode mode = GLFW.glfwGetVideoMode(m); if (mode == null) continue; int mw = mode.width(); int mh = mode.height(); if (windowCenterX >= mx[0] && windowCenterX < mx[0] + mw && windowCenterY >= my[0] && windowCenterY < my[0] + mh) { monitorResult = m; break; } } } return monitorResult; } private static int detectRefreshRateFromMonitor(long monitor) { GLFWVidMode vidMode = GLFW.glfwGetVideoMode(monitor); return (vidMode != null) ? vidMode.refreshRate() : 60; } }