From 54ac82cc9afced93bda4630eaf4085a42c673e21 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 02:12:08 +0800 Subject: [PATCH 01/17] =?UTF-8?q?feat(render):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=BB=91=E6=B4=9E=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E5=90=8E?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 mixin 配置中注册 PostChainAccessor 访问器 - 添加客户端配置选项控制引力透镜效果开关和参数 - 在 BlackHoleBlockEntity 中注册/注销客户端引力透镜管理器 - 实现 GameRendererMixin 加载透镜着色器效果 - 在 LevelRendererMixin 中注入引力透镜后期处理逻辑 - 添加 ModShaders 管理引力透镜着色器链 - 实现 RenderState 检查透镜效果启用状态 - 添加 gravitational_lens.fsh 片段着色器实现引力透镜算法 - 创建 gravitational_lens.json 后处理配置文件 - 实现 GravitationalLensManager 管理黑洞屏幕投影计算 - 创建 PostChainAccessor Mixin 接口访问后处理通道 --- .../assets/anvilcraft/lang/en_ud.json | 6 + .../assets/anvilcraft/lang/en_us.json | 6 + .../block/entity/BlackHoleBlockEntity.java | 21 ++- .../anvilcraft/client/init/ModShaders.java | 23 +++ .../client/renderer/RenderState.java | 4 + .../support/GravitationalLensManager.java | 131 ++++++++++++++++++ .../config/AnvilCraftClientConfig.java | 9 ++ .../anvilcraft/mixin/GameRendererMixin.java | 1 + .../anvilcraft/mixin/LevelRendererMixin.java | 87 ++++++++++++ .../anvilcraft/mixin/PostChainAccessor.java | 14 ++ src/main/resources/anvilcraft.mixins.json | 1 + .../shaders/post/gravitational_lens.json | 13 ++ .../shaders/program/gravitational_lens.fsh | 68 +++++++++ .../shaders/program/gravitational_lens.json | 108 +++++++++++++++ 14 files changed, 486 insertions(+), 6 deletions(-) create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java create mode 100644 src/main/java/dev/dubhe/anvilcraft/mixin/PostChainAccessor.java create mode 100644 src/main/resources/assets/anvilcraft/shaders/post/gravitational_lens.json create mode 100644 src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh create mode 100644 src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 72ccaf05d1..b47ca4edb9 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -129,6 +129,8 @@ "anvilcraft.configuration.do_not_show_tooltip_when_jade_present.tooltip": "ʇuǝsǝɹd ǝpɐɾ uǝɥʍ dᴉʇꞁooʇ ʇuǝuodɯoɔ ɹǝʍod ɹǝpuǝɹ ʇou oᗡ", "anvilcraft.configuration.ember_anvil_beyond_max_level": "ꞁǝʌǝꞀ xɐW puoʎǝᗺ ꞁᴉʌuⱯ ɹǝqɯƎ", "anvilcraft.configuration.ember_anvil_beyond_max_level.tooltip": "ꞁᴉʌuⱯ ɹǝqɯƎ uᴉ ꞁǝʌǝꞁ xɐɯ puoʎǝq sʞooᗺ pǝʇuɐɥɔuƎ ɥʇᴉʍ sɯǝʇᴉ ᵷuᴉuᴉqɯoƆ", + "anvilcraft.configuration.event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ", + "anvilcraft.configuration.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'ʞsᴉp ʞɔɐꞁq ɹǝᵷᵷᴉq = ɹǝɥᵷᴉɥ) ǝɔɐds Ʌ∩ uǝǝɹɔs uᴉ snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", "anvilcraft.configuration.felling_block_per_level": "ꞁǝʌǝꞀ ɹǝԀ ʞɔoꞁᗺ ᵷuᴉꞁꞁǝℲ", "anvilcraft.configuration.felling_block_per_level.tooltip": "ʇuǝɯʇuɐɥɔuǝ ᵷuᴉꞁꞁǝℲ ɟo ꞁǝʌǝꞁ ɹǝd ʇnɔ ǝq uɐɔ ʇɐɥʇ sᵷoꞁ ɟo ɹǝqɯnu ɯnɯᴉxɐɯ ǝɥ⟘", "anvilcraft.configuration.frost_anvil_beyond_max_level": "ꞁǝʌǝꞀ xɐW puoʎǝᗺ ꞁᴉʌuⱯ ʇsoɹℲ", @@ -178,6 +180,8 @@ "anvilcraft.configuration.iono_craft_backpack_max_flight_time.tooltip": "sʞɔᴉʇ uᴉ ǝɯᴉ⟘ ʇɥᵷᴉꞁℲ xɐW ʞɔɐdʞɔɐᗺ ʇɟɐɹƆ ouoI", "anvilcraft.configuration.is_laser_do_impact_checking": "ᵷuᴉʞɔǝɥƆ ʇɔɐdɯI oᗡ ɹǝsɐꞀ sI", "anvilcraft.configuration.is_laser_do_impact_checking.tooltip": "ᵷuᴉʞɔǝɥɔ ʇɔɐdɯᴉ op ɹǝsɐꞁ sI", + "anvilcraft.configuration.lens_strength": "ɥʇᵷuǝɹʇS suǝꞀ", + "anvilcraft.configuration.lens_strength.tooltip": "(ʇꞁnɐɟǝp ᘔ00˙0 'ᵷuᴉpuǝq ɹǝᵷuoɹʇs = ɹǝɥᵷᴉɥ) ɥʇᵷuǝɹʇs uoᴉʇɹoʇsᴉp suǝꞀ", "anvilcraft.configuration.lightning_strike_depth": "ɥʇdǝᗡ ǝʞᴉɹʇS ᵷuᴉuʇɥᵷᴉꞀ", "anvilcraft.configuration.lightning_strike_depth.tooltip": "ɥɔɐǝɹ uɐɔ ǝʞᴉɹʇs ᵷuᴉuʇɥᵷᴉꞁ ɐ ɥʇdǝp ɯnɯᴉxɐW", "anvilcraft.configuration.lightning_strike_radius": "snᴉpɐᴚ ǝʞᴉɹʇS ᵷuᴉuʇɥᵷᴉꞀ", @@ -209,6 +213,8 @@ "anvilcraft.configuration.redstone_emp_radius.tooltip": "ꞁᴉʌuɐ ǝɥʇ ʎq pǝddoɹp ʞɔoꞁq ɹǝd pǝʇɐɹǝuǝᵷ ɥʇᵷuǝꞁ ԀWƎ ǝuoʇspǝᴚ", "anvilcraft.configuration.remote_power_transmitter_range": "ɹǝʇʇᴉɯsuɐɹ⟘ ɹǝʍoԀ ǝʇoɯǝᴚ ɟo ǝᵷuɐᴚ", "anvilcraft.configuration.remote_power_transmitter_range.tooltip": "ɹǝʇʇᴉɯsuɐɹʇ ɹǝʍod ǝʇoɯǝɹ ɟo ǝᵷuɐɹ pᴉɹᵷ ɹǝʍoԀ", + "anvilcraft.configuration.render_black_hole_lensing": "ᵷuᴉsuǝꞀ ǝꞁoH ʞɔɐꞁᗺ ɹǝpuǝᴚ", + "anvilcraft.configuration.render_black_hole_lensing.tooltip": "sǝꞁoɥ ʞɔɐꞁq ɹɐǝu ʇɔǝɟɟǝ ᵷuᴉssǝɔoɹd-ʇsod ᵷuᴉsuǝꞁ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", "anvilcraft.configuration.render_bloom_effect": "ʇɔǝɟɟƎ ɯooꞁᗺ ɹǝpuǝᴚ", "anvilcraft.configuration.render_bloom_effect.tooltip": "˙sǝuᴉꞁ ɹǝʇʇᴉɯsuɐɹʇ ɹǝʍod puɐ ɹǝsɐꞁ uo ʇɔǝɟɟǝ ɯooꞁᗺ", "anvilcraft.configuration.render_power_transmitter_lines": "sǝuᴉꞀ ɹǝʇʇᴉɯsuɐɹ⟘ ɹǝʍoԀ ɹǝpuǝᴚ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 662897170b..d9de9da8f4 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -129,6 +129,8 @@ "anvilcraft.configuration.do_not_show_tooltip_when_jade_present.tooltip": "Do not render power component tooltip when jade present", "anvilcraft.configuration.ember_anvil_beyond_max_level": "Ember Anvil Beyond Max Level", "anvilcraft.configuration.ember_anvil_beyond_max_level.tooltip": "Combining items with Enchanted Books beyond max level in Ember Anvil", + "anvilcraft.configuration.event_horizon_radius": "Event Horizon Radius", + "anvilcraft.configuration.event_horizon_radius.tooltip": "Event horizon radius in screen UV space (higher = bigger black disk, 0.083 default)", "anvilcraft.configuration.felling_block_per_level": "Felling Block Per Level", "anvilcraft.configuration.felling_block_per_level.tooltip": "The maximum number of logs that can be cut per level of Felling enchantment", "anvilcraft.configuration.frost_anvil_beyond_max_level": "Frost Anvil Beyond Max Level", @@ -178,6 +180,8 @@ "anvilcraft.configuration.iono_craft_backpack_max_flight_time.tooltip": "Iono Craft Backpack Max Flight Time in ticks", "anvilcraft.configuration.is_laser_do_impact_checking": "Is Laser Do Impact Checking", "anvilcraft.configuration.is_laser_do_impact_checking.tooltip": "Is laser do impact checking", + "anvilcraft.configuration.lens_strength": "Lens Strength", + "anvilcraft.configuration.lens_strength.tooltip": "Lens distortion strength (higher = stronger bending, 0.002 default)", "anvilcraft.configuration.lightning_strike_depth": "Lightning Strike Depth", "anvilcraft.configuration.lightning_strike_depth.tooltip": "Maximum depth a lightning strike can reach", "anvilcraft.configuration.lightning_strike_radius": "Lightning Strike Radius", @@ -209,6 +213,8 @@ "anvilcraft.configuration.redstone_emp_radius.tooltip": "Redstone EMP length generated per block dropped by the anvil", "anvilcraft.configuration.remote_power_transmitter_range": "Range of Remote Power Transmitter", "anvilcraft.configuration.remote_power_transmitter_range.tooltip": "Power grid range of remote power transmitter", + "anvilcraft.configuration.render_black_hole_lensing": "Render Black Hole Lensing", + "anvilcraft.configuration.render_black_hole_lensing.tooltip": "Gravitational lensing post-processing effect near black holes", "anvilcraft.configuration.render_bloom_effect": "Render Bloom Effect", "anvilcraft.configuration.render_bloom_effect.tooltip": "Bloom effect on laser and power transmitter lines.", "anvilcraft.configuration.render_power_transmitter_lines": "Render Power Transmitter Lines", diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java index 25c4a8e7a4..faa994be1d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java @@ -1,5 +1,6 @@ package dev.dubhe.anvilcraft.block.entity; +import dev.dubhe.anvilcraft.client.support.GravitationalLensManager; import dev.dubhe.anvilcraft.init.block.ModBlockEntities; import dev.dubhe.anvilcraft.util.GravityManager; import net.minecraft.core.BlockPos; @@ -19,10 +20,14 @@ public static BlackHoleBlockEntity createBlockEntity(BlockEntityType type, Bl @Override public void onLoad() { super.onLoad(); - if (this.level != null && !this.level.isClientSide) { - GravityManager.GravitySourceType type = GravityManager.GravitySourceManager.getType(this.getBlockState().getBlock()); - if (type != null) { - GravityManager.GravitySourceManager.addSource(this.level, this.worldPosition, type); + if (this.level != null) { + if (this.level.isClientSide) { + GravitationalLensManager.register(this.worldPosition); + } else { + GravityManager.GravitySourceType type = GravityManager.GravitySourceManager.getType(this.getBlockState().getBlock()); + if (type != null) { + GravityManager.GravitySourceManager.addSource(this.level, this.worldPosition, type); + } } } } @@ -30,8 +35,12 @@ public void onLoad() { @Override public void setRemoved() { super.setRemoved(); - if (this.level != null && !this.level.isClientSide) { - GravityManager.GravitySourceManager.removeSource(this.level, this.worldPosition); + if (this.level != null) { + if (this.level.isClientSide) { + GravitationalLensManager.unregister(this.worldPosition); + } else { + GravityManager.GravitySourceManager.removeSource(this.level, this.worldPosition); + } } } } \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java b/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java index e4b34ea7f2..4fe4ae0c83 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java @@ -15,9 +15,12 @@ public class ModShaders { public static final ResourceLocation LASER_BLOOM_LOCATION = AnvilCraft.of("shaders/post/bloom.json"); + public static final ResourceLocation GRAVITATIONAL_LENS_LOCATION = AnvilCraft.of("shaders/post/gravitational_lens.json"); @Getter private static PostChain bloomChain; + @Getter + private static PostChain lensChain; static final Minecraft MINECRAFT = Minecraft.getInstance(); @Getter @@ -102,6 +105,9 @@ public static void resize(int width, int height) { if (bloomChain != null) { bloomChain.resize(width, height); } + if (lensChain != null) { + lensChain.resize(width, height); + } orthoMatrix = new Matrix4f() .setOrtho( 0f, @@ -132,4 +138,21 @@ public static void loadBloomEffect(ResourceProvider resourceProvider) throws IOE AnvilCraft.LOGGER.error("Could not load bloom effect shader.", tr); } } + + public static void loadLensEffect(ResourceProvider resourceProvider) throws IOException { + try { + lensChain = new PostChain( + MINECRAFT.getTextureManager(), + resourceProvider, + Minecraft.getInstance().getMainRenderTarget(), + GRAVITATIONAL_LENS_LOCATION + ); + lensChain.resize( + Minecraft.getInstance().getWindow().getWidth(), + Minecraft.getInstance().getWindow().getHeight() + ); + } catch (Throwable tr) { + AnvilCraft.LOGGER.error("Could not load gravitational lens effect shader.", tr); + } + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/RenderState.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/RenderState.java index 92aa4094df..dad4ffbb57 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/RenderState.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/RenderState.java @@ -38,4 +38,8 @@ public static boolean isBloomEffectEnabled() { public static boolean isScanPreviewEffectEnabled() { return AnvilCraftClient.CONFIG.renderScanPreviewEffect; } + + public static boolean isLensEffectEnabled() { + return isEnhancedRenderingAvailable() && AnvilCraftClient.CONFIG.renderBlackHoleLensing; + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java new file mode 100644 index 0000000000..aa38fec624 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -0,0 +1,131 @@ +package dev.dubhe.anvilcraft.client.support; + +import net.minecraft.client.Camera; +import net.minecraft.core.BlockPos; +import org.joml.Matrix4f; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import org.joml.Vector4f; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class GravitationalLensManager { + private static final int MAX_BLACK_HOLES = 4; + private static final int MAX_SEARCH_DISTANCE_SQR = 256 * 256; + + /** Client-side cache of loaded black hole block positions. */ + public static final Set CLIENT_BLACK_HOLE_POSITIONS = + Collections.newSetFromMap(new ConcurrentHashMap<>()); + + public static void register(BlockPos pos) { + CLIENT_BLACK_HOLE_POSITIONS.add(pos.immutable()); + } + + public static void unregister(BlockPos pos) { + CLIENT_BLACK_HOLE_POSITIONS.remove(pos); + } + + /** + * Holds the screen-space projection result for a single black hole. + */ + public record ScreenProjection(float screenU, float screenV, boolean onScreen) {} + + /** + * Project a world position to screen UV coordinates. + * + * @param worldPos the block position in world space + * @param camera the current camera + * @param projectionMatrix the projection matrix from renderLevel + * @return ScreenProjection with UV coordinates and on-screen flag + */ + public static ScreenProjection projectToScreen( + BlockPos worldPos, + Camera camera, + Matrix4f projectionMatrix + ) { + float yaw = camera.getYRot(); + float pitch = camera.getXRot(); + + // Build view matrix from camera rotation and position. + // Minecraft yaw: 0 = south (+Z), 90 = west (-X), 180 = north (-Z), 270 = east (+X). + // Minecraft pitch: positive = looking down. + // We invert both rotations to go from world → camera space. + Quaternionf cameraRotation = new Quaternionf() + .rotateX((float) Math.toRadians(pitch)) + .rotateY((float) Math.toRadians(yaw + 180.0f)); + + Vector3f cameraPos = camera.getPosition().toVector3f(); + + Matrix4f viewMatrix = new Matrix4f() + .rotate(cameraRotation) + .translate(-cameraPos.x, -cameraPos.y, -cameraPos.z); + + Matrix4f viewProj = new Matrix4f(projectionMatrix).mul(viewMatrix); + + // Center of block + Vector4f clip = viewProj.transform( + new Vector4f( + worldPos.getX() + 0.5f, + worldPos.getY() + 0.5f, + worldPos.getZ() + 0.5f, + 1.0f + ) + ); + + if (clip.w <= 0.0f) { + return new ScreenProjection(0, 0, false); + } + + float ndcX = clip.x / clip.w; + float ndcY = clip.y / clip.w; + float ndcZ = clip.z / clip.w; + + // Check if within clip space bounds + if (ndcZ < -1.0f || ndcZ > 1.0f) { + return new ScreenProjection(0, 0, false); + } + if (ndcX < -1.0f || ndcX > 1.0f || ndcY < -1.0f || ndcY > 1.0f) { + return new ScreenProjection(0, 0, false); + } + + float screenU = (ndcX + 1.0f) / 2.0f; + float screenV = (ndcY + 1.0f) / 2.0f; + + return new ScreenProjection(screenU, screenV, true); + } + + /** + * Collect up to {@code maxCount} on-screen black hole projections, + * sorted by distance to camera (nearest first). + */ + public static List collectVisibleBlackHoles( + Camera camera, + Matrix4f projectionMatrix, + int maxCount + ) { + List result = new ArrayList<>(); + if (CLIENT_BLACK_HOLE_POSITIONS.isEmpty()) return result; + + Vector3f cameraPos = camera.getPosition().toVector3f(); + + for (BlockPos pos : CLIENT_BLACK_HOLE_POSITIONS) { + // Quick distance cull + double dx = pos.getX() + 0.5 - cameraPos.x; + double dy = pos.getY() + 0.5 - cameraPos.y; + double dz = pos.getZ() + 0.5 - cameraPos.z; + double distSqr = dx * dx + dy * dy + dz * dz; + if (distSqr > MAX_SEARCH_DISTANCE_SQR) continue; + + ScreenProjection proj = projectToScreen(pos, camera, projectionMatrix); + if (proj.onScreen) { + result.add(proj); + if (result.size() >= maxCount) break; + } + } + return result; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java index e25577e67e..a227c5c931 100644 --- a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java +++ b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java @@ -45,6 +45,15 @@ public class AnvilCraftClientConfig { @Comment("Scanline post-processing effect on 3D structure previews.") public boolean renderScanPreviewEffect = true; + @Comment("Gravitational lensing post-processing effect near black holes") + public boolean renderBlackHoleLensing = true; + + @Comment("Lens distortion strength (higher = stronger bending, 0.002 default)") + public double lensStrength = 1.0 / 512.0; + + @Comment("Event horizon radius in screen UV space (higher = bigger black disk, 0.083 default)") + public double eventHorizonRadius = 1.0 / 12.0; + @Comment("A vertical item frame vertically displays items") public boolean verticalItemFrame = false; diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java index c0e5049551..7992722e1a 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java @@ -24,6 +24,7 @@ abstract class GameRendererMixin { ) void loadBloomEffect(ResourceProvider resourceProvider, CallbackInfo ci) throws IOException { ModShaders.loadBloomEffect(resourceProvider); + ModShaders.loadLensEffect(resourceProvider); } @Inject( diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 47110f377c..f82fbc383d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -12,9 +12,11 @@ import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexFormat; import dev.dubhe.anvilcraft.api.rendering.CacheableBERenderingPipeline; +import dev.dubhe.anvilcraft.client.AnvilCraftClient; import dev.dubhe.anvilcraft.client.init.ModRenderTargets; import dev.dubhe.anvilcraft.client.init.ModShaders; import dev.dubhe.anvilcraft.client.renderer.RenderState; +import dev.dubhe.anvilcraft.client.support.GravitationalLensManager; import dev.dubhe.anvilcraft.client.support.PowerGridSupport; import dev.dubhe.anvilcraft.client.support.RenderSupport; import net.minecraft.client.Camera; @@ -25,6 +27,8 @@ import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.PostChain; +import net.minecraft.client.renderer.PostPass; import net.minecraft.client.renderer.ShaderInstance; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; @@ -164,4 +168,87 @@ void bloomPostProcess( RenderSystem.enableDepthTest(); minecraft.getMainRenderTarget().bindWrite(false); } + + @Inject( + method = "renderLevel", + at = @At("TAIL") + ) + void gravitationalLensPostProcess( + DeltaTracker deltaTracker, + boolean renderBlockOutline, + Camera camera, + GameRenderer gameRenderer, + LightTexture lightTexture, + Matrix4f frustumMatrix, + Matrix4f projectionMatrix, + CallbackInfo ci + ) { + if (!RenderState.isLensEffectEnabled()) return; + if (ModShaders.getLensChain() == null) return; + + PostChain lensChain = ModShaders.getLensChain(); + java.util.List passes = ((PostChainAccessor) lensChain).getPasses(); + if (passes.isEmpty()) return; + + PostPass pass = passes.get(0); + + // Collect visible black hole screen positions + java.util.List holes = + GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 4); + + int count = Math.min(holes.size(), 4); + + // Set dynamic black hole position uniforms + for (int i = 0; i < 4; i++) { + String xName = "BlackHole" + (i + 1) + "X"; + String yName = "BlackHole" + (i + 1) + "Y"; + if (i < count) { + GravitationalLensManager.ScreenProjection proj = holes.get(i); + pass.getEffect().safeGetUniform(xName).set(proj.screenU()); + pass.getEffect().safeGetUniform(yName).set(proj.screenV()); + } else { + pass.getEffect().safeGetUniform(xName).set(0.0f); + pass.getEffect().safeGetUniform(yName).set(0.0f); + } + } + pass.getEffect().safeGetUniform("BlackHoleCount").set((float) count); + pass.getEffect().safeGetUniform("LensStrength") + .set((float) AnvilCraftClient.CONFIG.lensStrength); + pass.getEffect().safeGetUniform("EventHorizonRadius") + .set((float) AnvilCraftClient.CONFIG.eventHorizonRadius); + + // Run the lens post chain (reads main target, writes to result target) + lensChain.process(RenderSupport.getPartialTick()); + + // Blit result back to the main render target + RenderTarget result = lensChain.getTempTarget("result"); + RenderTarget main = Minecraft.getInstance().getMainRenderTarget(); + + float width = main.width; + float height = main.height; + + ShaderInstance blitShader = ModShaders.getBlitShader(); + RenderSystem.viewport(0, 0, (int) width, (int) height); + blitShader.setSampler("DiffuseSampler", result); + blitShader.safeGetUniform("ProjMat").set(ModShaders.getOrthoMatrix()); + blitShader.safeGetUniform("OutSize").set(width, height); + RenderSystem.depthFunc(GL11.GL_ALWAYS); + + BufferBuilder bufferbuilder = Tesselator.getInstance() + .begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION); + bufferbuilder.addVertex(0.0F, 0.0F, 500.0F); + bufferbuilder.addVertex(width, 0.0F, 500.0F); + bufferbuilder.addVertex(width, height, 500.0F); + bufferbuilder.addVertex(0.0F, height, 500.0F); + + blitShader.apply(); + main.bindWrite(false); + BufferUploader.draw(bufferbuilder.buildOrThrow()); + main.unbindWrite(); + result.unbindRead(); + + RenderSystem.depthFunc(GL11.GL_LEQUAL); + RenderSystem.enableDepthTest(); + main.bindWrite(false); + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/PostChainAccessor.java b/src/main/java/dev/dubhe/anvilcraft/mixin/PostChainAccessor.java new file mode 100644 index 0000000000..bf677e7139 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/PostChainAccessor.java @@ -0,0 +1,14 @@ +package dev.dubhe.anvilcraft.mixin; + +import net.minecraft.client.renderer.PostChain; +import net.minecraft.client.renderer.PostPass; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(PostChain.class) +public interface PostChainAccessor { + @Accessor + List getPasses(); +} diff --git a/src/main/resources/anvilcraft.mixins.json b/src/main/resources/anvilcraft.mixins.json index ccd487e464..1e959e5566 100644 --- a/src/main/resources/anvilcraft.mixins.json +++ b/src/main/resources/anvilcraft.mixins.json @@ -91,6 +91,7 @@ "MinecraftClientMixin", "MouseHandlerMixin", "ParticleEngineMixin", + "PostChainAccessor", "SectionCompilerMixin", "SoundEngineMixin", "accessor.MultiPlayerGameModeAccessor", diff --git a/src/main/resources/assets/anvilcraft/shaders/post/gravitational_lens.json b/src/main/resources/assets/anvilcraft/shaders/post/gravitational_lens.json new file mode 100644 index 0000000000..8e5249eaac --- /dev/null +++ b/src/main/resources/assets/anvilcraft/shaders/post/gravitational_lens.json @@ -0,0 +1,13 @@ +{ + "targets": [ + "mcinput", + "result" + ], + "passes": [ + { + "name": "anvilcraft:gravitational_lens", + "intarget": "minecraft:main", + "outtarget": "result" + } + ] +} diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh new file mode 100644 index 0000000000..8fe5a8e47e --- /dev/null +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -0,0 +1,68 @@ +#version 150 + +uniform sampler2D DiffuseSampler; +uniform vec2 InSize; + +// Up to 4 black hole screen positions in UV coordinates (0-1) +uniform float BlackHole1X; +uniform float BlackHole1Y; +uniform float BlackHole2X; +uniform float BlackHole2Y; +uniform float BlackHole3X; +uniform float BlackHole3Y; +uniform float BlackHole4X; +uniform float BlackHole4Y; + +uniform float BlackHoleCount; +uniform float LensStrength; +uniform float EventHorizonRadius; + +in vec2 texCoord; +out vec4 fragColor; + +vec2 getHolePos(int i) { + if (i == 0) return vec2(BlackHole1X, BlackHole1Y); + if (i == 1) return vec2(BlackHole2X, BlackHole2Y); + if (i == 2) return vec2(BlackHole3X, BlackHole3Y); + return vec2(BlackHole4X, BlackHole4Y); +} + +void main() { + vec2 uv = texCoord; + vec2 offset = vec2(0.0); + int count = int(BlackHoleCount); + + // Combine gravitational displacement from all active black holes + for (int i = 0; i < 4; i++) { + if (i >= count) break; + + vec2 holeUv = getHolePos(i); + vec2 toHole = holeUv - uv; + float dist = length(toHole); + + if (dist < 0.0001) continue; + + vec2 dir = toHole / dist; + + // Inverse-square falloff (mirroring the reference gravitational lensing) + float distSqr = dist * dist; + float gravity = LensStrength / distSqr; + + // Mask out within event horizon to avoid singularity + float mask = step(EventHorizonRadius, dist); + + offset += dir * gravity * mask; + } + + vec3 color = texture(DiffuseSampler, uv + offset).rgb; + + // Render event horizon (black disk) for each black hole + for (int i = 0; i < 4; i++) { + if (i >= count) break; + float dist = length(getHolePos(i) - uv); + float horizonMask = 1.0 - step(EventHorizonRadius, dist); + color = mix(color, vec3(0.0, 0.0, 0.0), horizonMask); + } + + fragColor = vec4(color, 1.0); +} diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json new file mode 100644 index 0000000000..d9692ad075 --- /dev/null +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json @@ -0,0 +1,108 @@ +{ + "blend": { + "func": "add", + "srcrgb": "one", + "dstrgb": "zero" + }, + "vertex": "blit", + "fragment": "anvilcraft:gravitational_lens", + "attributes": [ + "Position" + ], + "samplers": [ + { + "name": "DiffuseSampler" + } + ], + "uniforms": [ + { + "name": "ProjMat", + "type": "matrix4x4", + "count": 16, + "values": [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ] + }, + { + "name": "InSize", + "type": "float", + "count": 2, + "values": [ 1.0, 1.0 ] + }, + { + "name": "OutSize", + "type": "float", + "count": 2, + "values": [ 1.0, 1.0 ] + }, + { + "name": "BlackHole1X", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "BlackHole1Y", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "BlackHole2X", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "BlackHole2Y", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "BlackHole3X", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "BlackHole3Y", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "BlackHole4X", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "BlackHole4Y", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "BlackHoleCount", + "type": "float", + "count": 1, + "values": [ 0.0 ] + }, + { + "name": "LensStrength", + "type": "float", + "count": 1, + "values": [ 0.001953125 ] + }, + { + "name": "EventHorizonRadius", + "type": "float", + "count": 1, + "values": [ 0.083333333 ] + } + ] +} From eaa29d00867b5cf536307ea628b2e5b67be98574 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 03:01:18 +0800 Subject: [PATCH 02/17] =?UTF-8?q?feat(render):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=BB=91=E6=B4=9E=E9=80=8F=E9=95=9C=E6=95=88=E6=9E=9C=E7=9A=84?= =?UTF-8?q?=E7=AB=8B=E6=96=B9=E4=BD=93=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在配置中新增 LensingShape 枚举类型,支持 CIRCULAR 和 CUBIC 模式 - 添加立方体模式相关的配置选项:cubicEventHorizonRadius 和 lensPerspectiveScale - 在着色器中实现多边形透镜计算函数和射线多边形交点算法 - 新增顶点投影和凸包计算逻辑以支持立方体形状渲染 - 修改渲染管线以传递多边形顶点数据到着色器 - 实现透视缩放和距离计算以改善视觉效果 - 更新均匀变量设置以支持新的着色器参数 --- .../assets/anvilcraft/lang/en_ud.json | 8 +- .../assets/anvilcraft/lang/en_us.json | 8 +- .../support/GravitationalLensManager.java | 224 ++++++++++++++---- .../config/AnvilCraftClientConfig.java | 16 +- .../anvilcraft/mixin/LevelRendererMixin.java | 55 ++++- .../shaders/program/gravitational_lens.fsh | 218 +++++++++++++++-- .../shaders/program/gravitational_lens.json | 151 +++++------- 7 files changed, 506 insertions(+), 174 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index b47ca4edb9..db554c479d 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -123,6 +123,8 @@ "anvilcraft.configuration.chute_max_cooldown.tooltip": "(sʞɔᴉʇ uᴉ) ǝʇnɥɔ ɟo ǝɯᴉʇ uʍopꞁooɔ ɯnɯᴉxɐW", "anvilcraft.configuration.clean_fluid_after_update_menger_sponge": "ǝᵷuodS ɹǝᵷuǝW ǝʇɐpd∩ ɹǝʇɟⱯ pᴉnꞁℲ uɐǝꞁƆ", "anvilcraft.configuration.clean_fluid_after_update_menger_sponge.tooltip": "ǝᵷuodS ɹǝᵷuǝW ᵷuᴉʇɐpdn ɹǝʇɟɐ pᴉnꞁɟ uɐǝꞁɔ oʇ ɹǝɥʇǝɥM", + "anvilcraft.configuration.cubic_event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ ɔᴉqnƆ", + "anvilcraft.configuration.cubic_event_horizon_radius.tooltip": "˙ɹǝᵷᵷᴉq = ⥝< ˙ɹɐꞁnɔɹᴉɔ sɐ ǝzᴉs ǝɯɐs = 0˙⥝ ˙snᴉpɐɹ Ʌ∩ uo ɹǝᴉꞁdᴉʇꞁnɯ uozᴉɹoɥ ǝpoɯ ƆIᗺ∩Ɔ", "anvilcraft.configuration.display_anvil_animation": "uoᴉʇɐɯᴉuⱯ ꞁᴉʌuⱯ ʎɐꞁdsᴉᗡ", "anvilcraft.configuration.display_anvil_animation.tooltip": "uoᴉʇɐɯᴉuɐ ǝʇɐʇᴉʌǝꞁ ꞁᴉʌuɐ ʍoɥs pꞁnoɥS", "anvilcraft.configuration.do_not_show_tooltip_when_jade_present": "ʇuǝsǝɹԀ ǝpɐՐ uǝɥM dᴉʇꞁoo⟘ ʍoɥS ʇoN oᗡ", @@ -130,7 +132,7 @@ "anvilcraft.configuration.ember_anvil_beyond_max_level": "ꞁǝʌǝꞀ xɐW puoʎǝᗺ ꞁᴉʌuⱯ ɹǝqɯƎ", "anvilcraft.configuration.ember_anvil_beyond_max_level.tooltip": "ꞁᴉʌuⱯ ɹǝqɯƎ uᴉ ꞁǝʌǝꞁ xɐɯ puoʎǝq sʞooᗺ pǝʇuɐɥɔuƎ ɥʇᴉʍ sɯǝʇᴉ ᵷuᴉuᴉqɯoƆ", "anvilcraft.configuration.event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ", - "anvilcraft.configuration.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'ʞsᴉp ʞɔɐꞁq ɹǝᵷᵷᴉq = ɹǝɥᵷᴉɥ) ǝɔɐds Ʌ∩ uǝǝɹɔs uᴉ snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", + "anvilcraft.configuration.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'sʇᴉun Ʌ∩ uǝǝɹɔs) ǝpoɯ ᴚⱯꞀ∩ƆᴚIƆ uᴉ snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", "anvilcraft.configuration.felling_block_per_level": "ꞁǝʌǝꞀ ɹǝԀ ʞɔoꞁᗺ ᵷuᴉꞁꞁǝℲ", "anvilcraft.configuration.felling_block_per_level.tooltip": "ʇuǝɯʇuɐɥɔuǝ ᵷuᴉꞁꞁǝℲ ɟo ꞁǝʌǝꞁ ɹǝd ʇnɔ ǝq uɐɔ ʇɐɥʇ sᵷoꞁ ɟo ɹǝqɯnu ɯnɯᴉxɐɯ ǝɥ⟘", "anvilcraft.configuration.frost_anvil_beyond_max_level": "ꞁǝʌǝꞀ xɐW puoʎǝᗺ ꞁᴉʌuⱯ ʇsoɹℲ", @@ -180,8 +182,12 @@ "anvilcraft.configuration.iono_craft_backpack_max_flight_time.tooltip": "sʞɔᴉʇ uᴉ ǝɯᴉ⟘ ʇɥᵷᴉꞁℲ xɐW ʞɔɐdʞɔɐᗺ ʇɟɐɹƆ ouoI", "anvilcraft.configuration.is_laser_do_impact_checking": "ᵷuᴉʞɔǝɥƆ ʇɔɐdɯI oᗡ ɹǝsɐꞀ sI", "anvilcraft.configuration.is_laser_do_impact_checking.tooltip": "ᵷuᴉʞɔǝɥɔ ʇɔɐdɯᴉ op ɹǝsɐꞁ sI", + "anvilcraft.configuration.lens_perspective_scale": "ǝꞁɐɔS ǝʌᴉʇɔǝdsɹǝԀ suǝꞀ", + "anvilcraft.configuration.lens_perspective_scale.tooltip": "˙ɹǝᵷᵷᴉq = ɹǝsoꞁƆ ˙ǝzᴉs ᵷᴉɟuoɔ = ʇɔǝɟɟǝ 'ǝɔuɐʇsᴉp sᴉɥʇ ʇⱯ ˙ᵷuᴉꞁɐɔs ǝʌᴉʇɔǝdsɹǝd ɹoɟ ǝɔuɐʇsᴉp ǝɔuǝɹǝɟǝᴚ", "anvilcraft.configuration.lens_strength": "ɥʇᵷuǝɹʇS suǝꞀ", "anvilcraft.configuration.lens_strength.tooltip": "(ʇꞁnɐɟǝp ᘔ00˙0 'ᵷuᴉpuǝq ɹǝᵷuoɹʇs = ɹǝɥᵷᴉɥ) ɥʇᵷuǝɹʇs uoᴉʇɹoʇsᴉp suǝꞀ", + "anvilcraft.configuration.lensing_shape": "ǝdɐɥS ᵷuᴉsuǝꞀ", + "anvilcraft.configuration.lensing_shape.tooltip": "(ǝꞁᵷuɐ ʍǝᴉʌ ɯoɹɟ ǝʇʇǝnoɥꞁᴉs ǝqnɔ sʍoꞁꞁoɟ) ƆIᗺ∩Ɔ ɹo (ɹǝpɐɥs ǝɔuǝɹǝɟǝɹ ǝʞᴉꞁ 'punoɹ) ᴚⱯꞀ∩ƆᴚIƆ :ǝdɐɥs ᵷuᴉsuǝꞀ", "anvilcraft.configuration.lightning_strike_depth": "ɥʇdǝᗡ ǝʞᴉɹʇS ᵷuᴉuʇɥᵷᴉꞀ", "anvilcraft.configuration.lightning_strike_depth.tooltip": "ɥɔɐǝɹ uɐɔ ǝʞᴉɹʇs ᵷuᴉuʇɥᵷᴉꞁ ɐ ɥʇdǝp ɯnɯᴉxɐW", "anvilcraft.configuration.lightning_strike_radius": "snᴉpɐᴚ ǝʞᴉɹʇS ᵷuᴉuʇɥᵷᴉꞀ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index d9de9da8f4..a6719c55dc 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -123,6 +123,8 @@ "anvilcraft.configuration.chute_max_cooldown.tooltip": "Maximum cooldown time of chute (in ticks)", "anvilcraft.configuration.clean_fluid_after_update_menger_sponge": "Clean Fluid After Update Menger Sponge", "anvilcraft.configuration.clean_fluid_after_update_menger_sponge.tooltip": "Whether to clean fluid after updating Menger Sponge", + "anvilcraft.configuration.cubic_event_horizon_radius": "Cubic Event Horizon Radius", + "anvilcraft.configuration.cubic_event_horizon_radius.tooltip": "CUBIC mode horizon multiplier on UV radius. 1.0 = same size as circular. >1 = bigger.", "anvilcraft.configuration.display_anvil_animation": "Display Anvil Animation", "anvilcraft.configuration.display_anvil_animation.tooltip": "Should show anvil levitate animation", "anvilcraft.configuration.do_not_show_tooltip_when_jade_present": "Do Not Show Tooltip When Jade Present", @@ -130,7 +132,7 @@ "anvilcraft.configuration.ember_anvil_beyond_max_level": "Ember Anvil Beyond Max Level", "anvilcraft.configuration.ember_anvil_beyond_max_level.tooltip": "Combining items with Enchanted Books beyond max level in Ember Anvil", "anvilcraft.configuration.event_horizon_radius": "Event Horizon Radius", - "anvilcraft.configuration.event_horizon_radius.tooltip": "Event horizon radius in screen UV space (higher = bigger black disk, 0.083 default)", + "anvilcraft.configuration.event_horizon_radius.tooltip": "Event horizon radius in CIRCULAR mode (screen UV units, 0.083 default)", "anvilcraft.configuration.felling_block_per_level": "Felling Block Per Level", "anvilcraft.configuration.felling_block_per_level.tooltip": "The maximum number of logs that can be cut per level of Felling enchantment", "anvilcraft.configuration.frost_anvil_beyond_max_level": "Frost Anvil Beyond Max Level", @@ -180,8 +182,12 @@ "anvilcraft.configuration.iono_craft_backpack_max_flight_time.tooltip": "Iono Craft Backpack Max Flight Time in ticks", "anvilcraft.configuration.is_laser_do_impact_checking": "Is Laser Do Impact Checking", "anvilcraft.configuration.is_laser_do_impact_checking.tooltip": "Is laser do impact checking", + "anvilcraft.configuration.lens_perspective_scale": "Lens Perspective Scale", + "anvilcraft.configuration.lens_perspective_scale.tooltip": "Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.", "anvilcraft.configuration.lens_strength": "Lens Strength", "anvilcraft.configuration.lens_strength.tooltip": "Lens distortion strength (higher = stronger bending, 0.002 default)", + "anvilcraft.configuration.lensing_shape": "Lensing Shape", + "anvilcraft.configuration.lensing_shape.tooltip": "Lensing shape: CIRCULAR (round, like reference shader) or CUBIC (follows cube silhouette from view angle)", "anvilcraft.configuration.lightning_strike_depth": "Lightning Strike Depth", "anvilcraft.configuration.lightning_strike_depth.tooltip": "Maximum depth a lightning strike can reach", "anvilcraft.configuration.lightning_strike_radius": "Lightning Strike Radius", diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index aa38fec624..373bf4c02b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -1,13 +1,16 @@ package dev.dubhe.anvilcraft.client.support; +import dev.dubhe.anvilcraft.config.AnvilCraftClientConfig; import net.minecraft.client.Camera; import net.minecraft.core.BlockPos; import org.joml.Matrix4f; import org.joml.Quaternionf; +import org.joml.Vector2f; import org.joml.Vector3f; import org.joml.Vector4f; import java.util.ArrayList; +import java.util.Comparator; import java.util.Collections; import java.util.List; import java.util.Set; @@ -15,8 +18,12 @@ public class GravitationalLensManager { private static final int MAX_BLACK_HOLES = 4; + private static final int MAX_POLY_VERTS = 6; private static final int MAX_SEARCH_DISTANCE_SQR = 256 * 256; + /** Half-extent of the black hole block model: box(4,4,4,12,12,12) → (12-4)/2/16 = 0.25 */ + private static final float CUBE_HALF_EXTENT = 0.25f; + /** Client-side cache of loaded black hole block positions. */ public static final Set CLIENT_BLACK_HOLE_POSITIONS = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -30,30 +37,45 @@ public static void unregister(BlockPos pos) { } /** - * Holds the screen-space projection result for a single black hole. + * Full per-hole data passed to the shader. */ - public record ScreenProjection(float screenU, float screenV, boolean onScreen) {} + public static final class HoleProjection { + /** Center UV of the black hole on screen. */ + public final float centerU; + public final float centerV; + public final boolean onScreen; + /** Distance from camera to black hole (world units). */ + public final float cameraDistance; + + /** Number of convex-hull polygon vertices (0 when circular mode or degenerate). */ + public final int polyVertCount; + /** UV coordinates of polygon vertices, tightly packed. Length = polyVertCount. */ + public final float[] polyU; + public final float[] polyV; + + HoleProjection(float cu, float cv, boolean onScreen, float dist, + int vertCount, float[] pu, float[] pv) { + this.centerU = cu; + this.centerV = cv; + this.onScreen = onScreen; + this.cameraDistance = dist; + this.polyVertCount = vertCount; + this.polyU = pu; + this.polyV = pv; + } + + static HoleProjection offScreen() { + return new HoleProjection(0, 0, false, 0, 0, new float[0], new float[0]); + } + } /** - * Project a world position to screen UV coordinates. - * - * @param worldPos the block position in world space - * @param camera the current camera - * @param projectionMatrix the projection matrix from renderLevel - * @return ScreenProjection with UV coordinates and on-screen flag + * Build the combined view-projection matrix from camera position/rotation + projection. */ - public static ScreenProjection projectToScreen( - BlockPos worldPos, - Camera camera, - Matrix4f projectionMatrix - ) { + private static Matrix4f buildViewProj(Camera camera, Matrix4f projectionMatrix) { float yaw = camera.getYRot(); float pitch = camera.getXRot(); - // Build view matrix from camera rotation and position. - // Minecraft yaw: 0 = south (+Z), 90 = west (-X), 180 = north (-Z), 270 = east (+X). - // Minecraft pitch: positive = looking down. - // We invert both rotations to go from world → camera space. Quaternionf cameraRotation = new Quaternionf() .rotateX((float) Math.toRadians(pitch)) .rotateY((float) Math.toRadians(yaw + 180.0f)); @@ -64,67 +86,167 @@ public static ScreenProjection projectToScreen( .rotate(cameraRotation) .translate(-cameraPos.x, -cameraPos.y, -cameraPos.z); - Matrix4f viewProj = new Matrix4f(projectionMatrix).mul(viewMatrix); - - // Center of block - Vector4f clip = viewProj.transform( - new Vector4f( - worldPos.getX() + 0.5f, - worldPos.getY() + 0.5f, - worldPos.getZ() + 0.5f, - 1.0f - ) - ); + return new Matrix4f(projectionMatrix).mul(viewMatrix); + } - if (clip.w <= 0.0f) { - return new ScreenProjection(0, 0, false); - } + /** + * Transform a world-space point to NDC, then to screen UV. + * Returns null if the point is behind the camera or off-screen. + */ + private static Vector2f worldToScreenUV(float wx, float wy, float wz, Matrix4f viewProj) { + Vector4f clip = viewProj.transform(new Vector4f(wx, wy, wz, 1.0f)); + if (clip.w <= 0.0f) return null; float ndcX = clip.x / clip.w; float ndcY = clip.y / clip.w; float ndcZ = clip.z / clip.w; - // Check if within clip space bounds - if (ndcZ < -1.0f || ndcZ > 1.0f) { - return new ScreenProjection(0, 0, false); + if (ndcZ < -1.0f || ndcZ > 1.0f) return null; + if (ndcX < -1.0f || ndcX > 1.0f || ndcY < -1.0f || ndcY > 1.0f) return null; + + return new Vector2f((ndcX + 1.0f) / 2.0f, (ndcY + 1.0f) / 2.0f); + } + + /** + * Compute the 2D convex hull of points using Andrew's monotone chain. + * Returns vertices in CCW order. Max 6 for a cube projection. + */ + static List convexHull2D(List points) { + if (points.size() <= 2) return new ArrayList<>(points); + + // Remove exact duplicates + List unique = new ArrayList<>(); + for (Vector2f p : points) { + boolean dup = false; + for (Vector2f u : unique) { + if (Math.abs(u.x - p.x) < 0.0001f && Math.abs(u.y - p.y) < 0.0001f) { + dup = true; + break; + } + } + if (!dup) unique.add(p); + } + if (unique.size() <= 2) return unique; + + unique.sort(Comparator.comparingDouble(v -> v.x) + .thenComparingDouble(v -> v.y)); + + List hull = new ArrayList<>(); + + // Lower hull + for (Vector2f p : unique) { + while (hull.size() >= 2) { + Vector2f a = hull.get(hull.size() - 2); + Vector2f b = hull.get(hull.size() - 1); + if ((b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x) > 0) break; + hull.remove(hull.size() - 1); + } + hull.add(p); + } + + // Upper hull + int lowerSize = hull.size(); + for (int i = unique.size() - 2; i >= 0; i--) { + Vector2f p = unique.get(i); + while (hull.size() > lowerSize) { + Vector2f a = hull.get(hull.size() - 2); + Vector2f b = hull.get(hull.size() - 1); + if ((b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x) > 0) break; + hull.remove(hull.size() - 1); + } + hull.add(p); } - if (ndcX < -1.0f || ndcX > 1.0f || ndcY < -1.0f || ndcY > 1.0f) { - return new ScreenProjection(0, 0, false); + + hull.remove(hull.size() - 1); // Remove duplicate + return hull; + } + + /** + * Project the 8 vertices of the cube model and compute the 2D convex hull. + * Returns an empty list if the projection is degenerate. + */ + static List computeCubeProjectionHull( + BlockPos worldPos, Matrix4f viewProj + ) { + float cx = worldPos.getX() + 0.5f; + float cy = worldPos.getY() + 0.5f; + float cz = worldPos.getZ() + 0.5f; + float h = CUBE_HALF_EXTENT; + + List projected = new ArrayList<>(8); + for (int ix = 0; ix < 2; ix++) { + float x = cx + (ix == 0 ? -h : h); + for (int iy = 0; iy < 2; iy++) { + float y = cy + (iy == 0 ? -h : h); + for (int iz = 0; iz < 2; iz++) { + float z = cz + (iz == 0 ? -h : h); + Vector2f uv = worldToScreenUV(x, y, z, viewProj); + if (uv != null) projected.add(uv); + } + } } - float screenU = (ndcX + 1.0f) / 2.0f; - float screenV = (ndcY + 1.0f) / 2.0f; + if (projected.size() < 3) return projected; // degenerate - return new ScreenProjection(screenU, screenV, true); + List hull = convexHull2D(projected); + // Limit to MAX_POLY_VERTS + if (hull.size() > MAX_POLY_VERTS) { + hull = hull.subList(0, MAX_POLY_VERTS); + } + return hull; } /** - * Collect up to {@code maxCount} on-screen black hole projections, - * sorted by distance to camera (nearest first). + * Collect up to {@code maxCount} on-screen black holes with optional polygon hull data. + * + * @param shapeMode CIRCULAR or CUBIC — determines whether polygon hulls are computed. */ - public static List collectVisibleBlackHoles( + public static List collectVisibleBlackHoles( Camera camera, Matrix4f projectionMatrix, - int maxCount + int maxCount, + AnvilCraftClientConfig.LensingShape shapeMode ) { - List result = new ArrayList<>(); + List result = new ArrayList<>(); if (CLIENT_BLACK_HOLE_POSITIONS.isEmpty()) return result; + Matrix4f viewProj = buildViewProj(camera, projectionMatrix); Vector3f cameraPos = camera.getPosition().toVector3f(); for (BlockPos pos : CLIENT_BLACK_HOLE_POSITIONS) { - // Quick distance cull double dx = pos.getX() + 0.5 - cameraPos.x; double dy = pos.getY() + 0.5 - cameraPos.y; double dz = pos.getZ() + 0.5 - cameraPos.z; - double distSqr = dx * dx + dy * dy + dz * dz; - if (distSqr > MAX_SEARCH_DISTANCE_SQR) continue; + if (dx * dx + dy * dy + dz * dz > MAX_SEARCH_DISTANCE_SQR) continue; + + // Project center + Vector2f centerUV = worldToScreenUV( + pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f, viewProj + ); + if (centerUV == null) continue; - ScreenProjection proj = projectToScreen(pos, camera, projectionMatrix); - if (proj.onScreen) { - result.add(proj); - if (result.size() >= maxCount) break; + List hull = Collections.emptyList(); + if (shapeMode == AnvilCraftClientConfig.LensingShape.CUBIC) { + hull = computeCubeProjectionHull(pos, viewProj); } + + float[] pu, pv; + int vertCount = hull.size(); + if (vertCount > 0) { + pu = new float[vertCount]; + pv = new float[vertCount]; + for (int i = 0; i < vertCount; i++) { + pu[i] = hull.get(i).x; + pv[i] = hull.get(i).y; + } + } else { + pu = new float[0]; + pv = new float[0]; + } + + float dist = (float) Math.sqrt(dx * dx + dy * dy + dz * dz); + result.add(new HoleProjection(centerUV.x, centerUV.y, true, dist, vertCount, pu, pv)); + if (result.size() >= maxCount) break; } return result; } diff --git a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java index a227c5c931..edcfb71ba1 100644 --- a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java +++ b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java @@ -48,12 +48,26 @@ public class AnvilCraftClientConfig { @Comment("Gravitational lensing post-processing effect near black holes") public boolean renderBlackHoleLensing = true; + @Comment("Lensing shape: CIRCULAR (round, like reference shader) or CUBIC (follows cube silhouette from view angle)") + public LensingShape lensingShape = LensingShape.CIRCULAR; + @Comment("Lens distortion strength (higher = stronger bending, 0.002 default)") public double lensStrength = 1.0 / 512.0; - @Comment("Event horizon radius in screen UV space (higher = bigger black disk, 0.083 default)") + @Comment("Event horizon radius in CIRCULAR mode (screen UV units, 0.083 default)") public double eventHorizonRadius = 1.0 / 12.0; + @Comment("CUBIC mode horizon multiplier on UV radius. 1.0 = same size as circular. >1 = bigger.") + public double cubicEventHorizonRadius = 1.0; + + @Comment("Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.") + public double lensPerspectiveScale = 10.0; + + public enum LensingShape { + CIRCULAR, + CUBIC + } + @Comment("A vertical item frame vertically displays items") public boolean verticalItemFrame = false; diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index f82fbc383d..533d12d23e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -13,6 +13,7 @@ import com.mojang.blaze3d.vertex.VertexFormat; import dev.dubhe.anvilcraft.api.rendering.CacheableBERenderingPipeline; import dev.dubhe.anvilcraft.client.AnvilCraftClient; +import dev.dubhe.anvilcraft.config.AnvilCraftClientConfig; import dev.dubhe.anvilcraft.client.init.ModRenderTargets; import dev.dubhe.anvilcraft.client.init.ModShaders; import dev.dubhe.anvilcraft.client.renderer.RenderState; @@ -192,30 +193,66 @@ void gravitationalLensPostProcess( PostPass pass = passes.get(0); - // Collect visible black hole screen positions - java.util.List holes = - GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 4); + AnvilCraftClientConfig.LensingShape shape = AnvilCraftClient.CONFIG.lensingShape; + + // Collect visible black holes with optional polygon hull data + java.util.List holes = + GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 4, shape); int count = Math.min(holes.size(), 4); - // Set dynamic black hole position uniforms + // Set dynamic black hole position and distance uniforms for (int i = 0; i < 4; i++) { String xName = "BlackHole" + (i + 1) + "X"; String yName = "BlackHole" + (i + 1) + "Y"; + String dName = "BlackHole" + (i + 1) + "Dist"; if (i < count) { - GravitationalLensManager.ScreenProjection proj = holes.get(i); - pass.getEffect().safeGetUniform(xName).set(proj.screenU()); - pass.getEffect().safeGetUniform(yName).set(proj.screenV()); + GravitationalLensManager.HoleProjection h = holes.get(i); + pass.getEffect().safeGetUniform(xName).set(h.centerU); + pass.getEffect().safeGetUniform(yName).set(h.centerV); + pass.getEffect().safeGetUniform(dName).set(h.cameraDistance); } else { pass.getEffect().safeGetUniform(xName).set(0.0f); pass.getEffect().safeGetUniform(yName).set(0.0f); + pass.getEffect().safeGetUniform(dName).set(1.0f); } } + + // Set polygon vertex data (tightly packed across all holes) + int globalVertIdx = 0; + for (int i = 0; i < 4; i++) { + pass.getEffect().safeGetUniform("PolyStart[" + i + "]").set((float) globalVertIdx); + if (i < count && shape == AnvilCraftClientConfig.LensingShape.CUBIC) { + GravitationalLensManager.HoleProjection h = holes.get(i); + int vc = h.polyVertCount; + pass.getEffect().safeGetUniform("PolyCount[" + i + "]").set((float) vc); + for (int j = 0; j < vc && globalVertIdx < 24; j++) { + String uName = "PolyVerts[" + globalVertIdx + "]"; + pass.getEffect().safeGetUniform(uName).set(h.polyU[j], h.polyV[j]); + globalVertIdx++; + } + } else { + pass.getEffect().safeGetUniform("PolyCount[" + i + "]").set(0.0f); + } + } + // Zero out remaining poly verts + for (int k = globalVertIdx; k < 24; k++) { + pass.getEffect().safeGetUniform("PolyVerts[" + k + "]").set(0.0f, 0.0f); + } + + // Cubic mode: cubicEventHorizonRadius acts as a multiplier on the UV-based radius + float horizonRadius = (float) (AnvilCraftClient.CONFIG.eventHorizonRadius + * (shape == AnvilCraftClientConfig.LensingShape.CUBIC + ? AnvilCraftClient.CONFIG.cubicEventHorizonRadius : 1.0)); + pass.getEffect().safeGetUniform("BlackHoleCount").set((float) count); pass.getEffect().safeGetUniform("LensStrength") .set((float) AnvilCraftClient.CONFIG.lensStrength); - pass.getEffect().safeGetUniform("EventHorizonRadius") - .set((float) AnvilCraftClient.CONFIG.eventHorizonRadius); + pass.getEffect().safeGetUniform("EventHorizonRadius").set(horizonRadius); + pass.getEffect().safeGetUniform("LensingShape") + .set(shape == AnvilCraftClientConfig.LensingShape.CUBIC ? 1.0f : 0.0f); + pass.getEffect().safeGetUniform("PerspectiveScale") + .set((float) AnvilCraftClient.CONFIG.lensPerspectiveScale); // Run the lens post chain (reads main target, writes to result target) lensChain.process(RenderSupport.getPartialTick()); diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index 8fe5a8e47e..da2bcdc7b6 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -13,9 +13,22 @@ uniform float BlackHole3Y; uniform float BlackHole4X; uniform float BlackHole4Y; +// Distance from camera to each black hole (world units) +uniform float BlackHole1Dist; +uniform float BlackHole2Dist; +uniform float BlackHole3Dist; +uniform float BlackHole4Dist; + +// Polygon vertex data for cubic lensing (tightly packed for all holes, max 24) +uniform vec2 PolyVerts[24]; +uniform float PolyStart[4]; +uniform float PolyCount[4]; + uniform float BlackHoleCount; uniform float LensStrength; uniform float EventHorizonRadius; +uniform float LensingShape; // 0 = CIRCULAR, 1 = CUBIC +uniform float PerspectiveScale; // reference distance for perspective (default 10.0) in vec2 texCoord; out vec4 fragColor; @@ -27,40 +40,207 @@ vec2 getHolePos(int i) { return vec2(BlackHole4X, BlackHole4Y); } +float getHoleDist(int i) { + if (i == 0) return BlackHole1Dist; + if (i == 1) return BlackHole2Dist; + if (i == 2) return BlackHole3Dist; + return BlackHole4Dist; +} + +// Signed distance to convex polygon: negative inside, positive outside. +float sdPolygon(vec2 p, int startIdx, int vertCount) { + if (vertCount < 3) return 1.0; + float d = 1e10; + int posCount = 0; + int negCount = 0; + + for (int j = 0; j < 6; j++) { + if (j >= vertCount) break; + int jNext = (j + 1) % vertCount; + vec2 a = PolyVerts[startIdx + j]; + vec2 b = PolyVerts[startIdx + jNext]; + vec2 e = b - a; + vec2 ap = p - a; + + // Cross product sign per edge + float crossVal = e.x * ap.y - e.y * ap.x; + if (crossVal > 0.0) posCount++; + else if (crossVal < 0.0) negCount++; + + // Distance to this edge segment + float t = clamp(dot(ap, e) / max(dot(e, e), 0.00001), 0.0, 1.0); + vec2 closest = a + t * e; + d = min(d, length(p - closest)); + } + + // Inside iff all cross products have same sign (convex polygon) + float inside = (posCount == vertCount || negCount == vertCount) ? -1.0 : 1.0; + return d * inside; +} + +// Distance from origin along `dir` to polygon boundary. +// Returns a large number if no intersection is found. +float rayToPolygon(vec2 origin, vec2 dir, int startIdx, int vertCount) { + float minT = 1e10; + for (int j = 0; j < 6; j++) { + if (j >= vertCount) break; + int jNext = (j + 1) % vertCount; + vec2 a = PolyVerts[startIdx + j]; + vec2 b = PolyVerts[startIdx + jNext]; + vec2 e = b - a; + float det = dir.x * e.y - dir.y * e.x; + if (abs(det) < 0.00001) continue; + float t = ((a.x - origin.x) * e.y - (a.y - origin.y) * e.x) / det; + float s = ((a.x - origin.x) * dir.y - (a.y - origin.y) * dir.x) / (-det); + if (t > 0.0 && s >= 0.0 && s <= 1.0) { + minT = min(minT, t); + } + } + return minT; +} + void main() { vec2 uv = texCoord; + float aspectRatio = InSize.x / InSize.y; + vec2 offset = vec2(0.0); int count = int(BlackHoleCount); - // Combine gravitational displacement from all active black holes for (int i = 0; i < 4; i++) { if (i >= count) break; vec2 holeUv = getHolePos(i); - vec2 toHole = holeUv - uv; - float dist = length(toHole); - - if (dist < 0.0001) continue; - - vec2 dir = toHole / dist; - - // Inverse-square falloff (mirroring the reference gravitational lensing) - float distSqr = dist * dist; - float gravity = LensStrength / distSqr; - - // Mask out within event horizon to avoid singularity - float mask = step(EventHorizonRadius, dist); - - offset += dir * gravity * mask; + bool isCubic = LensingShape > 0.5; + int pStart = int(PolyStart[i]); + int pCount = int(PolyCount[i]); + + float perspScale = PerspectiveScale / max(getHoleDist(i), 0.1); + + float effDist; + vec2 dir; + + if (isCubic && pCount >= 3) { + // --- Cubic mode: polygon-shaped gravitational field --- + + // Compute polygon center + vec2 polyCenter = vec2(0.0); + for (int j = 0; j < 6; j++) { + if (j >= pCount) break; + polyCenter += PolyVerts[pStart + j]; + } + polyCenter /= float(pCount); + + // Compute average polygon radius in UV units + float avgPolyRadius = 0.0; + for (int j = 0; j < 6; j++) { + if (j >= pCount) break; + avgPolyRadius += length(PolyVerts[pStart + j] - polyCenter); + } + avgPolyRadius /= float(pCount); + + // Direction and distance from polygon center to current pixel + vec2 fromCenter = uv - polyCenter; + float distFromCenter = length(fromCenter); + + if (distFromCenter < 0.0001) continue; + + vec2 centerDir = fromCenter / distFromCenter; + + // Distance from center to polygon boundary in this direction + float boundaryDist = rayToPolygon(polyCenter, centerDir, pStart, pCount); + if (boundaryDist >= 1e9) { + boundaryDist = avgPolyRadius; + } + + // Normalized t: 0 at center, 1 at boundary, >1 outside + float t = distFromCenter / max(boundaryDist, 0.0001); + + // Convert UV-based EventHorizonRadius to polygon-normalized threshold. + // horizonScale = how many polygon-radius-units the UV horizon covers. + float horizonScale = (EventHorizonRadius * perspScale) / max(avgPolyRadius, 0.00001); + // Map t so that t=horizonScale corresponds to the event horizon boundary. + effDist = t / max(horizonScale, 0.0001); + // Direction toward polygon center (in raw UV) + dir = centerDir; + } else { + // --- Circular mode --- + vec2 toHole = holeUv - uv; + toHole.x *= aspectRatio; + float dist = length(toHole); + + if (dist < 0.0001) continue; + + effDist = dist; + dir = toHole / dist; + } + + // Inverse-square gravitational pull (scaled by perspective) + float distSqr = effDist * effDist; + float gravity = LensStrength * perspScale / distSqr; + + // Mask out within event horizon (scaled by perspective) + float scaledHorizon = EventHorizonRadius * perspScale; + float mask = step(scaledHorizon, effDist); + + vec2 lensOffset = dir * gravity * mask; + + if (!isCubic || pCount < 3) { + // Circular mode: convert aspect-ratio back + lensOffset.x /= aspectRatio; + } + // Cubic mode: offset is already in raw UV space, no conversion needed + + offset += lensOffset; } vec3 color = texture(DiffuseSampler, uv + offset).rgb; - // Render event horizon (black disk) for each black hole + // Render event horizon for each black hole for (int i = 0; i < 4; i++) { if (i >= count) break; - float dist = length(getHolePos(i) - uv); - float horizonMask = 1.0 - step(EventHorizonRadius, dist); + + vec2 holeUv = getHolePos(i); + bool isCubic = LensingShape > 0.5; + int pStart = int(PolyStart[i]); + int pCount = int(PolyCount[i]); + + float horizonMask = 0.0; + + if (isCubic && pCount >= 3) { + // Polygon-shaped event horizon. + vec2 polyCenter = vec2(0.0); + for (int j = 0; j < 6; j++) { + if (j >= pCount) break; + polyCenter += PolyVerts[pStart + j]; + } + polyCenter /= float(pCount); + + // Average polygon radius in UV units + float avgPolyRadius = 0.0; + for (int j = 0; j < 6; j++) { + if (j >= pCount) break; + avgPolyRadius += length(PolyVerts[pStart + j] - polyCenter); + } + avgPolyRadius /= float(pCount); + + // Convert UV-based horizon to polygon fraction + float perspS = PerspectiveScale / max(getHoleDist(i), 0.1); + float horizonUv = EventHorizonRadius * perspS; + float r = clamp(horizonUv / max(avgPolyRadius, 0.00001), 0.0, 0.99); + + float scale = 1.0 / max(1.0 - r, 0.01); + vec2 testPt = polyCenter + (uv - polyCenter) * scale; + float sd = sdPolygon(testPt, pStart, pCount); + horizonMask = step(sd, 0.0); + } else { + // Circular event horizon (aspect-ratio corrected, perspective scaled) + float perspS = PerspectiveScale / max(getHoleDist(i), 0.1); + vec2 toHole = uv - holeUv; + toHole.x *= aspectRatio; + float dist = length(toHole); + horizonMask = 1.0 - step(EventHorizonRadius * perspS, dist); + } + color = mix(color, vec3(0.0, 0.0, 0.0), horizonMask); } diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json index d9692ad075..08718a2e33 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json @@ -10,99 +10,66 @@ "Position" ], "samplers": [ - { - "name": "DiffuseSampler" - } + { "name": "DiffuseSampler" } ], "uniforms": [ - { - "name": "ProjMat", - "type": "matrix4x4", - "count": 16, - "values": [ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 - ] - }, - { - "name": "InSize", - "type": "float", - "count": 2, - "values": [ 1.0, 1.0 ] - }, - { - "name": "OutSize", - "type": "float", - "count": 2, - "values": [ 1.0, 1.0 ] - }, - { - "name": "BlackHole1X", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "BlackHole1Y", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "BlackHole2X", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "BlackHole2Y", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "BlackHole3X", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "BlackHole3Y", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "BlackHole4X", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "BlackHole4Y", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "BlackHoleCount", - "type": "float", - "count": 1, - "values": [ 0.0 ] - }, - { - "name": "LensStrength", - "type": "float", - "count": 1, - "values": [ 0.001953125 ] - }, - { - "name": "EventHorizonRadius", - "type": "float", - "count": 1, - "values": [ 0.083333333 ] - } + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "InSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, + { "name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, + + { "name": "BlackHole1X", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole1Y", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole2X", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole2Y", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole3X", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole3Y", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole4X", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole4Y", "type": "float", "count": 1, "values": [ 0.0 ] }, + + { "name": "BlackHole1Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "BlackHole2Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "BlackHole3Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "BlackHole4Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + + { "name": "PolyVerts[0]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[1]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[2]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[3]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[4]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[5]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[6]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[7]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[8]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[9]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[10]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[11]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[12]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[13]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[14]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[15]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[16]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[17]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[18]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[19]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[20]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[21]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[22]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + { "name": "PolyVerts[23]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, + + { "name": "PolyStart[0]", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "PolyStart[1]", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "PolyStart[2]", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "PolyStart[3]", "type": "float", "count": 1, "values": [ 0.0 ] }, + + { "name": "PolyCount[0]", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "PolyCount[1]", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "PolyCount[2]", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "PolyCount[3]", "type": "float", "count": 1, "values": [ 0.0 ] }, + + { "name": "BlackHoleCount", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "LensStrength", "type": "float", "count": 1, "values": [ 0.001953125 ] }, + { "name": "EventHorizonRadius", "type": "float", "count": 1, "values": [ 0.083333333 ] }, + { "name": "LensingShape", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "PerspectiveScale", "type": "float", "count": 1, "values": [ 10.0 ] } ] } From 58a18e9e4ad9dfe868243f539e99ff69691f367d Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 03:12:29 +0800 Subject: [PATCH 03/17] =?UTF-8?q?feat(render):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=AB=8B=E6=96=B9=E4=BD=93=E5=A4=9A=E8=BE=B9=E5=BD=A2=E7=BC=A9?= =?UTF-8?q?=E6=94=BE=E9=85=8D=E7=BD=AE=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在客户端配置中添加 cubicPolygonScale 参数用于控制立方体剪影放大比例 - 更新英语和英语倒置语言文件以支持新的配置选项 - 修改重力透镜着色器将距离转换回UV单位使重力强度与多边形大小成正比 - 调整立方体投影计算逻辑以使用多边形缩放参数 - 更新渲染层混合以传递多边形缩放配置值到透镜管理器 --- .../resources/assets/anvilcraft/lang/en_ud.json | 2 ++ .../resources/assets/anvilcraft/lang/en_us.json | 2 ++ .../client/support/GravitationalLensManager.java | 9 +++++---- .../dubhe/anvilcraft/config/AnvilCraftClientConfig.java | 3 +++ .../dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java | 4 +++- .../anvilcraft/shaders/program/gravitational_lens.fsh | 7 ++----- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index db554c479d..529ff3f280 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -125,6 +125,8 @@ "anvilcraft.configuration.clean_fluid_after_update_menger_sponge.tooltip": "ǝᵷuodS ɹǝᵷuǝW ᵷuᴉʇɐpdn ɹǝʇɟɐ pᴉnꞁɟ uɐǝꞁɔ oʇ ɹǝɥʇǝɥM", "anvilcraft.configuration.cubic_event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ ɔᴉqnƆ", "anvilcraft.configuration.cubic_event_horizon_radius.tooltip": "˙ɹǝᵷᵷᴉq = ⥝< ˙ɹɐꞁnɔɹᴉɔ sɐ ǝzᴉs ǝɯɐs = 0˙⥝ ˙snᴉpɐɹ Ʌ∩ uo ɹǝᴉꞁdᴉʇꞁnɯ uozᴉɹoɥ ǝpoɯ ƆIᗺ∩Ɔ", + "anvilcraft.configuration.cubic_polygon_scale": "ǝꞁɐɔS uoᵷʎꞁoԀ ɔᴉqnƆ", + "anvilcraft.configuration.cubic_polygon_scale.tooltip": "˙xᘔ = 0˙ᘔ 'ꞁǝpoɯ ꞁɐnʇɔɐ = 0˙⥝ ˙ᵷuᴉsuǝꞁ ɹoɟ ǝʇʇǝnoɥꞁᴉs ǝqnɔ ǝɥʇ sǝʇɐꞁɟuᴉ :ǝꞁɐɔs uoᵷʎꞁod ǝpoɯ ƆIᗺ∩Ɔ", "anvilcraft.configuration.display_anvil_animation": "uoᴉʇɐɯᴉuⱯ ꞁᴉʌuⱯ ʎɐꞁdsᴉᗡ", "anvilcraft.configuration.display_anvil_animation.tooltip": "uoᴉʇɐɯᴉuɐ ǝʇɐʇᴉʌǝꞁ ꞁᴉʌuɐ ʍoɥs pꞁnoɥS", "anvilcraft.configuration.do_not_show_tooltip_when_jade_present": "ʇuǝsǝɹԀ ǝpɐՐ uǝɥM dᴉʇꞁoo⟘ ʍoɥS ʇoN oᗡ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index a6719c55dc..6133edc878 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -125,6 +125,8 @@ "anvilcraft.configuration.clean_fluid_after_update_menger_sponge.tooltip": "Whether to clean fluid after updating Menger Sponge", "anvilcraft.configuration.cubic_event_horizon_radius": "Cubic Event Horizon Radius", "anvilcraft.configuration.cubic_event_horizon_radius.tooltip": "CUBIC mode horizon multiplier on UV radius. 1.0 = same size as circular. >1 = bigger.", + "anvilcraft.configuration.cubic_polygon_scale": "Cubic Polygon Scale", + "anvilcraft.configuration.cubic_polygon_scale.tooltip": "CUBIC mode polygon scale: inflates the cube silhouette for lensing. 1.0 = actual model, 2.0 = 2x.", "anvilcraft.configuration.display_anvil_animation": "Display Anvil Animation", "anvilcraft.configuration.display_anvil_animation.tooltip": "Should show anvil levitate animation", "anvilcraft.configuration.do_not_show_tooltip_when_jade_present": "Do Not Show Tooltip When Jade Present", diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index 373bf4c02b..665be5faab 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -166,12 +166,12 @@ static List convexHull2D(List points) { * Returns an empty list if the projection is degenerate. */ static List computeCubeProjectionHull( - BlockPos worldPos, Matrix4f viewProj + BlockPos worldPos, Matrix4f viewProj, double polygonScale ) { float cx = worldPos.getX() + 0.5f; float cy = worldPos.getY() + 0.5f; float cz = worldPos.getZ() + 0.5f; - float h = CUBE_HALF_EXTENT; + float h = CUBE_HALF_EXTENT * (float) polygonScale; List projected = new ArrayList<>(8); for (int ix = 0; ix < 2; ix++) { @@ -205,7 +205,8 @@ public static List collectVisibleBlackHoles( Camera camera, Matrix4f projectionMatrix, int maxCount, - AnvilCraftClientConfig.LensingShape shapeMode + AnvilCraftClientConfig.LensingShape shapeMode, + double polygonScale ) { List result = new ArrayList<>(); if (CLIENT_BLACK_HOLE_POSITIONS.isEmpty()) return result; @@ -227,7 +228,7 @@ public static List collectVisibleBlackHoles( List hull = Collections.emptyList(); if (shapeMode == AnvilCraftClientConfig.LensingShape.CUBIC) { - hull = computeCubeProjectionHull(pos, viewProj); + hull = computeCubeProjectionHull(pos, viewProj, polygonScale); } float[] pu, pv; diff --git a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java index edcfb71ba1..3514630613 100644 --- a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java +++ b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java @@ -57,6 +57,9 @@ public class AnvilCraftClientConfig { @Comment("Event horizon radius in CIRCULAR mode (screen UV units, 0.083 default)") public double eventHorizonRadius = 1.0 / 12.0; + @Comment("CUBIC mode polygon scale: inflates the cube silhouette for lensing. 1.0 = actual model, 2.0 = 2x.") + public double cubicPolygonScale = 2.0; + @Comment("CUBIC mode horizon multiplier on UV radius. 1.0 = same size as circular. >1 = bigger.") public double cubicEventHorizonRadius = 1.0; diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 533d12d23e..5496dc4ce5 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -197,7 +197,9 @@ void gravitationalLensPostProcess( // Collect visible black holes with optional polygon hull data java.util.List holes = - GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 4, shape); + GravitationalLensManager.collectVisibleBlackHoles( + camera, projectionMatrix, 4, shape, + AnvilCraftClient.CONFIG.cubicPolygonScale); int count = Math.min(holes.size(), 4); diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index da2bcdc7b6..1594fee23f 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -155,11 +155,8 @@ void main() { // Normalized t: 0 at center, 1 at boundary, >1 outside float t = distFromCenter / max(boundaryDist, 0.0001); - // Convert UV-based EventHorizonRadius to polygon-normalized threshold. - // horizonScale = how many polygon-radius-units the UV horizon covers. - float horizonScale = (EventHorizonRadius * perspScale) / max(avgPolyRadius, 0.00001); - // Map t so that t=horizonScale corresponds to the event horizon boundary. - effDist = t / max(horizonScale, 0.0001); + // Convert back to UV units so gravity strength is proportional to polygon size + effDist = t * avgPolyRadius; // Direction toward polygon center (in raw UV) dir = centerDir; } else { From 6c17460cc1e2fa37edbdad63e80a83d85d61490a Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 04:09:39 +0800 Subject: [PATCH 04/17] =?UTF-8?q?feat(render):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=AB=8B=E6=96=B9=E4=BD=93=E5=A4=9A=E8=BE=B9=E5=BD=A2=E7=BC=A9?= =?UTF-8?q?=E6=94=BE=E9=85=8D=E7=BD=AE=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在客户端配置中添加 cubicPolygonScale 参数用于控制立方体剪影放大比例 - 更新英语和英语倒置语言文件以支持新的配置选项 - 修改重力透镜着色器将距离转换回UV单位使重力强度与多边形大小成正比 - 调整立方体投影计算逻辑以使用多边形缩放参数 - 更新渲染层混合以传递多边形缩放配置值到透镜管理器 --- .../assets/anvilcraft/lang/en_ud.json | 8 +- .../assets/anvilcraft/lang/en_us.json | 8 +- .../support/GravitationalLensManager.java | 164 ++------------- .../config/AnvilCraftClientConfig.java | 16 +- .../anvilcraft/mixin/LevelRendererMixin.java | 41 +--- .../shaders/program/gravitational_lens.fsh | 188 ++---------------- .../shaders/program/gravitational_lens.json | 36 ---- 7 files changed, 41 insertions(+), 420 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 529ff3f280..05d318f012 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -123,10 +123,6 @@ "anvilcraft.configuration.chute_max_cooldown.tooltip": "(sʞɔᴉʇ uᴉ) ǝʇnɥɔ ɟo ǝɯᴉʇ uʍopꞁooɔ ɯnɯᴉxɐW", "anvilcraft.configuration.clean_fluid_after_update_menger_sponge": "ǝᵷuodS ɹǝᵷuǝW ǝʇɐpd∩ ɹǝʇɟⱯ pᴉnꞁℲ uɐǝꞁƆ", "anvilcraft.configuration.clean_fluid_after_update_menger_sponge.tooltip": "ǝᵷuodS ɹǝᵷuǝW ᵷuᴉʇɐpdn ɹǝʇɟɐ pᴉnꞁɟ uɐǝꞁɔ oʇ ɹǝɥʇǝɥM", - "anvilcraft.configuration.cubic_event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ ɔᴉqnƆ", - "anvilcraft.configuration.cubic_event_horizon_radius.tooltip": "˙ɹǝᵷᵷᴉq = ⥝< ˙ɹɐꞁnɔɹᴉɔ sɐ ǝzᴉs ǝɯɐs = 0˙⥝ ˙snᴉpɐɹ Ʌ∩ uo ɹǝᴉꞁdᴉʇꞁnɯ uozᴉɹoɥ ǝpoɯ ƆIᗺ∩Ɔ", - "anvilcraft.configuration.cubic_polygon_scale": "ǝꞁɐɔS uoᵷʎꞁoԀ ɔᴉqnƆ", - "anvilcraft.configuration.cubic_polygon_scale.tooltip": "˙xᘔ = 0˙ᘔ 'ꞁǝpoɯ ꞁɐnʇɔɐ = 0˙⥝ ˙ᵷuᴉsuǝꞁ ɹoɟ ǝʇʇǝnoɥꞁᴉs ǝqnɔ ǝɥʇ sǝʇɐꞁɟuᴉ :ǝꞁɐɔs uoᵷʎꞁod ǝpoɯ ƆIᗺ∩Ɔ", "anvilcraft.configuration.display_anvil_animation": "uoᴉʇɐɯᴉuⱯ ꞁᴉʌuⱯ ʎɐꞁdsᴉᗡ", "anvilcraft.configuration.display_anvil_animation.tooltip": "uoᴉʇɐɯᴉuɐ ǝʇɐʇᴉʌǝꞁ ꞁᴉʌuɐ ʍoɥs pꞁnoɥS", "anvilcraft.configuration.do_not_show_tooltip_when_jade_present": "ʇuǝsǝɹԀ ǝpɐՐ uǝɥM dᴉʇꞁoo⟘ ʍoɥS ʇoN oᗡ", @@ -134,7 +130,7 @@ "anvilcraft.configuration.ember_anvil_beyond_max_level": "ꞁǝʌǝꞀ xɐW puoʎǝᗺ ꞁᴉʌuⱯ ɹǝqɯƎ", "anvilcraft.configuration.ember_anvil_beyond_max_level.tooltip": "ꞁᴉʌuⱯ ɹǝqɯƎ uᴉ ꞁǝʌǝꞁ xɐɯ puoʎǝq sʞooᗺ pǝʇuɐɥɔuƎ ɥʇᴉʍ sɯǝʇᴉ ᵷuᴉuᴉqɯoƆ", "anvilcraft.configuration.event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ", - "anvilcraft.configuration.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'sʇᴉun Ʌ∩ uǝǝɹɔs) ǝpoɯ ᴚⱯꞀ∩ƆᴚIƆ uᴉ snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", + "anvilcraft.configuration.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'sʇᴉun Ʌ∩ uǝǝɹɔs) snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", "anvilcraft.configuration.felling_block_per_level": "ꞁǝʌǝꞀ ɹǝԀ ʞɔoꞁᗺ ᵷuᴉꞁꞁǝℲ", "anvilcraft.configuration.felling_block_per_level.tooltip": "ʇuǝɯʇuɐɥɔuǝ ᵷuᴉꞁꞁǝℲ ɟo ꞁǝʌǝꞁ ɹǝd ʇnɔ ǝq uɐɔ ʇɐɥʇ sᵷoꞁ ɟo ɹǝqɯnu ɯnɯᴉxɐɯ ǝɥ⟘", "anvilcraft.configuration.frost_anvil_beyond_max_level": "ꞁǝʌǝꞀ xɐW puoʎǝᗺ ꞁᴉʌuⱯ ʇsoɹℲ", @@ -188,8 +184,6 @@ "anvilcraft.configuration.lens_perspective_scale.tooltip": "˙ɹǝᵷᵷᴉq = ɹǝsoꞁƆ ˙ǝzᴉs ᵷᴉɟuoɔ = ʇɔǝɟɟǝ 'ǝɔuɐʇsᴉp sᴉɥʇ ʇⱯ ˙ᵷuᴉꞁɐɔs ǝʌᴉʇɔǝdsɹǝd ɹoɟ ǝɔuɐʇsᴉp ǝɔuǝɹǝɟǝᴚ", "anvilcraft.configuration.lens_strength": "ɥʇᵷuǝɹʇS suǝꞀ", "anvilcraft.configuration.lens_strength.tooltip": "(ʇꞁnɐɟǝp ᘔ00˙0 'ᵷuᴉpuǝq ɹǝᵷuoɹʇs = ɹǝɥᵷᴉɥ) ɥʇᵷuǝɹʇs uoᴉʇɹoʇsᴉp suǝꞀ", - "anvilcraft.configuration.lensing_shape": "ǝdɐɥS ᵷuᴉsuǝꞀ", - "anvilcraft.configuration.lensing_shape.tooltip": "(ǝꞁᵷuɐ ʍǝᴉʌ ɯoɹɟ ǝʇʇǝnoɥꞁᴉs ǝqnɔ sʍoꞁꞁoɟ) ƆIᗺ∩Ɔ ɹo (ɹǝpɐɥs ǝɔuǝɹǝɟǝɹ ǝʞᴉꞁ 'punoɹ) ᴚⱯꞀ∩ƆᴚIƆ :ǝdɐɥs ᵷuᴉsuǝꞀ", "anvilcraft.configuration.lightning_strike_depth": "ɥʇdǝᗡ ǝʞᴉɹʇS ᵷuᴉuʇɥᵷᴉꞀ", "anvilcraft.configuration.lightning_strike_depth.tooltip": "ɥɔɐǝɹ uɐɔ ǝʞᴉɹʇs ᵷuᴉuʇɥᵷᴉꞁ ɐ ɥʇdǝp ɯnɯᴉxɐW", "anvilcraft.configuration.lightning_strike_radius": "snᴉpɐᴚ ǝʞᴉɹʇS ᵷuᴉuʇɥᵷᴉꞀ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 6133edc878..cb904f9fb7 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -123,10 +123,6 @@ "anvilcraft.configuration.chute_max_cooldown.tooltip": "Maximum cooldown time of chute (in ticks)", "anvilcraft.configuration.clean_fluid_after_update_menger_sponge": "Clean Fluid After Update Menger Sponge", "anvilcraft.configuration.clean_fluid_after_update_menger_sponge.tooltip": "Whether to clean fluid after updating Menger Sponge", - "anvilcraft.configuration.cubic_event_horizon_radius": "Cubic Event Horizon Radius", - "anvilcraft.configuration.cubic_event_horizon_radius.tooltip": "CUBIC mode horizon multiplier on UV radius. 1.0 = same size as circular. >1 = bigger.", - "anvilcraft.configuration.cubic_polygon_scale": "Cubic Polygon Scale", - "anvilcraft.configuration.cubic_polygon_scale.tooltip": "CUBIC mode polygon scale: inflates the cube silhouette for lensing. 1.0 = actual model, 2.0 = 2x.", "anvilcraft.configuration.display_anvil_animation": "Display Anvil Animation", "anvilcraft.configuration.display_anvil_animation.tooltip": "Should show anvil levitate animation", "anvilcraft.configuration.do_not_show_tooltip_when_jade_present": "Do Not Show Tooltip When Jade Present", @@ -134,7 +130,7 @@ "anvilcraft.configuration.ember_anvil_beyond_max_level": "Ember Anvil Beyond Max Level", "anvilcraft.configuration.ember_anvil_beyond_max_level.tooltip": "Combining items with Enchanted Books beyond max level in Ember Anvil", "anvilcraft.configuration.event_horizon_radius": "Event Horizon Radius", - "anvilcraft.configuration.event_horizon_radius.tooltip": "Event horizon radius in CIRCULAR mode (screen UV units, 0.083 default)", + "anvilcraft.configuration.event_horizon_radius.tooltip": "Event horizon radius (screen UV units, 0.083 default)", "anvilcraft.configuration.felling_block_per_level": "Felling Block Per Level", "anvilcraft.configuration.felling_block_per_level.tooltip": "The maximum number of logs that can be cut per level of Felling enchantment", "anvilcraft.configuration.frost_anvil_beyond_max_level": "Frost Anvil Beyond Max Level", @@ -188,8 +184,6 @@ "anvilcraft.configuration.lens_perspective_scale.tooltip": "Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.", "anvilcraft.configuration.lens_strength": "Lens Strength", "anvilcraft.configuration.lens_strength.tooltip": "Lens distortion strength (higher = stronger bending, 0.002 default)", - "anvilcraft.configuration.lensing_shape": "Lensing Shape", - "anvilcraft.configuration.lensing_shape.tooltip": "Lensing shape: CIRCULAR (round, like reference shader) or CUBIC (follows cube silhouette from view angle)", "anvilcraft.configuration.lightning_strike_depth": "Lightning Strike Depth", "anvilcraft.configuration.lightning_strike_depth.tooltip": "Maximum depth a lightning strike can reach", "anvilcraft.configuration.lightning_strike_radius": "Lightning Strike Radius", diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index 665be5faab..c980a62bd0 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -1,6 +1,5 @@ package dev.dubhe.anvilcraft.client.support; -import dev.dubhe.anvilcraft.config.AnvilCraftClientConfig; import net.minecraft.client.Camera; import net.minecraft.core.BlockPos; import org.joml.Matrix4f; @@ -10,20 +9,14 @@ import org.joml.Vector4f; import java.util.ArrayList; -import java.util.Comparator; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class GravitationalLensManager { - private static final int MAX_BLACK_HOLES = 4; - private static final int MAX_POLY_VERTS = 6; private static final int MAX_SEARCH_DISTANCE_SQR = 256 * 256; - /** Half-extent of the black hole block model: box(4,4,4,12,12,12) → (12-4)/2/16 = 0.25 */ - private static final float CUBE_HALF_EXTENT = 0.25f; - /** Client-side cache of loaded black hole block positions. */ public static final Set CLIENT_BLACK_HOLE_POSITIONS = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -37,35 +30,19 @@ public static void unregister(BlockPos pos) { } /** - * Full per-hole data passed to the shader. + * Per-hole data passed to the shader. */ public static final class HoleProjection { /** Center UV of the black hole on screen. */ public final float centerU; public final float centerV; - public final boolean onScreen; /** Distance from camera to black hole (world units). */ public final float cameraDistance; - /** Number of convex-hull polygon vertices (0 when circular mode or degenerate). */ - public final int polyVertCount; - /** UV coordinates of polygon vertices, tightly packed. Length = polyVertCount. */ - public final float[] polyU; - public final float[] polyV; - - HoleProjection(float cu, float cv, boolean onScreen, float dist, - int vertCount, float[] pu, float[] pv) { + HoleProjection(float cu, float cv, float dist) { this.centerU = cu; this.centerV = cv; - this.onScreen = onScreen; this.cameraDistance = dist; - this.polyVertCount = vertCount; - this.polyU = pu; - this.polyV = pv; - } - - static HoleProjection offScreen() { - return new HoleProjection(0, 0, false, 0, 0, new float[0], new float[0]); } } @@ -90,123 +67,36 @@ private static Matrix4f buildViewProj(Camera camera, Matrix4f projectionMatrix) } /** - * Transform a world-space point to NDC, then to screen UV. - * Returns null if the point is behind the camera or off-screen. + * Transform a world-space point to screen UV via view-projection. + * Vertices behind the camera (clip.w ≤ 0) are mirrored so they still + * project to valid screen positions. */ private static Vector2f worldToScreenUV(float wx, float wy, float wz, Matrix4f viewProj) { Vector4f clip = viewProj.transform(new Vector4f(wx, wy, wz, 1.0f)); - if (clip.w <= 0.0f) return null; + + if (clip.w <= 0.0f) { + clip.x = -clip.x; + clip.y = -clip.y; + clip.z = -clip.z; + clip.w = -clip.w; + } float ndcX = clip.x / clip.w; float ndcY = clip.y / clip.w; - float ndcZ = clip.z / clip.w; - if (ndcZ < -1.0f || ndcZ > 1.0f) return null; - if (ndcX < -1.0f || ndcX > 1.0f || ndcY < -1.0f || ndcY > 1.0f) return null; + ndcX = Math.max(-1.0f, Math.min(1.0f, ndcX)); + ndcY = Math.max(-1.0f, Math.min(1.0f, ndcY)); return new Vector2f((ndcX + 1.0f) / 2.0f, (ndcY + 1.0f) / 2.0f); } /** - * Compute the 2D convex hull of points using Andrew's monotone chain. - * Returns vertices in CCW order. Max 6 for a cube projection. - */ - static List convexHull2D(List points) { - if (points.size() <= 2) return new ArrayList<>(points); - - // Remove exact duplicates - List unique = new ArrayList<>(); - for (Vector2f p : points) { - boolean dup = false; - for (Vector2f u : unique) { - if (Math.abs(u.x - p.x) < 0.0001f && Math.abs(u.y - p.y) < 0.0001f) { - dup = true; - break; - } - } - if (!dup) unique.add(p); - } - if (unique.size() <= 2) return unique; - - unique.sort(Comparator.comparingDouble(v -> v.x) - .thenComparingDouble(v -> v.y)); - - List hull = new ArrayList<>(); - - // Lower hull - for (Vector2f p : unique) { - while (hull.size() >= 2) { - Vector2f a = hull.get(hull.size() - 2); - Vector2f b = hull.get(hull.size() - 1); - if ((b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x) > 0) break; - hull.remove(hull.size() - 1); - } - hull.add(p); - } - - // Upper hull - int lowerSize = hull.size(); - for (int i = unique.size() - 2; i >= 0; i--) { - Vector2f p = unique.get(i); - while (hull.size() > lowerSize) { - Vector2f a = hull.get(hull.size() - 2); - Vector2f b = hull.get(hull.size() - 1); - if ((b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x) > 0) break; - hull.remove(hull.size() - 1); - } - hull.add(p); - } - - hull.remove(hull.size() - 1); // Remove duplicate - return hull; - } - - /** - * Project the 8 vertices of the cube model and compute the 2D convex hull. - * Returns an empty list if the projection is degenerate. - */ - static List computeCubeProjectionHull( - BlockPos worldPos, Matrix4f viewProj, double polygonScale - ) { - float cx = worldPos.getX() + 0.5f; - float cy = worldPos.getY() + 0.5f; - float cz = worldPos.getZ() + 0.5f; - float h = CUBE_HALF_EXTENT * (float) polygonScale; - - List projected = new ArrayList<>(8); - for (int ix = 0; ix < 2; ix++) { - float x = cx + (ix == 0 ? -h : h); - for (int iy = 0; iy < 2; iy++) { - float y = cy + (iy == 0 ? -h : h); - for (int iz = 0; iz < 2; iz++) { - float z = cz + (iz == 0 ? -h : h); - Vector2f uv = worldToScreenUV(x, y, z, viewProj); - if (uv != null) projected.add(uv); - } - } - } - - if (projected.size() < 3) return projected; // degenerate - - List hull = convexHull2D(projected); - // Limit to MAX_POLY_VERTS - if (hull.size() > MAX_POLY_VERTS) { - hull = hull.subList(0, MAX_POLY_VERTS); - } - return hull; - } - - /** - * Collect up to {@code maxCount} on-screen black holes with optional polygon hull data. - * - * @param shapeMode CIRCULAR or CUBIC — determines whether polygon hulls are computed. + * Collect up to {@code maxCount} visible black holes sorted by distance. */ public static List collectVisibleBlackHoles( Camera camera, Matrix4f projectionMatrix, - int maxCount, - AnvilCraftClientConfig.LensingShape shapeMode, - double polygonScale + int maxCount ) { List result = new ArrayList<>(); if (CLIENT_BLACK_HOLE_POSITIONS.isEmpty()) return result; @@ -220,33 +110,13 @@ public static List collectVisibleBlackHoles( double dz = pos.getZ() + 0.5 - cameraPos.z; if (dx * dx + dy * dy + dz * dz > MAX_SEARCH_DISTANCE_SQR) continue; - // Project center Vector2f centerUV = worldToScreenUV( pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f, viewProj ); if (centerUV == null) continue; - List hull = Collections.emptyList(); - if (shapeMode == AnvilCraftClientConfig.LensingShape.CUBIC) { - hull = computeCubeProjectionHull(pos, viewProj, polygonScale); - } - - float[] pu, pv; - int vertCount = hull.size(); - if (vertCount > 0) { - pu = new float[vertCount]; - pv = new float[vertCount]; - for (int i = 0; i < vertCount; i++) { - pu[i] = hull.get(i).x; - pv[i] = hull.get(i).y; - } - } else { - pu = new float[0]; - pv = new float[0]; - } - float dist = (float) Math.sqrt(dx * dx + dy * dy + dz * dz); - result.add(new HoleProjection(centerUV.x, centerUV.y, true, dist, vertCount, pu, pv)); + result.add(new HoleProjection(centerUV.x, centerUV.y, dist)); if (result.size() >= maxCount) break; } return result; diff --git a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java index 3514630613..641ff7efb2 100644 --- a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java +++ b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java @@ -48,29 +48,15 @@ public class AnvilCraftClientConfig { @Comment("Gravitational lensing post-processing effect near black holes") public boolean renderBlackHoleLensing = true; - @Comment("Lensing shape: CIRCULAR (round, like reference shader) or CUBIC (follows cube silhouette from view angle)") - public LensingShape lensingShape = LensingShape.CIRCULAR; - @Comment("Lens distortion strength (higher = stronger bending, 0.002 default)") public double lensStrength = 1.0 / 512.0; - @Comment("Event horizon radius in CIRCULAR mode (screen UV units, 0.083 default)") + @Comment("Event horizon radius (screen UV units, 0.083 default)") public double eventHorizonRadius = 1.0 / 12.0; - @Comment("CUBIC mode polygon scale: inflates the cube silhouette for lensing. 1.0 = actual model, 2.0 = 2x.") - public double cubicPolygonScale = 2.0; - - @Comment("CUBIC mode horizon multiplier on UV radius. 1.0 = same size as circular. >1 = bigger.") - public double cubicEventHorizonRadius = 1.0; - @Comment("Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.") public double lensPerspectiveScale = 10.0; - public enum LensingShape { - CIRCULAR, - CUBIC - } - @Comment("A vertical item frame vertically displays items") public boolean verticalItemFrame = false; diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 5496dc4ce5..62346cd95b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -13,7 +13,6 @@ import com.mojang.blaze3d.vertex.VertexFormat; import dev.dubhe.anvilcraft.api.rendering.CacheableBERenderingPipeline; import dev.dubhe.anvilcraft.client.AnvilCraftClient; -import dev.dubhe.anvilcraft.config.AnvilCraftClientConfig; import dev.dubhe.anvilcraft.client.init.ModRenderTargets; import dev.dubhe.anvilcraft.client.init.ModShaders; import dev.dubhe.anvilcraft.client.renderer.RenderState; @@ -193,13 +192,9 @@ void gravitationalLensPostProcess( PostPass pass = passes.get(0); - AnvilCraftClientConfig.LensingShape shape = AnvilCraftClient.CONFIG.lensingShape; - - // Collect visible black holes with optional polygon hull data + // Collect visible black holes java.util.List holes = - GravitationalLensManager.collectVisibleBlackHoles( - camera, projectionMatrix, 4, shape, - AnvilCraftClient.CONFIG.cubicPolygonScale); + GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 4); int count = Math.min(holes.size(), 4); @@ -220,39 +215,11 @@ void gravitationalLensPostProcess( } } - // Set polygon vertex data (tightly packed across all holes) - int globalVertIdx = 0; - for (int i = 0; i < 4; i++) { - pass.getEffect().safeGetUniform("PolyStart[" + i + "]").set((float) globalVertIdx); - if (i < count && shape == AnvilCraftClientConfig.LensingShape.CUBIC) { - GravitationalLensManager.HoleProjection h = holes.get(i); - int vc = h.polyVertCount; - pass.getEffect().safeGetUniform("PolyCount[" + i + "]").set((float) vc); - for (int j = 0; j < vc && globalVertIdx < 24; j++) { - String uName = "PolyVerts[" + globalVertIdx + "]"; - pass.getEffect().safeGetUniform(uName).set(h.polyU[j], h.polyV[j]); - globalVertIdx++; - } - } else { - pass.getEffect().safeGetUniform("PolyCount[" + i + "]").set(0.0f); - } - } - // Zero out remaining poly verts - for (int k = globalVertIdx; k < 24; k++) { - pass.getEffect().safeGetUniform("PolyVerts[" + k + "]").set(0.0f, 0.0f); - } - - // Cubic mode: cubicEventHorizonRadius acts as a multiplier on the UV-based radius - float horizonRadius = (float) (AnvilCraftClient.CONFIG.eventHorizonRadius - * (shape == AnvilCraftClientConfig.LensingShape.CUBIC - ? AnvilCraftClient.CONFIG.cubicEventHorizonRadius : 1.0)); - pass.getEffect().safeGetUniform("BlackHoleCount").set((float) count); pass.getEffect().safeGetUniform("LensStrength") .set((float) AnvilCraftClient.CONFIG.lensStrength); - pass.getEffect().safeGetUniform("EventHorizonRadius").set(horizonRadius); - pass.getEffect().safeGetUniform("LensingShape") - .set(shape == AnvilCraftClientConfig.LensingShape.CUBIC ? 1.0f : 0.0f); + pass.getEffect().safeGetUniform("EventHorizonRadius") + .set((float) AnvilCraftClient.CONFIG.eventHorizonRadius); pass.getEffect().safeGetUniform("PerspectiveScale") .set((float) AnvilCraftClient.CONFIG.lensPerspectiveScale); diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index 1594fee23f..2a0207c8fc 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -19,16 +19,10 @@ uniform float BlackHole2Dist; uniform float BlackHole3Dist; uniform float BlackHole4Dist; -// Polygon vertex data for cubic lensing (tightly packed for all holes, max 24) -uniform vec2 PolyVerts[24]; -uniform float PolyStart[4]; -uniform float PolyCount[4]; - uniform float BlackHoleCount; uniform float LensStrength; uniform float EventHorizonRadius; -uniform float LensingShape; // 0 = CIRCULAR, 1 = CUBIC -uniform float PerspectiveScale; // reference distance for perspective (default 10.0) +uniform float PerspectiveScale; // reference distance (default 10.0) in vec2 texCoord; out vec4 fragColor; @@ -47,58 +41,6 @@ float getHoleDist(int i) { return BlackHole4Dist; } -// Signed distance to convex polygon: negative inside, positive outside. -float sdPolygon(vec2 p, int startIdx, int vertCount) { - if (vertCount < 3) return 1.0; - float d = 1e10; - int posCount = 0; - int negCount = 0; - - for (int j = 0; j < 6; j++) { - if (j >= vertCount) break; - int jNext = (j + 1) % vertCount; - vec2 a = PolyVerts[startIdx + j]; - vec2 b = PolyVerts[startIdx + jNext]; - vec2 e = b - a; - vec2 ap = p - a; - - // Cross product sign per edge - float crossVal = e.x * ap.y - e.y * ap.x; - if (crossVal > 0.0) posCount++; - else if (crossVal < 0.0) negCount++; - - // Distance to this edge segment - float t = clamp(dot(ap, e) / max(dot(e, e), 0.00001), 0.0, 1.0); - vec2 closest = a + t * e; - d = min(d, length(p - closest)); - } - - // Inside iff all cross products have same sign (convex polygon) - float inside = (posCount == vertCount || negCount == vertCount) ? -1.0 : 1.0; - return d * inside; -} - -// Distance from origin along `dir` to polygon boundary. -// Returns a large number if no intersection is found. -float rayToPolygon(vec2 origin, vec2 dir, int startIdx, int vertCount) { - float minT = 1e10; - for (int j = 0; j < 6; j++) { - if (j >= vertCount) break; - int jNext = (j + 1) % vertCount; - vec2 a = PolyVerts[startIdx + j]; - vec2 b = PolyVerts[startIdx + jNext]; - vec2 e = b - a; - float det = dir.x * e.y - dir.y * e.x; - if (abs(det) < 0.00001) continue; - float t = ((a.x - origin.x) * e.y - (a.y - origin.y) * e.x) / det; - float s = ((a.x - origin.x) * dir.y - (a.y - origin.y) * dir.x) / (-det); - if (t > 0.0 && s >= 0.0 && s <= 1.0) { - minT = min(minT, t); - } - } - return minT; -} - void main() { vec2 uv = texCoord; float aspectRatio = InSize.x / InSize.y; @@ -106,137 +48,41 @@ void main() { vec2 offset = vec2(0.0); int count = int(BlackHoleCount); + // --- Gravitational displacement --- for (int i = 0; i < 4; i++) { if (i >= count) break; vec2 holeUv = getHolePos(i); - bool isCubic = LensingShape > 0.5; - int pStart = int(PolyStart[i]); - int pCount = int(PolyCount[i]); + vec2 toHole = holeUv - uv; + toHole.x *= aspectRatio; + float dist = length(toHole); + + if (dist < 0.0001) continue; + vec2 dir = toHole / dist; float perspScale = PerspectiveScale / max(getHoleDist(i), 0.1); - float effDist; - vec2 dir; - - if (isCubic && pCount >= 3) { - // --- Cubic mode: polygon-shaped gravitational field --- - - // Compute polygon center - vec2 polyCenter = vec2(0.0); - for (int j = 0; j < 6; j++) { - if (j >= pCount) break; - polyCenter += PolyVerts[pStart + j]; - } - polyCenter /= float(pCount); - - // Compute average polygon radius in UV units - float avgPolyRadius = 0.0; - for (int j = 0; j < 6; j++) { - if (j >= pCount) break; - avgPolyRadius += length(PolyVerts[pStart + j] - polyCenter); - } - avgPolyRadius /= float(pCount); - - // Direction and distance from polygon center to current pixel - vec2 fromCenter = uv - polyCenter; - float distFromCenter = length(fromCenter); - - if (distFromCenter < 0.0001) continue; - - vec2 centerDir = fromCenter / distFromCenter; - - // Distance from center to polygon boundary in this direction - float boundaryDist = rayToPolygon(polyCenter, centerDir, pStart, pCount); - if (boundaryDist >= 1e9) { - boundaryDist = avgPolyRadius; - } - - // Normalized t: 0 at center, 1 at boundary, >1 outside - float t = distFromCenter / max(boundaryDist, 0.0001); - - // Convert back to UV units so gravity strength is proportional to polygon size - effDist = t * avgPolyRadius; - // Direction toward polygon center (in raw UV) - dir = centerDir; - } else { - // --- Circular mode --- - vec2 toHole = holeUv - uv; - toHole.x *= aspectRatio; - float dist = length(toHole); - - if (dist < 0.0001) continue; - - effDist = dist; - dir = toHole / dist; - } - - // Inverse-square gravitational pull (scaled by perspective) - float distSqr = effDist * effDist; - float gravity = LensStrength * perspScale / distSqr; - - // Mask out within event horizon (scaled by perspective) + float gravity = LensStrength * perspScale / (dist * dist); float scaledHorizon = EventHorizonRadius * perspScale; - float mask = step(scaledHorizon, effDist); + float mask = step(scaledHorizon, dist); vec2 lensOffset = dir * gravity * mask; - - if (!isCubic || pCount < 3) { - // Circular mode: convert aspect-ratio back - lensOffset.x /= aspectRatio; - } - // Cubic mode: offset is already in raw UV space, no conversion needed - + lensOffset.x /= aspectRatio; offset += lensOffset; } vec3 color = texture(DiffuseSampler, uv + offset).rgb; - // Render event horizon for each black hole + // --- Render event horizon --- for (int i = 0; i < 4; i++) { if (i >= count) break; vec2 holeUv = getHolePos(i); - bool isCubic = LensingShape > 0.5; - int pStart = int(PolyStart[i]); - int pCount = int(PolyCount[i]); - - float horizonMask = 0.0; - - if (isCubic && pCount >= 3) { - // Polygon-shaped event horizon. - vec2 polyCenter = vec2(0.0); - for (int j = 0; j < 6; j++) { - if (j >= pCount) break; - polyCenter += PolyVerts[pStart + j]; - } - polyCenter /= float(pCount); - - // Average polygon radius in UV units - float avgPolyRadius = 0.0; - for (int j = 0; j < 6; j++) { - if (j >= pCount) break; - avgPolyRadius += length(PolyVerts[pStart + j] - polyCenter); - } - avgPolyRadius /= float(pCount); - - // Convert UV-based horizon to polygon fraction - float perspS = PerspectiveScale / max(getHoleDist(i), 0.1); - float horizonUv = EventHorizonRadius * perspS; - float r = clamp(horizonUv / max(avgPolyRadius, 0.00001), 0.0, 0.99); - - float scale = 1.0 / max(1.0 - r, 0.01); - vec2 testPt = polyCenter + (uv - polyCenter) * scale; - float sd = sdPolygon(testPt, pStart, pCount); - horizonMask = step(sd, 0.0); - } else { - // Circular event horizon (aspect-ratio corrected, perspective scaled) - float perspS = PerspectiveScale / max(getHoleDist(i), 0.1); - vec2 toHole = uv - holeUv; - toHole.x *= aspectRatio; - float dist = length(toHole); - horizonMask = 1.0 - step(EventHorizonRadius * perspS, dist); - } + float perspS = PerspectiveScale / max(getHoleDist(i), 0.1); + vec2 toHole = uv - holeUv; + toHole.x *= aspectRatio; + float dist = length(toHole); + float horizonMask = 1.0 - step(EventHorizonRadius * perspS, dist); color = mix(color, vec3(0.0, 0.0, 0.0), horizonMask); } diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json index 08718a2e33..1fc28c682f 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json @@ -31,45 +31,9 @@ { "name": "BlackHole3Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "BlackHole4Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "PolyVerts[0]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[1]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[2]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[3]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[4]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[5]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[6]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[7]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[8]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[9]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[10]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[11]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[12]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[13]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[14]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[15]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[16]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[17]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[18]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[19]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[20]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[21]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[22]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - { "name": "PolyVerts[23]", "type": "float", "count": 2, "values": [ 0.0, 0.0 ] }, - - { "name": "PolyStart[0]", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "PolyStart[1]", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "PolyStart[2]", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "PolyStart[3]", "type": "float", "count": 1, "values": [ 0.0 ] }, - - { "name": "PolyCount[0]", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "PolyCount[1]", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "PolyCount[2]", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "PolyCount[3]", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHoleCount", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "LensStrength", "type": "float", "count": 1, "values": [ 0.001953125 ] }, { "name": "EventHorizonRadius", "type": "float", "count": 1, "values": [ 0.083333333 ] }, - { "name": "LensingShape", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "PerspectiveScale", "type": "float", "count": 1, "values": [ 10.0 ] } ] } From 95774f468d76b204126ac41ad74bb1523054c9fa Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 04:23:57 +0800 Subject: [PATCH 05/17] =?UTF-8?q?refactor(render):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E9=85=8D=E7=BD=AE=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将引力透镜相关配置项整合到 GravitationalLens 类中 - 移除旧的独立配置字段并创建新的嵌套配置结构 - 更新着色器程序中的参数访问路径 - 修改渲染状态检查逻辑以使用新的配置结构 - 更新英语和反向显示语言文件中的配置键名 - 移除不再使用的独立透镜配置项的翻译条目 --- .../assets/anvilcraft/lang/en_ud.json | 19 ++++++++++------- .../assets/anvilcraft/lang/en_us.json | 19 ++++++++++------- .../client/renderer/RenderState.java | 2 +- .../config/AnvilCraftClientConfig.java | 21 ++++++++++++------- .../anvilcraft/mixin/LevelRendererMixin.java | 6 +++--- .../shaders/program/gravitational_lens.fsh | 4 +--- 6 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 05d318f012..84e8e9b8d0 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -129,8 +129,6 @@ "anvilcraft.configuration.do_not_show_tooltip_when_jade_present.tooltip": "ʇuǝsǝɹd ǝpɐɾ uǝɥʍ dᴉʇꞁooʇ ʇuǝuodɯoɔ ɹǝʍod ɹǝpuǝɹ ʇou oᗡ", "anvilcraft.configuration.ember_anvil_beyond_max_level": "ꞁǝʌǝꞀ xɐW puoʎǝᗺ ꞁᴉʌuⱯ ɹǝqɯƎ", "anvilcraft.configuration.ember_anvil_beyond_max_level.tooltip": "ꞁᴉʌuⱯ ɹǝqɯƎ uᴉ ꞁǝʌǝꞁ xɐɯ puoʎǝq sʞooᗺ pǝʇuɐɥɔuƎ ɥʇᴉʍ sɯǝʇᴉ ᵷuᴉuᴉqɯoƆ", - "anvilcraft.configuration.event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ", - "anvilcraft.configuration.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'sʇᴉun Ʌ∩ uǝǝɹɔs) snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", "anvilcraft.configuration.felling_block_per_level": "ꞁǝʌǝꞀ ɹǝԀ ʞɔoꞁᗺ ᵷuᴉꞁꞁǝℲ", "anvilcraft.configuration.felling_block_per_level.tooltip": "ʇuǝɯʇuɐɥɔuǝ ᵷuᴉꞁꞁǝℲ ɟo ꞁǝʌǝꞁ ɹǝd ʇnɔ ǝq uɐɔ ʇɐɥʇ sᵷoꞁ ɟo ɹǝqɯnu ɯnɯᴉxɐɯ ǝɥ⟘", "anvilcraft.configuration.frost_anvil_beyond_max_level": "ꞁǝʌǝꞀ xɐW puoʎǝᗺ ꞁᴉʌuⱯ ʇsoɹℲ", @@ -147,6 +145,17 @@ "anvilcraft.configuration.giant_anvil_max_shock_radius.tooltip": "ɹoᴉʌɐɥǝq ʞɔoɥs s,ꞁᴉʌuɐ ʇuɐᴉᵷ ɟo snᴉpɐɹ ɯnɯᴉxɐW", "anvilcraft.configuration.goggle_mode": "ǝpoW ǝꞁᵷᵷo⅁", "anvilcraft.configuration.goggle_mode.tooltip": "oɟuᴉ ǝꞁᵷᵷoᵷ ɹǝɯɯɐɥ ꞁᴉʌuɐ ǝɥʇ ɟo ǝpoɯ ǝɥ⟘", + "anvilcraft.configuration.gravitational_lens": "suǝꞀ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", + "anvilcraft.configuration.gravitational_lens.button": "suǝꞀ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", + "anvilcraft.configuration.gravitational_lens.event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ", + "anvilcraft.configuration.gravitational_lens.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'sʇᴉun Ʌ∩ uǝǝɹɔs) snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", + "anvilcraft.configuration.gravitational_lens.lens_perspective_scale": "ǝꞁɐɔS ǝʌᴉʇɔǝdsɹǝԀ suǝꞀ", + "anvilcraft.configuration.gravitational_lens.lens_perspective_scale.tooltip": "˙ɹǝᵷᵷᴉq = ɹǝsoꞁƆ ˙ǝzᴉs ᵷᴉɟuoɔ = ʇɔǝɟɟǝ 'ǝɔuɐʇsᴉp sᴉɥʇ ʇⱯ ˙ᵷuᴉꞁɐɔs ǝʌᴉʇɔǝdsɹǝd ɹoɟ ǝɔuɐʇsᴉp ǝɔuǝɹǝɟǝᴚ", + "anvilcraft.configuration.gravitational_lens.lens_strength": "ɥʇᵷuǝɹʇS suǝꞀ", + "anvilcraft.configuration.gravitational_lens.lens_strength.tooltip": "(ʇꞁnɐɟǝp ᘔ00˙0 'ᵷuᴉpuǝq ɹǝᵷuoɹʇs = ɹǝɥᵷᴉɥ) ɥʇᵷuǝɹʇs uoᴉʇɹoʇsᴉp suǝꞀ", + "anvilcraft.configuration.gravitational_lens.render_black_hole_lensing": "ᵷuᴉsuǝꞀ ǝꞁoH ʞɔɐꞁᗺ ɹǝpuǝᴚ", + "anvilcraft.configuration.gravitational_lens.render_black_hole_lensing.tooltip": "sǝꞁoɥ ʞɔɐꞁq ɹɐǝu ʇɔǝɟɟǝ ᵷuᴉssǝɔoɹd-ʇsod ᵷuᴉsuǝꞁ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", + "anvilcraft.configuration.gravitational_lens.tooltip": "suǝꞀ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", "anvilcraft.configuration.ground_heave_particle_chance": "ǝɔuɐɥƆ ǝꞁɔᴉʇɹɐԀ ǝʌɐǝH punoɹ⅁", "anvilcraft.configuration.ground_heave_particle_chance.tooltip": "sǝꞁɔᴉʇɹɐd ǝʌɐǝɥ punoɹᵷ suʍɐds ʞɔoꞁq ɥɔɐǝ (0˙⥝-0˙0) ʎʇᴉꞁᴉqɐqoɹԀ", "anvilcraft.configuration.ground_heave_particle_count": "ʇunoƆ ǝꞁɔᴉʇɹɐԀ ǝʌɐǝH punoɹ⅁", @@ -180,10 +189,6 @@ "anvilcraft.configuration.iono_craft_backpack_max_flight_time.tooltip": "sʞɔᴉʇ uᴉ ǝɯᴉ⟘ ʇɥᵷᴉꞁℲ xɐW ʞɔɐdʞɔɐᗺ ʇɟɐɹƆ ouoI", "anvilcraft.configuration.is_laser_do_impact_checking": "ᵷuᴉʞɔǝɥƆ ʇɔɐdɯI oᗡ ɹǝsɐꞀ sI", "anvilcraft.configuration.is_laser_do_impact_checking.tooltip": "ᵷuᴉʞɔǝɥɔ ʇɔɐdɯᴉ op ɹǝsɐꞁ sI", - "anvilcraft.configuration.lens_perspective_scale": "ǝꞁɐɔS ǝʌᴉʇɔǝdsɹǝԀ suǝꞀ", - "anvilcraft.configuration.lens_perspective_scale.tooltip": "˙ɹǝᵷᵷᴉq = ɹǝsoꞁƆ ˙ǝzᴉs ᵷᴉɟuoɔ = ʇɔǝɟɟǝ 'ǝɔuɐʇsᴉp sᴉɥʇ ʇⱯ ˙ᵷuᴉꞁɐɔs ǝʌᴉʇɔǝdsɹǝd ɹoɟ ǝɔuɐʇsᴉp ǝɔuǝɹǝɟǝᴚ", - "anvilcraft.configuration.lens_strength": "ɥʇᵷuǝɹʇS suǝꞀ", - "anvilcraft.configuration.lens_strength.tooltip": "(ʇꞁnɐɟǝp ᘔ00˙0 'ᵷuᴉpuǝq ɹǝᵷuoɹʇs = ɹǝɥᵷᴉɥ) ɥʇᵷuǝɹʇs uoᴉʇɹoʇsᴉp suǝꞀ", "anvilcraft.configuration.lightning_strike_depth": "ɥʇdǝᗡ ǝʞᴉɹʇS ᵷuᴉuʇɥᵷᴉꞀ", "anvilcraft.configuration.lightning_strike_depth.tooltip": "ɥɔɐǝɹ uɐɔ ǝʞᴉɹʇs ᵷuᴉuʇɥᵷᴉꞁ ɐ ɥʇdǝp ɯnɯᴉxɐW", "anvilcraft.configuration.lightning_strike_radius": "snᴉpɐᴚ ǝʞᴉɹʇS ᵷuᴉuʇɥᵷᴉꞀ", @@ -215,8 +220,6 @@ "anvilcraft.configuration.redstone_emp_radius.tooltip": "ꞁᴉʌuɐ ǝɥʇ ʎq pǝddoɹp ʞɔoꞁq ɹǝd pǝʇɐɹǝuǝᵷ ɥʇᵷuǝꞁ ԀWƎ ǝuoʇspǝᴚ", "anvilcraft.configuration.remote_power_transmitter_range": "ɹǝʇʇᴉɯsuɐɹ⟘ ɹǝʍoԀ ǝʇoɯǝᴚ ɟo ǝᵷuɐᴚ", "anvilcraft.configuration.remote_power_transmitter_range.tooltip": "ɹǝʇʇᴉɯsuɐɹʇ ɹǝʍod ǝʇoɯǝɹ ɟo ǝᵷuɐɹ pᴉɹᵷ ɹǝʍoԀ", - "anvilcraft.configuration.render_black_hole_lensing": "ᵷuᴉsuǝꞀ ǝꞁoH ʞɔɐꞁᗺ ɹǝpuǝᴚ", - "anvilcraft.configuration.render_black_hole_lensing.tooltip": "sǝꞁoɥ ʞɔɐꞁq ɹɐǝu ʇɔǝɟɟǝ ᵷuᴉssǝɔoɹd-ʇsod ᵷuᴉsuǝꞁ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", "anvilcraft.configuration.render_bloom_effect": "ʇɔǝɟɟƎ ɯooꞁᗺ ɹǝpuǝᴚ", "anvilcraft.configuration.render_bloom_effect.tooltip": "˙sǝuᴉꞁ ɹǝʇʇᴉɯsuɐɹʇ ɹǝʍod puɐ ɹǝsɐꞁ uo ʇɔǝɟɟǝ ɯooꞁᗺ", "anvilcraft.configuration.render_power_transmitter_lines": "sǝuᴉꞀ ɹǝʇʇᴉɯsuɐɹ⟘ ɹǝʍoԀ ɹǝpuǝᴚ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index cb904f9fb7..5c8caff057 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -129,8 +129,6 @@ "anvilcraft.configuration.do_not_show_tooltip_when_jade_present.tooltip": "Do not render power component tooltip when jade present", "anvilcraft.configuration.ember_anvil_beyond_max_level": "Ember Anvil Beyond Max Level", "anvilcraft.configuration.ember_anvil_beyond_max_level.tooltip": "Combining items with Enchanted Books beyond max level in Ember Anvil", - "anvilcraft.configuration.event_horizon_radius": "Event Horizon Radius", - "anvilcraft.configuration.event_horizon_radius.tooltip": "Event horizon radius (screen UV units, 0.083 default)", "anvilcraft.configuration.felling_block_per_level": "Felling Block Per Level", "anvilcraft.configuration.felling_block_per_level.tooltip": "The maximum number of logs that can be cut per level of Felling enchantment", "anvilcraft.configuration.frost_anvil_beyond_max_level": "Frost Anvil Beyond Max Level", @@ -147,6 +145,17 @@ "anvilcraft.configuration.giant_anvil_max_shock_radius.tooltip": "Maximum radius of giant anvil's shock behavior", "anvilcraft.configuration.goggle_mode": "Goggle Mode", "anvilcraft.configuration.goggle_mode.tooltip": "The mode of the anvil hammer goggle info", + "anvilcraft.configuration.gravitational_lens": "Gravitational Lens", + "anvilcraft.configuration.gravitational_lens.button": "Gravitational Lens", + "anvilcraft.configuration.gravitational_lens.event_horizon_radius": "Event Horizon Radius", + "anvilcraft.configuration.gravitational_lens.event_horizon_radius.tooltip": "Event horizon radius (screen UV units, 0.083 default)", + "anvilcraft.configuration.gravitational_lens.lens_perspective_scale": "Lens Perspective Scale", + "anvilcraft.configuration.gravitational_lens.lens_perspective_scale.tooltip": "Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.", + "anvilcraft.configuration.gravitational_lens.lens_strength": "Lens Strength", + "anvilcraft.configuration.gravitational_lens.lens_strength.tooltip": "Lens distortion strength (higher = stronger bending, 0.002 default)", + "anvilcraft.configuration.gravitational_lens.render_black_hole_lensing": "Render Black Hole Lensing", + "anvilcraft.configuration.gravitational_lens.render_black_hole_lensing.tooltip": "Gravitational lensing post-processing effect near black holes", + "anvilcraft.configuration.gravitational_lens.tooltip": "Gravitational Lens", "anvilcraft.configuration.ground_heave_particle_chance": "Ground Heave Particle Chance", "anvilcraft.configuration.ground_heave_particle_chance.tooltip": "Probability (0.0-1.0) each block spawns ground heave particles", "anvilcraft.configuration.ground_heave_particle_count": "Ground Heave Particle Count", @@ -180,10 +189,6 @@ "anvilcraft.configuration.iono_craft_backpack_max_flight_time.tooltip": "Iono Craft Backpack Max Flight Time in ticks", "anvilcraft.configuration.is_laser_do_impact_checking": "Is Laser Do Impact Checking", "anvilcraft.configuration.is_laser_do_impact_checking.tooltip": "Is laser do impact checking", - "anvilcraft.configuration.lens_perspective_scale": "Lens Perspective Scale", - "anvilcraft.configuration.lens_perspective_scale.tooltip": "Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.", - "anvilcraft.configuration.lens_strength": "Lens Strength", - "anvilcraft.configuration.lens_strength.tooltip": "Lens distortion strength (higher = stronger bending, 0.002 default)", "anvilcraft.configuration.lightning_strike_depth": "Lightning Strike Depth", "anvilcraft.configuration.lightning_strike_depth.tooltip": "Maximum depth a lightning strike can reach", "anvilcraft.configuration.lightning_strike_radius": "Lightning Strike Radius", @@ -215,8 +220,6 @@ "anvilcraft.configuration.redstone_emp_radius.tooltip": "Redstone EMP length generated per block dropped by the anvil", "anvilcraft.configuration.remote_power_transmitter_range": "Range of Remote Power Transmitter", "anvilcraft.configuration.remote_power_transmitter_range.tooltip": "Power grid range of remote power transmitter", - "anvilcraft.configuration.render_black_hole_lensing": "Render Black Hole Lensing", - "anvilcraft.configuration.render_black_hole_lensing.tooltip": "Gravitational lensing post-processing effect near black holes", "anvilcraft.configuration.render_bloom_effect": "Render Bloom Effect", "anvilcraft.configuration.render_bloom_effect.tooltip": "Bloom effect on laser and power transmitter lines.", "anvilcraft.configuration.render_power_transmitter_lines": "Render Power Transmitter Lines", diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/RenderState.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/RenderState.java index dad4ffbb57..0301ab098e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/RenderState.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/RenderState.java @@ -40,6 +40,6 @@ public static boolean isScanPreviewEffectEnabled() { } public static boolean isLensEffectEnabled() { - return isEnhancedRenderingAvailable() && AnvilCraftClient.CONFIG.renderBlackHoleLensing; + return isEnhancedRenderingAvailable() && AnvilCraftClient.CONFIG.gravitationalLens.renderBlackHoleLensing; } } diff --git a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java index 641ff7efb2..b0fe7c2158 100644 --- a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java +++ b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java @@ -45,17 +45,22 @@ public class AnvilCraftClientConfig { @Comment("Scanline post-processing effect on 3D structure previews.") public boolean renderScanPreviewEffect = true; - @Comment("Gravitational lensing post-processing effect near black holes") - public boolean renderBlackHoleLensing = true; + @CollapsibleObject + public GravitationalLens gravitationalLens = new GravitationalLens(); + + public static class GravitationalLens { + @Comment("Gravitational lensing post-processing effect near black holes") + public boolean renderBlackHoleLensing = true; - @Comment("Lens distortion strength (higher = stronger bending, 0.002 default)") - public double lensStrength = 1.0 / 512.0; + @Comment("Lens distortion strength (higher = stronger bending, 0.002 default)") + public double lensStrength = 1.0 / 512.0; - @Comment("Event horizon radius (screen UV units, 0.083 default)") - public double eventHorizonRadius = 1.0 / 12.0; + @Comment("Event horizon radius (screen UV units, 0.083 default)") + public double eventHorizonRadius = 1.0 / 12.0; - @Comment("Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.") - public double lensPerspectiveScale = 10.0; + @Comment("Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.") + public double lensPerspectiveScale = 10.0; + } @Comment("A vertical item frame vertically displays items") public boolean verticalItemFrame = false; diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 62346cd95b..01fff7646b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -217,11 +217,11 @@ void gravitationalLensPostProcess( pass.getEffect().safeGetUniform("BlackHoleCount").set((float) count); pass.getEffect().safeGetUniform("LensStrength") - .set((float) AnvilCraftClient.CONFIG.lensStrength); + .set((float) AnvilCraftClient.CONFIG.gravitationalLens.lensStrength); pass.getEffect().safeGetUniform("EventHorizonRadius") - .set((float) AnvilCraftClient.CONFIG.eventHorizonRadius); + .set((float) AnvilCraftClient.CONFIG.gravitationalLens.eventHorizonRadius); pass.getEffect().safeGetUniform("PerspectiveScale") - .set((float) AnvilCraftClient.CONFIG.lensPerspectiveScale); + .set((float) AnvilCraftClient.CONFIG.gravitationalLens.lensPerspectiveScale); // Run the lens post chain (reads main target, writes to result target) lensChain.process(RenderSupport.getPartialTick()); diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index 2a0207c8fc..64d472002a 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -63,10 +63,8 @@ void main() { float perspScale = PerspectiveScale / max(getHoleDist(i), 0.1); float gravity = LensStrength * perspScale / (dist * dist); - float scaledHorizon = EventHorizonRadius * perspScale; - float mask = step(scaledHorizon, dist); - vec2 lensOffset = dir * gravity * mask; + vec2 lensOffset = dir * gravity; lensOffset.x /= aspectRatio; offset += lensOffset; } From 4ac9de3e64a1d1f585afcc4c998a5ba9d0a8a3d7 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 04:29:26 +0800 Subject: [PATCH 06/17] =?UTF-8?q?feat(shader):=20=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E7=9D=80=E8=89=B2=E5=99=A8?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9C=80=E5=A4=9A8=E4=B8=AA=E9=BB=91?= =?UTF-8?q?=E6=B4=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将黑洞数量从4个增加到8个,添加了BlackHole5-8的相关uniform变量 - 更新了getHolePos和getHoleDist函数以支持8个黑洞的坐标和距离获取 - 修改了循环范围从4次迭代扩展到8次以处理更多黑洞 - 在JSON配置文件中添加了新黑洞的参数定义 - 优化了世界坐标到屏幕坐标的转换逻辑,移除背面点镜像并返回null - 改进了黑洞收集逻辑,只处理屏幕上的黑洞并按距离排序 - 添加了屏幕边缘检测以跳过远离屏幕中心的黑洞 - 在LevelRendererMixin中同步更新了黑洞数量限制和循环范围 --- .../support/GravitationalLensManager.java | 25 ++++++++------- .../anvilcraft/mixin/LevelRendererMixin.java | 6 ++-- .../shaders/program/gravitational_lens.fsh | 32 +++++++++++++++---- .../shaders/program/gravitational_lens.json | 12 +++++++ 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index c980a62bd0..cd7f480a21 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -68,22 +68,16 @@ private static Matrix4f buildViewProj(Camera camera, Matrix4f projectionMatrix) /** * Transform a world-space point to screen UV via view-projection. - * Vertices behind the camera (clip.w ≤ 0) are mirrored so they still - * project to valid screen positions. + * Returns {@code null} when the point is behind the camera (clip.w ≤ 0). */ private static Vector2f worldToScreenUV(float wx, float wy, float wz, Matrix4f viewProj) { Vector4f clip = viewProj.transform(new Vector4f(wx, wy, wz, 1.0f)); - - if (clip.w <= 0.0f) { - clip.x = -clip.x; - clip.y = -clip.y; - clip.z = -clip.z; - clip.w = -clip.w; - } + if (clip.w <= 0.0f) return null; float ndcX = clip.x / clip.w; float ndcY = clip.y / clip.w; + // Clamp to screen edge — still useful for points slightly off-screen ndcX = Math.max(-1.0f, Math.min(1.0f, ndcX)); ndcY = Math.max(-1.0f, Math.min(1.0f, ndcY)); @@ -91,7 +85,7 @@ private static Vector2f worldToScreenUV(float wx, float wy, float wz, Matrix4f v } /** - * Collect up to {@code maxCount} visible black holes sorted by distance. + * Collect up to {@code maxCount} on-screen black holes, sorted nearest first. */ public static List collectVisibleBlackHoles( Camera camera, @@ -115,9 +109,18 @@ public static List collectVisibleBlackHoles( ); if (centerUV == null) continue; + // Skip black holes whose center is far off-screen + if (centerUV.x < -0.2f || centerUV.x > 1.2f + || centerUV.y < -0.2f || centerUV.y > 1.2f) continue; + float dist = (float) Math.sqrt(dx * dx + dy * dy + dz * dz); result.add(new HoleProjection(centerUV.x, centerUV.y, dist)); - if (result.size() >= maxCount) break; + } + + // Sort nearest first, then take the closest maxCount + result.sort((a, b) -> Float.compare(a.cameraDistance, b.cameraDistance)); + if (result.size() > maxCount) { + result = result.subList(0, maxCount); } return result; } diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 01fff7646b..9ba9ae1668 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -194,12 +194,12 @@ void gravitationalLensPostProcess( // Collect visible black holes java.util.List holes = - GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 4); + GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 8); - int count = Math.min(holes.size(), 4); + int count = Math.min(holes.size(), 8); // Set dynamic black hole position and distance uniforms - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 8; i++) { String xName = "BlackHole" + (i + 1) + "X"; String yName = "BlackHole" + (i + 1) + "Y"; String dName = "BlackHole" + (i + 1) + "Dist"; diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index 64d472002a..22915e07a6 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -3,7 +3,7 @@ uniform sampler2D DiffuseSampler; uniform vec2 InSize; -// Up to 4 black hole screen positions in UV coordinates (0-1) +// Up to 8 black hole screen positions in UV coordinates (0-1) uniform float BlackHole1X; uniform float BlackHole1Y; uniform float BlackHole2X; @@ -12,17 +12,29 @@ uniform float BlackHole3X; uniform float BlackHole3Y; uniform float BlackHole4X; uniform float BlackHole4Y; +uniform float BlackHole5X; +uniform float BlackHole5Y; +uniform float BlackHole6X; +uniform float BlackHole6Y; +uniform float BlackHole7X; +uniform float BlackHole7Y; +uniform float BlackHole8X; +uniform float BlackHole8Y; // Distance from camera to each black hole (world units) uniform float BlackHole1Dist; uniform float BlackHole2Dist; uniform float BlackHole3Dist; uniform float BlackHole4Dist; +uniform float BlackHole5Dist; +uniform float BlackHole6Dist; +uniform float BlackHole7Dist; +uniform float BlackHole8Dist; uniform float BlackHoleCount; uniform float LensStrength; uniform float EventHorizonRadius; -uniform float PerspectiveScale; // reference distance (default 10.0) +uniform float PerspectiveScale; in vec2 texCoord; out vec4 fragColor; @@ -31,14 +43,22 @@ vec2 getHolePos(int i) { if (i == 0) return vec2(BlackHole1X, BlackHole1Y); if (i == 1) return vec2(BlackHole2X, BlackHole2Y); if (i == 2) return vec2(BlackHole3X, BlackHole3Y); - return vec2(BlackHole4X, BlackHole4Y); + if (i == 3) return vec2(BlackHole4X, BlackHole4Y); + if (i == 4) return vec2(BlackHole5X, BlackHole5Y); + if (i == 5) return vec2(BlackHole6X, BlackHole6Y); + if (i == 6) return vec2(BlackHole7X, BlackHole7Y); + return vec2(BlackHole8X, BlackHole8Y); } float getHoleDist(int i) { if (i == 0) return BlackHole1Dist; if (i == 1) return BlackHole2Dist; if (i == 2) return BlackHole3Dist; - return BlackHole4Dist; + if (i == 3) return BlackHole4Dist; + if (i == 4) return BlackHole5Dist; + if (i == 5) return BlackHole6Dist; + if (i == 6) return BlackHole7Dist; + return BlackHole8Dist; } void main() { @@ -49,7 +69,7 @@ void main() { int count = int(BlackHoleCount); // --- Gravitational displacement --- - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 8; i++) { if (i >= count) break; vec2 holeUv = getHolePos(i); @@ -72,7 +92,7 @@ void main() { vec3 color = texture(DiffuseSampler, uv + offset).rgb; // --- Render event horizon --- - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 8; i++) { if (i >= count) break; vec2 holeUv = getHolePos(i); diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json index 1fc28c682f..4a02849752 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json @@ -25,11 +25,23 @@ { "name": "BlackHole3Y", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "BlackHole4X", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "BlackHole4Y", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole5X", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole5Y", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole6X", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole6Y", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole7X", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole7Y", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole8X", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "BlackHole8Y", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "BlackHole1Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "BlackHole2Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "BlackHole3Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "BlackHole4Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "BlackHole5Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "BlackHole6Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "BlackHole7Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "BlackHole8Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "BlackHoleCount", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "LensStrength", "type": "float", "count": 1, "values": [ 0.001953125 ] }, From 65f013bdfa91e324d710203f114b2e9cad1a0ec8 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 04:39:56 +0800 Subject: [PATCH 07/17] =?UTF-8?q?feat(renderer):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E6=96=B9=E5=90=91=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在客户端配置中添加 lensDirection 参数用于控制透镜方向 - 更新着色器程序将多个单独的黑洞性质统一为 vec4 数组 - 实现凹凸透镜效果:正值为凸透镜(向中心吸引),负值为凹透镜(远离中心) - 修改事件视界渲染逻辑仅对凸透镜生效 - 更新投影矩阵计算以支持透镜方向参数 - 添加新的本地化字符串资源用于透镜方向配置项 --- .../assets/anvilcraft/lang/en_ud.json | 2 + .../assets/anvilcraft/lang/en_us.json | 2 + .../support/GravitationalLensManager.java | 22 +++--- .../config/AnvilCraftClientConfig.java | 3 + .../anvilcraft/mixin/LevelRendererMixin.java | 18 ++--- .../shaders/program/gravitational_lens.fsh | 70 ++++++------------- .../shaders/program/gravitational_lens.json | 33 +++------ 7 files changed, 56 insertions(+), 94 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 84e8e9b8d0..ba7b019271 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -149,6 +149,8 @@ "anvilcraft.configuration.gravitational_lens.button": "suǝꞀ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", "anvilcraft.configuration.gravitational_lens.event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ", "anvilcraft.configuration.gravitational_lens.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'sʇᴉun Ʌ∩ uǝǝɹɔs) snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", + "anvilcraft.configuration.gravitational_lens.lens_direction": "uoᴉʇɔǝɹᴉᗡ suǝꞀ", + "anvilcraft.configuration.gravitational_lens.lens_direction.tooltip": "˙ɥʇᵷuǝɹʇs sǝꞁɐɔs ǝpnʇᴉuᵷɐW ˙(ʎɐʍɐ ɥsnd) ǝʌɐɔuoɔ = 0 < '(ɹǝʇuǝɔ pɹɐʍoʇ ꞁꞁnd) xǝʌuoɔ = 0 > :uoᴉʇɔǝɹᴉp suǝꞀ", "anvilcraft.configuration.gravitational_lens.lens_perspective_scale": "ǝꞁɐɔS ǝʌᴉʇɔǝdsɹǝԀ suǝꞀ", "anvilcraft.configuration.gravitational_lens.lens_perspective_scale.tooltip": "˙ɹǝᵷᵷᴉq = ɹǝsoꞁƆ ˙ǝzᴉs ᵷᴉɟuoɔ = ʇɔǝɟɟǝ 'ǝɔuɐʇsᴉp sᴉɥʇ ʇⱯ ˙ᵷuᴉꞁɐɔs ǝʌᴉʇɔǝdsɹǝd ɹoɟ ǝɔuɐʇsᴉp ǝɔuǝɹǝɟǝᴚ", "anvilcraft.configuration.gravitational_lens.lens_strength": "ɥʇᵷuǝɹʇS suǝꞀ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 5c8caff057..3b7b2cde36 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -149,6 +149,8 @@ "anvilcraft.configuration.gravitational_lens.button": "Gravitational Lens", "anvilcraft.configuration.gravitational_lens.event_horizon_radius": "Event Horizon Radius", "anvilcraft.configuration.gravitational_lens.event_horizon_radius.tooltip": "Event horizon radius (screen UV units, 0.083 default)", + "anvilcraft.configuration.gravitational_lens.lens_direction": "Lens Direction", + "anvilcraft.configuration.gravitational_lens.lens_direction.tooltip": "Lens direction: >0 = convex (pull toward center), <0 = concave (push away). Magnitude scales strength.", "anvilcraft.configuration.gravitational_lens.lens_perspective_scale": "Lens Perspective Scale", "anvilcraft.configuration.gravitational_lens.lens_perspective_scale.tooltip": "Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.", "anvilcraft.configuration.gravitational_lens.lens_strength": "Lens Strength", diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index cd7f480a21..b4ec28d9aa 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; public class GravitationalLensManager { private static final int MAX_SEARCH_DISTANCE_SQR = 256 * 256; @@ -38,11 +39,14 @@ public static final class HoleProjection { public final float centerV; /** Distance from camera to black hole (world units). */ public final float cameraDistance; + /** Lens direction: > 0 = convex (pull), < 0 = concave (push). */ + public final float lensDirection; - HoleProjection(float cu, float cv, float dist) { + HoleProjection(float cu, float cv, float dist, float dir) { this.centerU = cu; this.centerV = cv; this.cameraDistance = dist; + this.lensDirection = dir; } } @@ -70,7 +74,7 @@ private static Matrix4f buildViewProj(Camera camera, Matrix4f projectionMatrix) * Transform a world-space point to screen UV via view-projection. * Returns {@code null} when the point is behind the camera (clip.w ≤ 0). */ - private static Vector2f worldToScreenUV(float wx, float wy, float wz, Matrix4f viewProj) { + private static @Nullable Vector2f worldToScreenUV(float wx, float wy, float wz, Matrix4f viewProj) { Vector4f clip = viewProj.transform(new Vector4f(wx, wy, wz, 1.0f)); if (clip.w <= 0.0f) return null; @@ -78,8 +82,8 @@ private static Vector2f worldToScreenUV(float wx, float wy, float wz, Matrix4f v float ndcY = clip.y / clip.w; // Clamp to screen edge — still useful for points slightly off-screen - ndcX = Math.max(-1.0f, Math.min(1.0f, ndcX)); - ndcY = Math.max(-1.0f, Math.min(1.0f, ndcY)); + ndcX = Math.clamp(ndcX, -1.0f, 1.0f); + ndcY = Math.clamp(ndcY, -1.0f, 1.0f); return new Vector2f((ndcX + 1.0f) / 2.0f, (ndcY + 1.0f) / 2.0f); } @@ -90,7 +94,8 @@ private static Vector2f worldToScreenUV(float wx, float wy, float wz, Matrix4f v public static List collectVisibleBlackHoles( Camera camera, Matrix4f projectionMatrix, - int maxCount + int maxCount, + float lensDirection ) { List result = new ArrayList<>(); if (CLIENT_BLACK_HOLE_POSITIONS.isEmpty()) return result; @@ -102,7 +107,8 @@ public static List collectVisibleBlackHoles( double dx = pos.getX() + 0.5 - cameraPos.x; double dy = pos.getY() + 0.5 - cameraPos.y; double dz = pos.getZ() + 0.5 - cameraPos.z; - if (dx * dx + dy * dy + dz * dz > MAX_SEARCH_DISTANCE_SQR) continue; + double distanceSqr = dx * dx + dy * dy + dz * dz; + if (distanceSqr > MAX_SEARCH_DISTANCE_SQR) continue; Vector2f centerUV = worldToScreenUV( pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f, viewProj @@ -113,8 +119,8 @@ public static List collectVisibleBlackHoles( if (centerUV.x < -0.2f || centerUV.x > 1.2f || centerUV.y < -0.2f || centerUV.y > 1.2f) continue; - float dist = (float) Math.sqrt(dx * dx + dy * dy + dz * dz); - result.add(new HoleProjection(centerUV.x, centerUV.y, dist)); + float dist = (float) Math.sqrt(distanceSqr); + result.add(new HoleProjection(centerUV.x, centerUV.y, dist, lensDirection)); } // Sort nearest first, then take the closest maxCount diff --git a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java index b0fe7c2158..1dfd9d0e7c 100644 --- a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java +++ b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java @@ -60,6 +60,9 @@ public static class GravitationalLens { @Comment("Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.") public double lensPerspectiveScale = 10.0; + + @Comment("Lens direction: >0 = convex (gravitational pull toward center), <0 = concave (push away). Magnitude scales strength.") + public double lensDirection = 1.0; } @Comment("A vertical item frame vertically displays items") diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 9ba9ae1668..b606bfcfc1 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -194,24 +194,20 @@ void gravitationalLensPostProcess( // Collect visible black holes java.util.List holes = - GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 8); + GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 8, + (float) AnvilCraftClient.CONFIG.gravitationalLens.lensDirection); int count = Math.min(holes.size(), 8); - // Set dynamic black hole position and distance uniforms + // Set BlackHole vec4 uniforms: x=screenU, y=screenV, z=distance, w=lensDirection for (int i = 0; i < 8; i++) { - String xName = "BlackHole" + (i + 1) + "X"; - String yName = "BlackHole" + (i + 1) + "Y"; - String dName = "BlackHole" + (i + 1) + "Dist"; + String name = "BlackHole[" + i + "]"; if (i < count) { GravitationalLensManager.HoleProjection h = holes.get(i); - pass.getEffect().safeGetUniform(xName).set(h.centerU); - pass.getEffect().safeGetUniform(yName).set(h.centerV); - pass.getEffect().safeGetUniform(dName).set(h.cameraDistance); + pass.getEffect().safeGetUniform(name) + .set(h.centerU, h.centerV, h.cameraDistance, h.lensDirection); } else { - pass.getEffect().safeGetUniform(xName).set(0.0f); - pass.getEffect().safeGetUniform(yName).set(0.0f); - pass.getEffect().safeGetUniform(dName).set(1.0f); + pass.getEffect().safeGetUniform(name).set(0.0f, 0.0f, 1.0f, 1.0f); } } diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index 22915e07a6..f42b6db114 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -3,33 +3,10 @@ uniform sampler2D DiffuseSampler; uniform vec2 InSize; -// Up to 8 black hole screen positions in UV coordinates (0-1) -uniform float BlackHole1X; -uniform float BlackHole1Y; -uniform float BlackHole2X; -uniform float BlackHole2Y; -uniform float BlackHole3X; -uniform float BlackHole3Y; -uniform float BlackHole4X; -uniform float BlackHole4Y; -uniform float BlackHole5X; -uniform float BlackHole5Y; -uniform float BlackHole6X; -uniform float BlackHole6Y; -uniform float BlackHole7X; -uniform float BlackHole7Y; -uniform float BlackHole8X; -uniform float BlackHole8Y; - -// Distance from camera to each black hole (world units) -uniform float BlackHole1Dist; -uniform float BlackHole2Dist; -uniform float BlackHole3Dist; -uniform float BlackHole4Dist; -uniform float BlackHole5Dist; -uniform float BlackHole6Dist; -uniform float BlackHole7Dist; -uniform float BlackHole8Dist; +// BlackHole[i]: x=screenU, y=screenV, z=cameraDistance, w=lensDirection +// lensDirection > 0: convex (pull toward center, gravitational lens) +// lensDirection < 0: concave (push away from center, diverging lens) +uniform vec4 BlackHole[8]; uniform float BlackHoleCount; uniform float LensStrength; @@ -39,27 +16,9 @@ uniform float PerspectiveScale; in vec2 texCoord; out vec4 fragColor; -vec2 getHolePos(int i) { - if (i == 0) return vec2(BlackHole1X, BlackHole1Y); - if (i == 1) return vec2(BlackHole2X, BlackHole2Y); - if (i == 2) return vec2(BlackHole3X, BlackHole3Y); - if (i == 3) return vec2(BlackHole4X, BlackHole4Y); - if (i == 4) return vec2(BlackHole5X, BlackHole5Y); - if (i == 5) return vec2(BlackHole6X, BlackHole6Y); - if (i == 6) return vec2(BlackHole7X, BlackHole7Y); - return vec2(BlackHole8X, BlackHole8Y); -} - -float getHoleDist(int i) { - if (i == 0) return BlackHole1Dist; - if (i == 1) return BlackHole2Dist; - if (i == 2) return BlackHole3Dist; - if (i == 3) return BlackHole4Dist; - if (i == 4) return BlackHole5Dist; - if (i == 5) return BlackHole6Dist; - if (i == 6) return BlackHole7Dist; - return BlackHole8Dist; -} +vec2 getHolePos(int i) { return BlackHole[i].xy; } +float getHoleDist(int i) { return BlackHole[i].z; } +float getLensDir(int i) { return BlackHole[i].w; } void main() { vec2 uv = texCoord; @@ -81,20 +40,31 @@ void main() { vec2 dir = toHole / dist; float perspScale = PerspectiveScale / max(getHoleDist(i), 0.1); + float lensDir = getLensDir(i); float gravity = LensStrength * perspScale / (dist * dist); - vec2 lensOffset = dir * gravity; + vec2 lensOffset; + if (lensDir < 0.0) { + // Concave: push away from center, scaled by |lensDir| + lensOffset = -dir * gravity * (-lensDir); + } else { + // Convex: pull toward center + lensOffset = dir * gravity * lensDir; + } lensOffset.x /= aspectRatio; offset += lensOffset; } vec3 color = texture(DiffuseSampler, uv + offset).rgb; - // --- Render event horizon --- + // --- Render event horizon (convex black holes only) --- for (int i = 0; i < 8; i++) { if (i >= count) break; + // Concave lenses have no event horizon + if (getLensDir(i) <= 0.0) continue; + vec2 holeUv = getHolePos(i); float perspS = PerspectiveScale / max(getHoleDist(i), 0.1); vec2 toHole = uv - holeUv; diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json index 4a02849752..d0cea6827e 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json @@ -17,31 +17,14 @@ { "name": "InSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, { "name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, - { "name": "BlackHole1X", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole1Y", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole2X", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole2Y", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole3X", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole3Y", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole4X", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole4Y", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole5X", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole5Y", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole6X", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole6Y", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole7X", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole7Y", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole8X", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "BlackHole8Y", "type": "float", "count": 1, "values": [ 0.0 ] }, - - { "name": "BlackHole1Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "BlackHole2Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "BlackHole3Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "BlackHole4Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "BlackHole5Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "BlackHole6Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "BlackHole7Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, - { "name": "BlackHole8Dist", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "BlackHole[0]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, + { "name": "BlackHole[1]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, + { "name": "BlackHole[2]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, + { "name": "BlackHole[3]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, + { "name": "BlackHole[4]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, + { "name": "BlackHole[5]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, + { "name": "BlackHole[6]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, + { "name": "BlackHole[7]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, { "name": "BlackHoleCount", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "LensStrength", "type": "float", "count": 1, "values": [ 0.001953125 ] }, From 264d78a2887055b71ad8a4c5f7f82b4355658de3 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 04:47:18 +0800 Subject: [PATCH 08/17] =?UTF-8?q?feat(renderer):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=99=BD=E6=B4=9E=E6=94=AF=E6=8C=81=E5=88=B0=E5=BC=95=E5=8A=9B?= =?UTF-8?q?=E9=80=8F=E9=95=9C=E6=B8=B2=E6=9F=93=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 GravitationalLensManager 中添加白洞位置客户端缓存 - 实现白洞注册和注销方法 - 修改 collectVisibleBlackHoles 方法以同时收集黑洞和白洞数据 - 区分黑洞为凸透镜效果(正方向),白洞为凹透镜效果(负方向) - 更新 LevelRendererMixin 以使用新的双方向透镜参数 - 在 WhiteHoleBlockEntity 中添加客户端侧的透镜管理器注册 - 重构收集逻辑到独立的 collectFromSet 辅助方法 --- .../block/entity/WhiteHoleBlockEntity.java | 21 ++++++--- .../support/GravitationalLensManager.java | 46 +++++++++++++------ .../anvilcraft/mixin/LevelRendererMixin.java | 6 +-- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java index 49701b5748..e2a9edd8ba 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java @@ -1,5 +1,6 @@ package dev.dubhe.anvilcraft.block.entity; +import dev.dubhe.anvilcraft.client.support.GravitationalLensManager; import dev.dubhe.anvilcraft.init.block.ModBlockEntities; import dev.dubhe.anvilcraft.util.GravityManager; import net.minecraft.core.BlockPos; @@ -19,10 +20,14 @@ public static WhiteHoleBlockEntity createBlockEntity(BlockEntityType type, Bl @Override public void onLoad() { super.onLoad(); - if (this.level != null && !this.level.isClientSide) { - GravityManager.GravitySourceType type = GravityManager.GravitySourceManager.getType(this.getBlockState().getBlock()); - if (type != null) { - GravityManager.GravitySourceManager.addSource(this.level, this.worldPosition, type); + if (this.level != null) { + if (this.level.isClientSide) { + GravitationalLensManager.registerWhiteHole(this.worldPosition); + } else { + GravityManager.GravitySourceType type = GravityManager.GravitySourceManager.getType(this.getBlockState().getBlock()); + if (type != null) { + GravityManager.GravitySourceManager.addSource(this.level, this.worldPosition, type); + } } } } @@ -30,8 +35,12 @@ public void onLoad() { @Override public void setRemoved() { super.setRemoved(); - if (this.level != null && !this.level.isClientSide) { - GravityManager.GravitySourceManager.removeSource(this.level, this.worldPosition); + if (this.level != null) { + if (this.level.isClientSide) { + GravitationalLensManager.unregisterWhiteHole(this.worldPosition); + } else { + GravityManager.GravitySourceManager.removeSource(this.level, this.worldPosition); + } } } } \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index b4ec28d9aa..3e279f8767 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -21,6 +21,9 @@ public class GravitationalLensManager { /** Client-side cache of loaded black hole block positions. */ public static final Set CLIENT_BLACK_HOLE_POSITIONS = Collections.newSetFromMap(new ConcurrentHashMap<>()); + /** Client-side cache of loaded white hole block positions. */ + public static final Set CLIENT_WHITE_HOLE_POSITIONS = + Collections.newSetFromMap(new ConcurrentHashMap<>()); public static void register(BlockPos pos) { CLIENT_BLACK_HOLE_POSITIONS.add(pos.immutable()); @@ -30,6 +33,14 @@ public static void unregister(BlockPos pos) { CLIENT_BLACK_HOLE_POSITIONS.remove(pos); } + public static void registerWhiteHole(BlockPos pos) { + CLIENT_WHITE_HOLE_POSITIONS.add(pos.immutable()); + } + + public static void unregisterWhiteHole(BlockPos pos) { + CLIENT_WHITE_HOLE_POSITIONS.remove(pos); + } + /** * Per-hole data passed to the shader. */ @@ -89,21 +100,38 @@ private static Matrix4f buildViewProj(Camera camera, Matrix4f projectionMatrix) } /** - * Collect up to {@code maxCount} on-screen black holes, sorted nearest first. + * Collect up to {@code maxCount} on-screen holes from both black and white hole sets, + * sorted nearest first. Black holes get {@code blackHoleDir} (positive=convex pull), + * white holes get {@code whiteHoleDir} (negative=concave push). */ public static List collectVisibleBlackHoles( Camera camera, Matrix4f projectionMatrix, int maxCount, - float lensDirection + float blackHoleDir, + float whiteHoleDir ) { List result = new ArrayList<>(); - if (CLIENT_BLACK_HOLE_POSITIONS.isEmpty()) return result; Matrix4f viewProj = buildViewProj(camera, projectionMatrix); Vector3f cameraPos = camera.getPosition().toVector3f(); - for (BlockPos pos : CLIENT_BLACK_HOLE_POSITIONS) { + collectFromSet(CLIENT_BLACK_HOLE_POSITIONS, cameraPos, viewProj, blackHoleDir, result); + collectFromSet(CLIENT_WHITE_HOLE_POSITIONS, cameraPos, viewProj, whiteHoleDir, result); + + // Sort nearest first, then take the closest maxCount + result.sort((a, b) -> Float.compare(a.cameraDistance, b.cameraDistance)); + if (result.size() > maxCount) { + result = result.subList(0, maxCount); + } + return result; + } + + private static void collectFromSet( + Set positions, Vector3f cameraPos, Matrix4f viewProj, + float lensDir, List out + ) { + for (BlockPos pos : positions) { double dx = pos.getX() + 0.5 - cameraPos.x; double dy = pos.getY() + 0.5 - cameraPos.y; double dz = pos.getZ() + 0.5 - cameraPos.z; @@ -115,19 +143,11 @@ public static List collectVisibleBlackHoles( ); if (centerUV == null) continue; - // Skip black holes whose center is far off-screen if (centerUV.x < -0.2f || centerUV.x > 1.2f || centerUV.y < -0.2f || centerUV.y > 1.2f) continue; float dist = (float) Math.sqrt(distanceSqr); - result.add(new HoleProjection(centerUV.x, centerUV.y, dist, lensDirection)); + out.add(new HoleProjection(centerUV.x, centerUV.y, dist, lensDir)); } - - // Sort nearest first, then take the closest maxCount - result.sort((a, b) -> Float.compare(a.cameraDistance, b.cameraDistance)); - if (result.size() > maxCount) { - result = result.subList(0, maxCount); - } - return result; } } diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index b606bfcfc1..50572807e2 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -192,10 +192,10 @@ void gravitationalLensPostProcess( PostPass pass = passes.get(0); - // Collect visible black holes + // Collect visible holes: black holes use positive direction, white holes negative + float dir = (float) AnvilCraftClient.CONFIG.gravitationalLens.lensDirection; java.util.List holes = - GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 8, - (float) AnvilCraftClient.CONFIG.gravitationalLens.lensDirection); + GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 8, dir, -dir); int count = Math.min(holes.size(), 8); From 8df12bb31cc8c840d0cbded9cd4adeba89668fdd Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 05:05:32 +0800 Subject: [PATCH 09/17] =?UTF-8?q?fix(render):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E4=BA=8B=E4=BB=B6=E8=A7=86?= =?UTF-8?q?=E7=95=8C=E6=B8=B2=E6=9F=93=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新注释说明仅对屏幕内的凸面黑洞渲染事件视界 - 添加检查跳过屏幕外黑洞中心的事件视界渲染 - 避免屏幕外黑洞导致的渲染异常 --- .../anvilcraft/shaders/program/gravitational_lens.fsh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index f42b6db114..2c587421a4 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -58,7 +58,7 @@ void main() { vec3 color = texture(DiffuseSampler, uv + offset).rgb; - // --- Render event horizon (convex black holes only) --- + // --- Render event horizon (convex, on-screen black holes only) --- for (int i = 0; i < 8; i++) { if (i >= count) break; @@ -66,6 +66,10 @@ void main() { if (getLensDir(i) <= 0.0) continue; vec2 holeUv = getHolePos(i); + + // Skip event horizon when the hole center is off-screen + if (holeUv.x < 0.0 || holeUv.x > 1.0 || holeUv.y < 0.0 || holeUv.y > 1.0) continue; + float perspS = PerspectiveScale / max(getHoleDist(i), 0.1); vec2 toHole = uv - holeUv; toHole.x *= aspectRatio; From b5f0eb2b66d09968ea088b0794f669741fa858f4 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 05:31:36 +0800 Subject: [PATCH 10/17] =?UTF-8?q?perf(renderer):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E7=9D=80=E8=89=B2=E5=99=A8?= =?UTF-8?q?=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将BlackHole数组从8个元素扩展到256个元素以支持更多透镜 - 使用UBO(Uniform Buffer Object)替代逐个uniform设置提升渲染性能 - 添加GL30/GL31依赖以支持缓冲区操作 - 实现统一缓冲区绑定机制减少GPU状态切换 - 移除JSON配置中的硬编码BlackHole数组定义 - 重构着色器程序绑定逻辑以正确处理UBO块索引 --- .../anvilcraft/mixin/LevelRendererMixin.java | 52 +++++++++++++++---- .../shaders/program/gravitational_lens.fsh | 10 ++-- .../shaders/program/gravitational_lens.json | 9 ---- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 50572807e2..09dc4fe991 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -32,9 +32,13 @@ import net.minecraft.client.renderer.ShaderInstance; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL31; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +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; @@ -45,6 +49,13 @@ public abstract class LevelRendererMixin { @Final private Minecraft minecraft; + /** UBO handle for BlackHole[256] data — created once, updated per frame. */ + @Unique + private static int anvilcraft$lensUbo = 0; + /** Last program ID for which we bound the UBO block index. */ + @Unique + private static int anvilcraft$lensUboBlockBound = 0; + @Inject( method = "renderLevel", at = @At( @@ -190,25 +201,46 @@ void gravitationalLensPostProcess( java.util.List passes = ((PostChainAccessor) lensChain).getPasses(); if (passes.isEmpty()) return; - PostPass pass = passes.get(0); - // Collect visible holes: black holes use positive direction, white holes negative float dir = (float) AnvilCraftClient.CONFIG.gravitationalLens.lensDirection; java.util.List holes = - GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 8, dir, -dir); + GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 256, dir, -dir); - int count = Math.min(holes.size(), 8); + int count = Math.min(holes.size(), 256); - // Set BlackHole vec4 uniforms: x=screenU, y=screenV, z=distance, w=lensDirection - for (int i = 0; i < 8; i++) { - String name = "BlackHole[" + i + "]"; + // Upload BlackHole[256] data via UBO (binding point 0, std140) + java.nio.FloatBuffer buf = java.nio.ByteBuffer.allocateDirect(256 * 4 * 4) + .order(java.nio.ByteOrder.nativeOrder()).asFloatBuffer(); + for (int i = 0; i < 256; i++) { if (i < count) { GravitationalLensManager.HoleProjection h = holes.get(i); - pass.getEffect().safeGetUniform(name) - .set(h.centerU, h.centerV, h.cameraDistance, h.lensDirection); + buf.put(h.centerU).put(h.centerV).put(h.cameraDistance).put(h.lensDirection); } else { - pass.getEffect().safeGetUniform(name).set(0.0f, 0.0f, 1.0f, 1.0f); + buf.put(0.0f).put(0.0f).put(1.0f).put(1.0f); + } + } + buf.flip(); + + if (anvilcraft$lensUbo == 0) { + anvilcraft$lensUbo = GL15.glGenBuffers(); + GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, anvilcraft$lensUbo); + GL15.glBufferData(GL31.GL_UNIFORM_BUFFER, buf, GL15.GL_DYNAMIC_DRAW); + } else { + GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, anvilcraft$lensUbo); + GL15.glBufferSubData(GL31.GL_UNIFORM_BUFFER, 0, buf); + } + GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, 0, anvilcraft$lensUbo); + + PostPass pass = passes.getFirst(); + + // Bind UBO block "BlackHoles" to binding point 0 (only needed once per shader load) + int programId = pass.getEffect().getId(); + if (anvilcraft$lensUboBlockBound != programId) { + int blockIndex = GL31.glGetUniformBlockIndex(programId, "BlackHoles"); + if (blockIndex != GL31.GL_INVALID_INDEX) { + GL31.glUniformBlockBinding(programId, blockIndex, 0); } + anvilcraft$lensUboBlockBound = programId; } pass.getEffect().safeGetUniform("BlackHoleCount").set((float) count); diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index 2c587421a4..8d6f9b01ba 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -6,7 +6,9 @@ uniform vec2 InSize; // BlackHole[i]: x=screenU, y=screenV, z=cameraDistance, w=lensDirection // lensDirection > 0: convex (pull toward center, gravitational lens) // lensDirection < 0: concave (push away from center, diverging lens) -uniform vec4 BlackHole[8]; +layout (std140) uniform BlackHoles { + vec4 BlackHole[256]; +}; uniform float BlackHoleCount; uniform float LensStrength; @@ -16,9 +18,9 @@ uniform float PerspectiveScale; in vec2 texCoord; out vec4 fragColor; -vec2 getHolePos(int i) { return BlackHole[i].xy; } -float getHoleDist(int i) { return BlackHole[i].z; } -float getLensDir(int i) { return BlackHole[i].w; } +vec2 getHolePos(int i) { return BlackHole[i].xy; } +float getHoleDist(int i) { return BlackHole[i].z; } +float getLensDir(int i) { return BlackHole[i].w; } void main() { vec2 uv = texCoord; diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json index d0cea6827e..2d55e4f446 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.json @@ -17,15 +17,6 @@ { "name": "InSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, { "name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, - { "name": "BlackHole[0]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, - { "name": "BlackHole[1]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, - { "name": "BlackHole[2]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, - { "name": "BlackHole[3]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, - { "name": "BlackHole[4]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, - { "name": "BlackHole[5]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, - { "name": "BlackHole[6]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, - { "name": "BlackHole[7]", "type": "float", "count": 4, "values": [ 0.0, 0.0, 1.0, 1.0 ] }, - { "name": "BlackHoleCount", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "LensStrength", "type": "float", "count": 1, "values": [ 0.001953125 ] }, { "name": "EventHorizonRadius", "type": "float", "count": 1, "values": [ 0.083333333 ] }, From 088f5ea96f476bb60cc642281987057409cc5d75 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 05:36:10 +0800 Subject: [PATCH 11/17] =?UTF-8?q?refactor(render):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E6=95=88=E6=9E=9C=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将事件视界遮罩计算从硬边缘改为平滑过渡效果 - 重命名黑洞收集方法为更通用的孔洞收集方法 - 更新渲染器中对孔洞收集方法的调用以支持黑白洞统一处理 --- .../anvilcraft/client/support/GravitationalLensManager.java | 2 +- .../java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java | 2 +- .../assets/anvilcraft/shaders/program/gravitational_lens.fsh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index 3e279f8767..0ddcd1b3f4 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -104,7 +104,7 @@ private static Matrix4f buildViewProj(Camera camera, Matrix4f projectionMatrix) * sorted nearest first. Black holes get {@code blackHoleDir} (positive=convex pull), * white holes get {@code whiteHoleDir} (negative=concave push). */ - public static List collectVisibleBlackHoles( + public static List collectVisibleHoles( Camera camera, Matrix4f projectionMatrix, int maxCount, diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 09dc4fe991..3d5fe7d51f 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -204,7 +204,7 @@ void gravitationalLensPostProcess( // Collect visible holes: black holes use positive direction, white holes negative float dir = (float) AnvilCraftClient.CONFIG.gravitationalLens.lensDirection; java.util.List holes = - GravitationalLensManager.collectVisibleBlackHoles(camera, projectionMatrix, 256, dir, -dir); + GravitationalLensManager.collectVisibleHoles(camera, projectionMatrix, 256, dir, -dir); int count = Math.min(holes.size(), 256); diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index 8d6f9b01ba..d57d06d0cd 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -76,7 +76,7 @@ void main() { vec2 toHole = uv - holeUv; toHole.x *= aspectRatio; float dist = length(toHole); - float horizonMask = 1.0 - step(EventHorizonRadius * perspS, dist); + float horizonMask = 1.0 - smoothstep(EventHorizonRadius * perspS * 0.95, EventHorizonRadius * perspS * 1.05, dist); color = mix(color, vec3(0.0, 0.0, 0.0), horizonMask); } From ee1f06868801c7aacbc21bd0dfcb520c9784143a Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 05:41:07 +0800 Subject: [PATCH 12/17] =?UTF-8?q?feat(render):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=BB=91=E6=B4=9E=E6=B8=B2=E6=9F=93=E6=95=B0=E9=87=8F=E9=99=90?= =?UTF-8?q?=E5=88=B6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在客户端配置中添加最大黑洞数量设置选项(范围2-256) - 更新英语和反向英语的语言文件以支持新配置项 - 修改渲染器以使用配置的最大黑洞数量替代硬编码值 - 将渲染逻辑中的固定256限制替换为可配置的最大值 --- src/generated/resources/assets/anvilcraft/lang/en_ud.json | 4 +++- src/generated/resources/assets/anvilcraft/lang/en_us.json | 4 +++- .../dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java | 4 ++++ .../java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java | 5 +++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index ba7b019271..cb6cd84abf 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -150,11 +150,13 @@ "anvilcraft.configuration.gravitational_lens.event_horizon_radius": "snᴉpɐᴚ uozᴉɹoH ʇuǝʌƎ", "anvilcraft.configuration.gravitational_lens.event_horizon_radius.tooltip": "(ʇꞁnɐɟǝp Ɛ80˙0 'sʇᴉun Ʌ∩ uǝǝɹɔs) snᴉpɐɹ uozᴉɹoɥ ʇuǝʌƎ", "anvilcraft.configuration.gravitational_lens.lens_direction": "uoᴉʇɔǝɹᴉᗡ suǝꞀ", - "anvilcraft.configuration.gravitational_lens.lens_direction.tooltip": "˙ɥʇᵷuǝɹʇs sǝꞁɐɔs ǝpnʇᴉuᵷɐW ˙(ʎɐʍɐ ɥsnd) ǝʌɐɔuoɔ = 0 < '(ɹǝʇuǝɔ pɹɐʍoʇ ꞁꞁnd) xǝʌuoɔ = 0 > :uoᴉʇɔǝɹᴉp suǝꞀ", + "anvilcraft.configuration.gravitational_lens.lens_direction.tooltip": "˙ɥʇᵷuǝɹʇs sǝꞁɐɔs ǝpnʇᴉuᵷɐW ˙(ʎɐʍɐ ɥsnd) ǝʌɐɔuoɔ = 0> '(ɹǝʇuǝɔ pɹɐʍoʇ ꞁꞁnd ꞁɐuoᴉʇɐʇᴉʌɐɹᵷ) xǝʌuoɔ = 0< :uoᴉʇɔǝɹᴉp suǝꞀ", "anvilcraft.configuration.gravitational_lens.lens_perspective_scale": "ǝꞁɐɔS ǝʌᴉʇɔǝdsɹǝԀ suǝꞀ", "anvilcraft.configuration.gravitational_lens.lens_perspective_scale.tooltip": "˙ɹǝᵷᵷᴉq = ɹǝsoꞁƆ ˙ǝzᴉs ᵷᴉɟuoɔ = ʇɔǝɟɟǝ 'ǝɔuɐʇsᴉp sᴉɥʇ ʇⱯ ˙ᵷuᴉꞁɐɔs ǝʌᴉʇɔǝdsɹǝd ɹoɟ ǝɔuɐʇsᴉp ǝɔuǝɹǝɟǝᴚ", "anvilcraft.configuration.gravitational_lens.lens_strength": "ɥʇᵷuǝɹʇS suǝꞀ", "anvilcraft.configuration.gravitational_lens.lens_strength.tooltip": "(ʇꞁnɐɟǝp ᘔ00˙0 'ᵷuᴉpuǝq ɹǝᵷuoɹʇs = ɹǝɥᵷᴉɥ) ɥʇᵷuǝɹʇs uoᴉʇɹoʇsᴉp suǝꞀ", + "anvilcraft.configuration.gravitational_lens.max_hole_count": "ʇunoƆ ǝꞁoH xɐW", + "anvilcraft.configuration.gravitational_lens.max_hole_count.tooltip": "˙ǝɔuɐɯɹoɟɹǝd ɹǝʇʇǝq = ɹǝʍoꞁ 'sǝꞁoɥ ǝɹoɯ = ɹǝɥᵷᴉH ˙(9ϛᘔ-ᘔ) pǝɹǝpuǝɹ sǝꞁoɥ ǝʇᴉɥʍ/ʞɔɐꞁq ɟo ɹǝqɯnu ɯnɯᴉxɐW", "anvilcraft.configuration.gravitational_lens.render_black_hole_lensing": "ᵷuᴉsuǝꞀ ǝꞁoH ʞɔɐꞁᗺ ɹǝpuǝᴚ", "anvilcraft.configuration.gravitational_lens.render_black_hole_lensing.tooltip": "sǝꞁoɥ ʞɔɐꞁq ɹɐǝu ʇɔǝɟɟǝ ᵷuᴉssǝɔoɹd-ʇsod ᵷuᴉsuǝꞁ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", "anvilcraft.configuration.gravitational_lens.tooltip": "suǝꞀ ꞁɐuoᴉʇɐʇᴉʌɐɹ⅁", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 3b7b2cde36..3dc588393a 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -150,11 +150,13 @@ "anvilcraft.configuration.gravitational_lens.event_horizon_radius": "Event Horizon Radius", "anvilcraft.configuration.gravitational_lens.event_horizon_radius.tooltip": "Event horizon radius (screen UV units, 0.083 default)", "anvilcraft.configuration.gravitational_lens.lens_direction": "Lens Direction", - "anvilcraft.configuration.gravitational_lens.lens_direction.tooltip": "Lens direction: >0 = convex (pull toward center), <0 = concave (push away). Magnitude scales strength.", + "anvilcraft.configuration.gravitational_lens.lens_direction.tooltip": "Lens direction: >0 = convex (gravitational pull toward center), <0 = concave (push away). Magnitude scales strength.", "anvilcraft.configuration.gravitational_lens.lens_perspective_scale": "Lens Perspective Scale", "anvilcraft.configuration.gravitational_lens.lens_perspective_scale.tooltip": "Reference distance for perspective scaling. At this distance, effect = config size. Closer = bigger.", "anvilcraft.configuration.gravitational_lens.lens_strength": "Lens Strength", "anvilcraft.configuration.gravitational_lens.lens_strength.tooltip": "Lens distortion strength (higher = stronger bending, 0.002 default)", + "anvilcraft.configuration.gravitational_lens.max_hole_count": "Max Hole Count", + "anvilcraft.configuration.gravitational_lens.max_hole_count.tooltip": "Maximum number of black/white holes rendered (2-256). Higher = more holes, lower = better performance.", "anvilcraft.configuration.gravitational_lens.render_black_hole_lensing": "Render Black Hole Lensing", "anvilcraft.configuration.gravitational_lens.render_black_hole_lensing.tooltip": "Gravitational lensing post-processing effect near black holes", "anvilcraft.configuration.gravitational_lens.tooltip": "Gravitational Lens", diff --git a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java index 1dfd9d0e7c..3c73675500 100644 --- a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java +++ b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftClientConfig.java @@ -52,6 +52,10 @@ public static class GravitationalLens { @Comment("Gravitational lensing post-processing effect near black holes") public boolean renderBlackHoleLensing = true; + @Comment("Maximum number of black/white holes rendered (2-256). Higher = more holes, lower = better performance.") + @BoundedDiscrete(min = 2, max = 256) + public int maxHoleCount = 8; + @Comment("Lens distortion strength (higher = stronger bending, 0.002 default)") public double lensStrength = 1.0 / 512.0; diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 3d5fe7d51f..4b795f0e6b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -203,10 +203,11 @@ void gravitationalLensPostProcess( // Collect visible holes: black holes use positive direction, white holes negative float dir = (float) AnvilCraftClient.CONFIG.gravitationalLens.lensDirection; + int maxCount = AnvilCraftClient.CONFIG.gravitationalLens.maxHoleCount; java.util.List holes = - GravitationalLensManager.collectVisibleHoles(camera, projectionMatrix, 256, dir, -dir); + GravitationalLensManager.collectVisibleHoles(camera, projectionMatrix, maxCount, dir, -dir); - int count = Math.min(holes.size(), 256); + int count = Math.min(holes.size(), maxCount); // Upload BlackHole[256] data via UBO (binding point 0, std140) java.nio.FloatBuffer buf = java.nio.ByteBuffer.allocateDirect(256 * 4 * 4) From ef1ebd62410b8c2e15b711d35198dc3e7f7a92b2 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 05:45:07 +0800 Subject: [PATCH 13/17] =?UTF-8?q?style(block):=20=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E9=BB=91=E6=B4=9E=E5=92=8C=E7=99=BD=E6=B4=9E=E6=96=B9=E5=9D=97?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E7=9A=84=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java | 2 +- .../dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java index faa994be1d..67ed295564 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/BlackHoleBlockEntity.java @@ -43,4 +43,4 @@ public void setRemoved() { } } } -} \ No newline at end of file +} diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java index e2a9edd8ba..3e8a932d63 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/WhiteHoleBlockEntity.java @@ -43,4 +43,4 @@ public void setRemoved() { } } } -} \ No newline at end of file +} From 415b70bf6f1d50f6a8e6369a802274cd47f19fc9 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 05:53:24 +0800 Subject: [PATCH 14/17] =?UTF-8?q?perf(renderer):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E6=B8=B2=E6=9F=93=E6=80=A7?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将引力透镜循环限制从 8 提升到 256 以支持更多黑洞 - 为 UBO 数据上传预分配 FloatBuffer 缓冲区以减少内存分配 - 添加 OpenGL 上下文丢失时的 UBO 重建逻辑 - 优化 UBO 更新流程以提高渲染效率 --- .../dubhe/anvilcraft/mixin/LevelRendererMixin.java | 14 ++++++++++---- .../shaders/program/gravitational_lens.fsh | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 4b795f0e6b..509d57f9fe 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -49,12 +49,17 @@ public abstract class LevelRendererMixin { @Final private Minecraft minecraft; - /** UBO handle for BlackHole[256] data — created once, updated per frame. */ + /** UBO handle for BlackHole[256] data — created once, recreated on GL context loss. */ @Unique private static int anvilcraft$lensUbo = 0; /** Last program ID for which we bound the UBO block index. */ @Unique private static int anvilcraft$lensUboBlockBound = 0; + /** Pre-allocated FloatBuffer for UBO upload (256 vec4s * 4 floats = 4096 bytes). */ + @Unique + private static final java.nio.FloatBuffer ANVILCRAFT$LENS_UBO_BUF = + java.nio.ByteBuffer.allocateDirect(256 * 4 * 4) + .order(java.nio.ByteOrder.nativeOrder()).asFloatBuffer(); @Inject( method = "renderLevel", @@ -210,8 +215,8 @@ void gravitationalLensPostProcess( int count = Math.min(holes.size(), maxCount); // Upload BlackHole[256] data via UBO (binding point 0, std140) - java.nio.FloatBuffer buf = java.nio.ByteBuffer.allocateDirect(256 * 4 * 4) - .order(java.nio.ByteOrder.nativeOrder()).asFloatBuffer(); + java.nio.FloatBuffer buf = ANVILCRAFT$LENS_UBO_BUF; + buf.clear(); for (int i = 0; i < 256; i++) { if (i < count) { GravitationalLensManager.HoleProjection h = holes.get(i); @@ -222,7 +227,8 @@ void gravitationalLensPostProcess( } buf.flip(); - if (anvilcraft$lensUbo == 0) { + if (anvilcraft$lensUbo == 0 || !GL15.glIsBuffer(anvilcraft$lensUbo)) { + if (anvilcraft$lensUbo != 0) GL15.glDeleteBuffers(anvilcraft$lensUbo); anvilcraft$lensUbo = GL15.glGenBuffers(); GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, anvilcraft$lensUbo); GL15.glBufferData(GL31.GL_UNIFORM_BUFFER, buf, GL15.GL_DYNAMIC_DRAW); diff --git a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh index d57d06d0cd..ef2f4ad3fa 100644 --- a/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh +++ b/src/main/resources/assets/anvilcraft/shaders/program/gravitational_lens.fsh @@ -30,7 +30,7 @@ void main() { int count = int(BlackHoleCount); // --- Gravitational displacement --- - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 256; i++) { if (i >= count) break; vec2 holeUv = getHolePos(i); @@ -61,7 +61,7 @@ void main() { vec3 color = texture(DiffuseSampler, uv + offset).rgb; // --- Render event horizon (convex, on-screen black holes only) --- - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 256; i++) { if (i >= count) break; // Concave lenses have no event horizon From 59d88c71916de4823c6ef97c588e23bfb6275a3f Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 06:00:50 +0800 Subject: [PATCH 15/17] =?UTF-8?q?refactor(rendering):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=95=9C=E5=A4=B4=E6=95=88=E6=9E=9C=E7=9A=84UBO=E7=BC=93?= =?UTF-8?q?=E5=86=B2=E5=8C=BA=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加anvilcraft$resetLensUbo方法用于重置镜头UBO句柄 - 简化镜头UBO缓冲区创建逻辑,移除冗余的glIsBuffer检查 - 在加载镜头效果时调用重置方法确保缓冲区正确初始化 - 添加适当的注释和抑制警告注解 --- .../dubhe/anvilcraft/client/init/ModShaders.java | 2 ++ .../anvilcraft/mixin/LevelRendererMixin.java | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java b/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java index 4fe4ae0c83..3dc42bec07 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java @@ -2,6 +2,7 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat; import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.mixin.LevelRendererMixin; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.PostChain; @@ -140,6 +141,7 @@ public static void loadBloomEffect(ResourceProvider resourceProvider) throws IOE } public static void loadLensEffect(ResourceProvider resourceProvider) throws IOException { + LevelRendererMixin.anvilcraft$resetLensUbo(); try { lensChain = new PostChain( MINECRAFT.getTextureManager(), diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 509d57f9fe..5a7e57e24d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -61,6 +61,19 @@ public abstract class LevelRendererMixin { java.nio.ByteBuffer.allocateDirect(256 * 4 * 4) .order(java.nio.ByteOrder.nativeOrder()).asFloatBuffer(); + /** + * Reset the lens UBO handle — call on shader reload or GL context recreation + * so the next frame allocates a fresh buffer. + */ + @SuppressWarnings("unused") // called from ModShaders.loadLensEffect() + public static void anvilcraft$resetLensUbo() { + if (anvilcraft$lensUbo != 0) { + GL15.glDeleteBuffers(anvilcraft$lensUbo); + anvilcraft$lensUbo = 0; + } + anvilcraft$lensUboBlockBound = 0; + } + @Inject( method = "renderLevel", at = @At( @@ -227,8 +240,7 @@ void gravitationalLensPostProcess( } buf.flip(); - if (anvilcraft$lensUbo == 0 || !GL15.glIsBuffer(anvilcraft$lensUbo)) { - if (anvilcraft$lensUbo != 0) GL15.glDeleteBuffers(anvilcraft$lensUbo); + if (anvilcraft$lensUbo == 0) { anvilcraft$lensUbo = GL15.glGenBuffers(); GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, anvilcraft$lensUbo); GL15.glBufferData(GL31.GL_UNIFORM_BUFFER, buf, GL15.GL_DYNAMIC_DRAW); From 33dd379f5b3cfa47d0cc6bfa5351f6f405180da8 Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 06:06:31 +0800 Subject: [PATCH 16/17] =?UTF-8?q?refactor(render):=20=E5=B0=86=E5=BC=95?= =?UTF-8?q?=E5=8A=9B=E9=80=8F=E9=95=9CUBO=E7=AE=A1=E7=90=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=BB=8E=E6=B8=B2=E6=9F=93=E5=99=A8=E4=B8=AD=E6=8F=90?= =?UTF-8?q?=E5=8F=96=E5=88=B0=E4=B8=93=E7=94=A8=E7=AE=A1=E7=90=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将GL相关的导入和UBO缓冲区管理代码移动到GravitationalLensManager类 - 在GravitationalLensManager中实现统一的UBO上传和状态重置方法 - 移除LevelRendererMixin中的UBO相关字段和处理逻辑 - 更新ModShaders中对UBO重置方法的调用引用 - 使用新的管理类方法处理透镜效果的均匀缓冲区对象 - 简化渲染流程中的黑洞数据上传实现 --- .../anvilcraft/client/init/ModShaders.java | 7 +- .../support/GravitationalLensManager.java | 69 +++++++++++++++++++ .../anvilcraft/mixin/LevelRendererMixin.java | 62 +---------------- 3 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java b/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java index 3dc42bec07..2579faea6f 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/init/ModShaders.java @@ -2,7 +2,7 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat; import dev.dubhe.anvilcraft.AnvilCraft; -import dev.dubhe.anvilcraft.mixin.LevelRendererMixin; +import dev.dubhe.anvilcraft.client.support.GravitationalLensManager; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.PostChain; @@ -42,7 +42,8 @@ public class ModShaders { public static void register(RegisterShadersEvent event) { try { - event.registerShader(new ShaderInstance( + event.registerShader( + new ShaderInstance( event.getResourceProvider(), AnvilCraft.of("rendertype_laser"), DefaultVertexFormat.BLOCK @@ -141,7 +142,7 @@ public static void loadBloomEffect(ResourceProvider resourceProvider) throws IOE } public static void loadLensEffect(ResourceProvider resourceProvider) throws IOException { - LevelRendererMixin.anvilcraft$resetLensUbo(); + GravitationalLensManager.resetLensUbo(); try { lensChain = new PostChain( MINECRAFT.getTextureManager(), diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index 0ddcd1b3f4..69aff4b5dd 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -8,6 +8,13 @@ import org.joml.Vector3f; import org.joml.Vector4f; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL31; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -150,4 +157,66 @@ private static void collectFromSet( out.add(new HoleProjection(centerUV.x, centerUV.y, dist, lensDir)); } } + + // ---- UBO management ---- + + /** Pre-allocated FloatBuffer for UBO upload (256 vec4s × 4 floats = 4096 bytes). */ + private static final FloatBuffer LENS_UBO_BUF = + ByteBuffer.allocateDirect(256 * 4 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + /** UBO handle — created on first frame, reset on shader reload. */ + private static int lensUbo = 0; + /** Last program ID for which the UBO block index was bound. */ + private static int lensUboBlockBound = 0; + + /** + * Upload hole data to the UBO and bind it. Call each frame before running the lens post-chain. + * + * @param holes collected hole projections (≤ 256) + * @param count actual number of holes to write (remaining slots zeroed) + * @param programId the shader program ID, for one-time block-index binding + */ + public static void uploadLensUbo(List holes, int count, int programId) { + FloatBuffer buf = LENS_UBO_BUF; + buf.clear(); + for (int i = 0; i < 256; i++) { + if (i < count) { + HoleProjection h = holes.get(i); + buf.put(h.centerU).put(h.centerV).put(h.cameraDistance).put(h.lensDirection); + } else { + buf.put(0.0f).put(0.0f).put(1.0f).put(1.0f); + } + } + buf.flip(); + + if (lensUbo == 0) { + lensUbo = GL15.glGenBuffers(); + GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, lensUbo); + GL15.glBufferData(GL31.GL_UNIFORM_BUFFER, buf, GL15.GL_DYNAMIC_DRAW); + } else { + GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, lensUbo); + GL15.glBufferSubData(GL31.GL_UNIFORM_BUFFER, 0, buf); + } + GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, 0, lensUbo); + + // Bind UBO block "BlackHoles" to binding point 0 (once per shader load) + if (lensUboBlockBound != programId) { + int blockIndex = GL31.glGetUniformBlockIndex(programId, "BlackHoles"); + if (blockIndex != GL31.GL_INVALID_INDEX) { + GL31.glUniformBlockBinding(programId, blockIndex, 0); + } + lensUboBlockBound = programId; + } + } + + /** + * Free the UBO and reset state. Call on shader reload / GL context recreation. + */ + public static void resetLensUbo() { + if (lensUbo != 0) { + GL15.glDeleteBuffers(lensUbo); + lensUbo = 0; + } + lensUboBlockBound = 0; + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 5a7e57e24d..d83649739a 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -32,13 +32,9 @@ import net.minecraft.client.renderer.ShaderInstance; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL30; -import org.lwjgl.opengl.GL31; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -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; @@ -49,31 +45,6 @@ public abstract class LevelRendererMixin { @Final private Minecraft minecraft; - /** UBO handle for BlackHole[256] data — created once, recreated on GL context loss. */ - @Unique - private static int anvilcraft$lensUbo = 0; - /** Last program ID for which we bound the UBO block index. */ - @Unique - private static int anvilcraft$lensUboBlockBound = 0; - /** Pre-allocated FloatBuffer for UBO upload (256 vec4s * 4 floats = 4096 bytes). */ - @Unique - private static final java.nio.FloatBuffer ANVILCRAFT$LENS_UBO_BUF = - java.nio.ByteBuffer.allocateDirect(256 * 4 * 4) - .order(java.nio.ByteOrder.nativeOrder()).asFloatBuffer(); - - /** - * Reset the lens UBO handle — call on shader reload or GL context recreation - * so the next frame allocates a fresh buffer. - */ - @SuppressWarnings("unused") // called from ModShaders.loadLensEffect() - public static void anvilcraft$resetLensUbo() { - if (anvilcraft$lensUbo != 0) { - GL15.glDeleteBuffers(anvilcraft$lensUbo); - anvilcraft$lensUbo = 0; - } - anvilcraft$lensUboBlockBound = 0; - } - @Inject( method = "renderLevel", at = @At( @@ -227,40 +198,9 @@ void gravitationalLensPostProcess( int count = Math.min(holes.size(), maxCount); - // Upload BlackHole[256] data via UBO (binding point 0, std140) - java.nio.FloatBuffer buf = ANVILCRAFT$LENS_UBO_BUF; - buf.clear(); - for (int i = 0; i < 256; i++) { - if (i < count) { - GravitationalLensManager.HoleProjection h = holes.get(i); - buf.put(h.centerU).put(h.centerV).put(h.cameraDistance).put(h.lensDirection); - } else { - buf.put(0.0f).put(0.0f).put(1.0f).put(1.0f); - } - } - buf.flip(); - - if (anvilcraft$lensUbo == 0) { - anvilcraft$lensUbo = GL15.glGenBuffers(); - GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, anvilcraft$lensUbo); - GL15.glBufferData(GL31.GL_UNIFORM_BUFFER, buf, GL15.GL_DYNAMIC_DRAW); - } else { - GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, anvilcraft$lensUbo); - GL15.glBufferSubData(GL31.GL_UNIFORM_BUFFER, 0, buf); - } - GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, 0, anvilcraft$lensUbo); - PostPass pass = passes.getFirst(); - // Bind UBO block "BlackHoles" to binding point 0 (only needed once per shader load) - int programId = pass.getEffect().getId(); - if (anvilcraft$lensUboBlockBound != programId) { - int blockIndex = GL31.glGetUniformBlockIndex(programId, "BlackHoles"); - if (blockIndex != GL31.GL_INVALID_INDEX) { - GL31.glUniformBlockBinding(programId, blockIndex, 0); - } - anvilcraft$lensUboBlockBound = programId; - } + GravitationalLensManager.uploadLensUbo(holes, count, pass.getEffect().getId()); pass.getEffect().safeGetUniform("BlackHoleCount").set((float) count); pass.getEffect().safeGetUniform("LensStrength") From d20b140b638dbbc7b9b616ee707a55239704fd6b Mon Sep 17 00:00:00 2001 From: Gugle Date: Tue, 23 Jun 2026 06:09:02 +0800 Subject: [PATCH 17/17] =?UTF-8?q?style(gravity):=20=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96=E5=BC=95=E5=8A=9B=E9=80=8F=E9=95=9C=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=99=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除多余的空行和导入语句 - 为类字段添加标准文档注释 - 调整条件语句的括号格式 - 统一代码风格和缩进 --- .../support/GravitationalLensManager.java | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java index 69aff4b5dd..c632756220 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/GravitationalLensManager.java @@ -7,7 +7,6 @@ import org.joml.Vector2f; import org.joml.Vector3f; import org.joml.Vector4f; - import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL31; @@ -25,10 +24,14 @@ public class GravitationalLensManager { private static final int MAX_SEARCH_DISTANCE_SQR = 256 * 256; - /** Client-side cache of loaded black hole block positions. */ + /** + * Client-side cache of loaded black hole block positions. + */ public static final Set CLIENT_BLACK_HOLE_POSITIONS = Collections.newSetFromMap(new ConcurrentHashMap<>()); - /** Client-side cache of loaded white hole block positions. */ + /** + * Client-side cache of loaded white hole block positions. + */ public static final Set CLIENT_WHITE_HOLE_POSITIONS = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -52,12 +55,18 @@ public static void unregisterWhiteHole(BlockPos pos) { * Per-hole data passed to the shader. */ public static final class HoleProjection { - /** Center UV of the black hole on screen. */ + /** + * Center UV of the black hole on screen. + */ public final float centerU; public final float centerV; - /** Distance from camera to black hole (world units). */ + /** + * Distance from camera to black hole (world units). + */ public final float cameraDistance; - /** Lens direction: > 0 = convex (pull), < 0 = concave (push). */ + /** + * Lens direction: > 0 = convex (pull), < 0 = concave (push). + */ public final float lensDirection; HoleProjection(float cu, float cv, float dist, float dir) { @@ -151,7 +160,9 @@ private static void collectFromSet( if (centerUV == null) continue; if (centerUV.x < -0.2f || centerUV.x > 1.2f - || centerUV.y < -0.2f || centerUV.y > 1.2f) continue; + || centerUV.y < -0.2f || centerUV.y > 1.2f) { + continue; + } float dist = (float) Math.sqrt(distanceSqr); out.add(new HoleProjection(centerUV.x, centerUV.y, dist, lensDir)); @@ -160,13 +171,19 @@ private static void collectFromSet( // ---- UBO management ---- - /** Pre-allocated FloatBuffer for UBO upload (256 vec4s × 4 floats = 4096 bytes). */ + /** + * Pre-allocated FloatBuffer for UBO upload (256 vec4s × 4 floats = 4096 bytes). + */ private static final FloatBuffer LENS_UBO_BUF = ByteBuffer.allocateDirect(256 * 4 * 4) .order(ByteOrder.nativeOrder()).asFloatBuffer(); - /** UBO handle — created on first frame, reset on shader reload. */ + /** + * UBO handle — created on first frame, reset on shader reload. + */ private static int lensUbo = 0; - /** Last program ID for which the UBO block index was bound. */ + /** + * Last program ID for which the UBO block index was bound. + */ private static int lensUboBlockBound = 0; /**