From daf36fdeacca707930092363589c89984af78f3b Mon Sep 17 00:00:00 2001 From: koka Date: Fri, 18 Sep 2026 01:24:13 +0200 Subject: [PATCH] Adapt solar volume density to weather for beta.138 --- README.md | 17 ++-- docs/shader-beta138.md | 86 +++++++++++++++++++ gradle.properties | 4 +- .../shader/PixelShadows122ClientChecks.java | 4 +- .../SunshaftWeather138ClientChecks.java | 39 +++++++++ .../shader/Sunshafts137ClientChecks.java | 11 +-- .../sanctuary/client/shader/PixelShadows.java | 7 +- .../sanctuary/client/shader/Sunshafts.java | 22 ++++- .../assets/sanctuary/lang/en_us.json | 2 +- .../assets/sanctuary/lang/fr_fr.json | 2 +- .../shaders/core/sunshaft_gather.fsh | 11 ++- .../sanctuary/shaders/include/sunshafts.glsl | 5 +- packwiz/pack.toml | 2 +- 13 files changed, 185 insertions(+), 27 deletions(-) create mode 100644 docs/shader-beta138.md create mode 100644 mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/SunshaftWeather138ClientChecks.java diff --git a/README.md b/README.md index d968370..a505c18 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,11 @@ La [vision complète](docs/vision.md) conserve les intentions ; le L’économie, les claims et les dimensions décrits dans la vision restent à implémenter. -La version **beta.137** ajoute des **rayons solaires volumétriques** qui traversent -les ouvertures du décor et le feuillage. Activés à 35 %, réglables indépendamment -du bloom dans **Options → Shaders**. Carte d'ombres partagée, portée de 64 blocs -maximum. Le [ticket](docs/shader-beta137.md) détaille les vérifications. -Release publiée et même instance Prism synchronisée ; sauvegardes et réglages conservés. +La version **beta.138** adapte l’épaisseur des **rayons solaires à la météo** : +plus denses et diffus dans le brouillard, renforcés sous les précipitations, +avec moins de lumière pendant l’orage. Le curseur reste inchangé. +Le [ticket](docs/shader-beta138.md) détaille le rendu et ses vérifications. +Livraison en cours de vérification. La [livraison précédente](docs/clay-workshop-integration-beta110.md) réunit déjà l’atelier, les œufs et la neige saisonnière dans l’instance **Sanctuary Beta**. Les [synchronisations antérieures](docs/git-sync-beta092.md) restent documentées. @@ -35,6 +35,13 @@ La [carte native beta.005](docs/atlas-beta005.md) prend le relais après l’étude de Xaero et des alternatives libres. +## beta.138 — Rayons et brouillard + +Densité automatique des rayons selon le brouillard et les précipitations. +[Contrat et vérifications](docs/shader-beta138.md) · +[Pack normal](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.138/Sanctuary-beta.138.mrpack) · +[Pack de test](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.138/Sanctuary-Test-beta.138.mrpack). + ## beta.137 — Rayons solaires Faisceaux volumétriques légers, intensité réglable et feuillage perméable. diff --git a/docs/shader-beta138.md b/docs/shader-beta138.md new file mode 100644 index 0000000..41d5161 --- /dev/null +++ b/docs/shader-beta138.md @@ -0,0 +1,86 @@ +# SHADER-138 — Épaisseur des rayons et météo + +Demande : épaissir les sunshafts avec la météo, surtout le brouillard. +Socle beta.137, branche `codex/sunshaft-weather-beta138`, Minecraft 26.3 / Java 25. +Aucune modification des règles météo, des sauvegardes ou du gameplay. + +## Contrat + +Densité et diffusion liées au brouillard environnemental réellement rendu, +indépendamment de la limite de distance de rendu. Pluie/neige natives : +renforcement plus modéré ; orage : réduction supplémentaire du soleil. +Transitions continues, mêmes deux passes et seize segments. Le réglage +manuel conserve sa valeur, aucune nouvelle option. + +## Rendu + +Le brouillard utilise `FogData.environmentalEnd`, séparé de la distance de +rendu native. Sa contribution croît en douceur entre 192 et 80 blocs de +visibilité. La densité passe de ×1 à ×3,5 pour le brouillard seul ; la diffusion +angulaire s'élargit et la lumière lointaine s'éteint plus vite. Les coefficients +ne dépendent d'aucun bruit animé ni historique d'image. + +Pluie et neige utilisent le niveau natif de précipitation : densité jusqu'à +×1,8, diffusion plus légère. La lumière disponible tombe progressivement à +25 % sous une pluie maximale et à 8,75 % sous un orage maximal. La carte solaire +reste disponible pour le volume même lorsque Minecraft cache entièrement +son disque solaire ; les ombres sur les surfaces conservent leur comportement. +Le brouillard et la météo continuent leurs transitions natives. + +Même cible au quart de chaque dimension, deux passes, seize segments et +quatre lectures de la carte par segment. L'empreinte de l'ombre ne change pas : +la météo ne crée pas d'ouverture artificielle à travers un mur. + +## Vérification + +Scénario client natif réussi en **3 min 29 s**, macOS / Apple M1 / OpenGL. +Les contrôles des volumes beta.137 passent à nouveau : ouvertures, feuilles, +pièce fermée, mouvement/retour, proximité, soleil hors champ, intensité, +OFF/zéro/nuit, redimensionnement, rechargement, préférences et libellés FR/EN. + +La suite utilise ensuite les vraies commandes serveur de météo : clair, +`weather fog`, pluie, orage et retour au clair, dans le même décor avec le +curseur maintenu à 35. Transition progressive du brouillard, rechargement +sous brouillard, absence d'éclairage d'une pièce fermée et restitution de +l'image claire initiale vérifiés. La contribution normalisée du volume dans +cette scène passe d'environ 0,0034 au clair à 0,0236 sous brouillard ; ce n'est +pas un multiplicateur universel de luminosité. L'orage reste sous la pluie. + +Log : `build/shader138-client-final.log`, marqueurs `SUNSHAFT137_PASS` et +`SUNSHAFT138_WEATHER_PASS`. Comparatif natif autonome : +`build/Shader-beta.138-comparaison.html`. + +## Diagnostic de la pluie + +La première exécution native a validé le brouillard, puis s'est arrêtée en +attendant des rayons sous une pluie maximale. Minecraft 26.3 fournit alors +`rainBrightness = 1 - rainLevel`, soit zéro : beta.137 ne construisait plus la +carte solaire. Le volume reçoit désormais son atténuation météo propre et +conserve la carte nécessaire, sans réactiver les ombres de surface sous la +pluie maximale. Log de diagnostic : `build/shader138-client-first.log`. + +## Limites + +Coefficients artistiques bornés, sans simulation physique de gouttes ni de +nuages. Le brouillard environnemental est distinct de la simple distance +d'affichage ; la pluie et la neige partagent le niveau natif de précipitation. +Les limites des volumes beta.137 restent applicables : 64 blocs maximum, +formes de la carte d'ombres et feuillage approximé. Pas de mesure de FPS ni +de validation Windows/Vulkan revendiquée. + + +## Construction et archives + +`./gradlew check build assemblePack assembleTestPack -x :sanctuary:runGameTest` +réussit en **2 min 20 s**, 126 tâches. Le serveur GameTest dédié reste exclu +conformément au refus EULA antérieur. Log : `build/shader138-build.log`. +Comparaison à beta.137 : **6 entrées de production** modifiées, uniquement +le rendu et les aides FR/EN. Gameplay et textures des gemmes strictement +conservés. JAR normal/Test identique, sources et template vérifiés. +Reçu local : `build/shader138-artifact.json`. + +- `Sanctuary-beta.138.mrpack` : 10432860 octets ; SHA-256 `67093d3a53e3d90c31e8fb940d709ccf41ad730595e358346fa27ad744b44460`. + +- `Sanctuary-Test-beta.138.mrpack` : 10451787 octets ; SHA-256 `1ff39158b47f44a831b6506c8c2636441b2bff02644d20b1698330097e5ff059`. + +JAR : `44b6dd14f0e962fcb6f1efd61e0e7bb2c8dbb033cef81598582ec16c46a46518`. diff --git a/gradle.properties b/gradle.properties index 52b0c7d..7503cac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,8 +9,8 @@ loom_version=1.17.20 fabric_api_version=0.160.5+26.3 # Release counter: beta.001, .002, .003, ... (see docs/versioning.md). -mod_version=beta.137 -pack_version=beta.137 +mod_version=beta.138 +pack_version=beta.138 resource_pack_version=beta.121 maven_group=fr.koka.sanctuary jei_version=30.32.0-sanctuary.3 diff --git a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/PixelShadows122ClientChecks.java b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/PixelShadows122ClientChecks.java index efa0af5..77e6e82 100644 --- a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/PixelShadows122ClientChecks.java +++ b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/PixelShadows122ClientChecks.java @@ -268,7 +268,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest { }); }); c.waitTicks(3); - c.takeScreenshot("sanctuary-beta137-shader-options-" + locale); + c.takeScreenshot("sanctuary-beta138-shader-options-" + locale); } c.runOnClient(m -> m.gui.setScreen(null)); } @@ -552,7 +552,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest { } static Frame capture(ClientGameTestContext c, String name) { - if (name != null) c.takeScreenshot("sanctuary-beta137-shader-" + name); + if (name != null) c.takeScreenshot("sanctuary-beta138-shader-" + name); var result = new CompletableFuture(); c.runOnClient(m -> Screenshot.takeScreenshot(m.gameRenderer.mainRenderTarget(), image -> { try (image) { diff --git a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/SunshaftWeather138ClientChecks.java b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/SunshaftWeather138ClientChecks.java new file mode 100644 index 0000000..fd9ccb9 --- /dev/null +++ b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/SunshaftWeather138ClientChecks.java @@ -0,0 +1,39 @@ +package fr.koka.sanctuary.client.shader; + +import static fr.koka.sanctuary.client.shader.PixelShadows122ClientChecks.*; +import static fr.koka.sanctuary.client.shader.Sunshafts137ClientChecks.*; +import fr.koka.sanctuary.client.WeatherClient; +import fr.koka.sanctuary.weather.DailyWeather; +import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext; +import net.fabricmc.fabric.api.client.gametest.v1.context.TestServerContext; + +/** Actual server weather -> native atmospheric fog -> shader scattering, same scene and manual strength. */ +final class SunshaftWeather138ClientChecks { + static void run(ClientGameTestContext c,TestServerContext server){ + check(Sunshafts.fogDensity(1024)==0&&Sunshafts.fogDensity(192)==0&&Sunshafts.fogDensity(80)==1,"Clear and full fog endpoints"); + float previous=0;for(int end=192;end>=80;end--){float d=Sunshafts.fogDensity(end);check(d>=previous&&d-previous<.014,"Fog density is bounded, smooth and monotonic");previous=d;} + check(Sunshafts.fogDensity(Float.POSITIVE_INFINITY)==0&&Sunshafts.fogDensity(Float.NaN)==0,"Invalid fog distance cannot create NaN shafts"); + c.runOnClient(m->{VanillaLight.setSunshafts(false);VanillaLight.setSunshaftIntensity(35);VanillaLight.setPixelShadows(false);VanillaLight.setBloom(false);VanillaLight.setLensFlare(false);VanillaLight.setOreEmission(false);VanillaLight.setPbr(false);VanillaLight.setSsgi(false);VanillaLight.setEdgeHighlights(false);}); + roof(server,1);camera(c,server,100.5,101,110.5,70);aim(c,server); + profile(c,server,DailyWeather.Kind.CLEAR);enable(c,false);var clearOff=capture(c,"weather-shafts-clear-off");enable(c,true);var clear=capture(c,"weather-shafts-clear-35");double clearEnergy=energy(clearOff,clear); + server.runOnServer(s->command(s,"weather fog"));c.waitFor(m->WeatherClient.state().kind()==DailyWeather.Kind.FOG,200); + float[] transition=new float[12];for(int i=0;iSunshafts.fogDensity(m.gameRenderer.gameRenderState().levelRenderState.cameraRenderState.fogData.environmentalEnd));if(i>0)check(transition[i]>=transition[i-1]-.002&&transition[i]-transition[i-1]<.4,"Live fog transition has no abrupt density jump");} + c.runOnClient(m->{check(m.gameRenderer.gameRenderState().levelRenderState.cameraRenderState.fogData.environmentalEnd<=80.01,"Fog command reaches actual native FogData");check(Sunshafts.applied(),"Atmospheric fog does not disable rays");}); + enable(c,false);var fogOff=capture(c,"weather-shafts-fog-off");enable(c,true);var fog=capture(c,"weather-shafts-fog-35");double fogEnergy=energy(fogOff,fog); + check(fogEnergy>clearEnergy*1.7,"Fog visibly thickens scattering at the same intensity: "+clearEnergy+" -> "+fogEnergy);unchanged(fog,capture(c,null),"Stable fog adds no animated noise"); + var reload=c.computeOnClient(net.minecraft.client.Minecraft::reloadResourcePacks);c.waitFor(m->reload.isDone()&&m.gui.overlay()==null,1200);reload.join();settle(c);unchanged(fog,capture(c,"weather-shafts-fog-reload"),"Weather coupling survives shader reload"); + roof(server,0);settle(c);enable(c,false);var sealedOff=capture(c,null);enable(c,true);unchanged(sealedOff,capture(c,"weather-shafts-fog-sealed"),"Dense fog cannot illuminate a sealed room through walls");roof(server,1);settle(c); + profile(c,server,DailyWeather.Kind.RAIN);enable(c,false);var rainOff=capture(c,"weather-shafts-rain-off");enable(c,true);double rainEnergy=energy(rainOff,capture(c,"weather-shafts-rain-35"));check(rainEnergy>0&&rainEnergycheck(VanillaLight.sunshaftIntensity()==35,"Weather never changes the player's intensity setting")); + c.runOnClient(m->VanillaLight.setSunshaftIntensity(0));c.waitTicks(10);c.runOnClient(m->check(!Sunshafts.allocated(),"Zero still frees atmospheric scattering")); + System.out.println("SUNSHAFT138_WEATHER_PASS clear="+clearEnergy+" fog="+fogEnergy+" rain="+rainEnergy+" storm="+stormEnergy+" smooth transitions, native fog, closed room, reload, restored clear and manual setting"); + } + private static void profile(ClientGameTestContext c,TestServerContext server,DailyWeather.Kind kind){ + server.runOnServer(s->command(s,"sanctuary weather "+kind.key())); + c.waitFor(m->WeatherClient.state().active()&&WeatherClient.state().kind()==kind,200); + c.waitFor(m->(kind.raining()?m.level.getRainLevel(1)>.999:m.level.getRainLevel(1)<.001)&&(kind.thundering()?m.level.getThunderLevel(1)>.999:m.level.getThunderLevel(1)<.001),600); + c.waitTicks(240); + } +} diff --git a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Sunshafts137ClientChecks.java b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Sunshafts137ClientChecks.java index d3549c0..99429e3 100644 --- a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Sunshafts137ClientChecks.java +++ b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Sunshafts137ClientChecks.java @@ -50,14 +50,15 @@ public final class Sunshafts137ClientChecks implements FabricClientGameTest { c.runOnClient(m->{VanillaLight.setSunshafts(false);});c.waitTicks(10);c.runOnClient(m->check(!Sunshafts.allocated()&&PixelShadows.applied(),"OFF preserves independently enabled surface shadows")); for(String locale:new String[]{"fr_fr","en_us"})c.runOnClient(m->{m.getLanguageManager().setSelected(locale);m.getLanguageManager().onResourceManagerReload(m.getResourceManager());for(String suffix:new String[]{"sunshafts","sunshafts_help","sunshaft_intensity","sunshaft_intensity_help"}){String key="sanctuary.shaders."+suffix;check(!Component.translatable(key).getString().equals(key),"Localized volume controls: "+locale);}}); System.out.println("SUNSHAFT137_PASS slotGain="+low+" leafGain="+leaf+" openGain="+open+" closed room, offscreen sun, camera return, proximity, reload/resize, shared map, zero/night, preferences, FR/EN"); + SunshaftWeather138ClientChecks.run(c,server); } - private static void roof(TestServerContext server,int mode){server.runOnServer(s->{for(int x=76;x<=124;x++)for(int z=76;z<=124;z++)s.overworld().setBlockAndUpdate(new BlockPos(x,113,z),(mode==3||mode==1&&Math.floorMod(z,8)<3?Blocks.AIR:mode==2?Blocks.OAK_LEAVES:Blocks.OAK_PLANKS).defaultBlockState().trySetValue(net.minecraft.world.level.block.state.properties.BlockStateProperties.PERSISTENT,true));});} - private static void aim(ClientGameTestContext c,TestServerContext server){var d=c.computeOnClient(m->PixelShadows.sunDirection(m.gameRenderer.gameRenderState().levelRenderState.skyRenderState.sunAngle));look(c,server,Mth.wrapDegrees((float)Math.toDegrees(Math.atan2(-d.x,d.z))+25),-12);} - private static void settle(ClientGameTestContext c){int before=c.computeOnClient(m->Sunshafts.renderedFrames());c.waitFor(m->Sunshafts.applied()&&PixelShadows.ready()&&Sunshafts.renderedFrames()>before+10,1200);c.waitTicks(30);} - private static void enable(ClientGameTestContext c,boolean enabled){c.runOnClient(m->VanillaLight.setSunshafts(enabled));if(enabled)settle(c);else c.waitTicks(10);} + static void roof(TestServerContext server,int mode){server.runOnServer(s->{for(int x=76;x<=124;x++)for(int z=76;z<=124;z++)s.overworld().setBlockAndUpdate(new BlockPos(x,113,z),(mode==3||mode==1&&Math.floorMod(z,8)<3?Blocks.AIR:mode==2?Blocks.OAK_LEAVES:Blocks.OAK_PLANKS).defaultBlockState().trySetValue(net.minecraft.world.level.block.state.properties.BlockStateProperties.PERSISTENT,true));});} + static void aim(ClientGameTestContext c,TestServerContext server){var d=c.computeOnClient(m->PixelShadows.sunDirection(m.gameRenderer.gameRenderState().levelRenderState.skyRenderState.sunAngle));look(c,server,Mth.wrapDegrees((float)Math.toDegrees(Math.atan2(-d.x,d.z))+25),-12);} + static void settle(ClientGameTestContext c){int before=c.computeOnClient(m->Sunshafts.renderedFrames());c.waitFor(m->Sunshafts.applied()&&PixelShadows.ready()&&Sunshafts.renderedFrames()>before+10,1200);c.waitTicks(30);} + static void enable(ClientGameTestContext c,boolean enabled){c.runOnClient(m->VanillaLight.setSunshafts(enabled));if(enabled)settle(c);else c.waitTicks(10);} private static long gain(Frame off,Frame on){long sum=0;for(int i=0;i>shift&255)-(off.pixels()[i]>>shift&255));return sum;} // Screen blending has less headroom on a brighter vanilla wall. Remove that factor // before comparing roofs, and exclude the roof itself where geometry changes. - private static double energy(Frame off,Frame on){double sum=0;int n=0;for(int y=off.height()/3;y>shift&255,b=on.pixels()[i]>>shift&255;if(a<240){sum+=Math.max(0,b-a)/(255.0-a);n++;}}}return sum/Math.max(1,n);} + static double energy(Frame off,Frame on){double sum=0;int n=0;for(int y=off.height()/3;y>shift&255,b=on.pixels()[i]>>shift&255;if(a<240){sum+=Math.max(0,b-a)/(255.0-a);n++;}}}return sum/Math.max(1,n);} private static void preferences(){try{var p=FabricLoader.getInstance().getConfigDir().resolve("sanctuary-shaders.json");Files.writeString(p,"{\"bloom\":false,\"pixelShadows\":false}");forgetPreferences();check(VanillaLight.sunshafts()&&VanillaLight.sunshaftIntensity()==35&&!VanillaLight.bloom()&&!VanillaLight.pixelShadows(),"Missing shaft settings preserve existing choices");VanillaLight.setSunshafts(false);VanillaLight.setSunshaftIntensity(73);forgetPreferences();check(!VanillaLight.sunshafts()&&VanillaLight.sunshaftIntensity()==73,"Custom shafts persist");for(int n:new int[]{-10,0,35,100,150}){VanillaLight.setSunshaftIntensity(n);forgetPreferences();check(VanillaLight.sunshaftIntensity()==Math.clamp(n,0,100),"Intensity clamps and persists");}VanillaLight.setSunshaftIntensity(35);}catch(java.io.IOException e){throw new AssertionError(e);}} } diff --git a/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/PixelShadows.java b/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/PixelShadows.java index be5cf28..32056f2 100644 --- a/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/PixelShadows.java +++ b/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/PixelShadows.java @@ -91,7 +91,10 @@ public final class PixelShadows { float moon = sky.moonPhase == null ? 1 : (float)(.5 + .5 * Math.cos(sky.moonPhase.index() * Math.PI / 4)); float strength = (lunar ? .10f * moon : .38f) * Math.clamp((sun.y - .08f) / .16f, 0, 1) * sky.rainBrightness; - if (strength <= .001f) return; + // Rain hides the native solar sprite completely. The atmospheric volume still + // needs occluders for its weak scattered daylight; surface shadows stay off. + boolean solarVolume=!lunar&&VanillaLight.sunshafts()&&VanillaLight.sunshaftIntensity()>0; + if (strength <= .001f && !solarVolume) return; int distance = VanillaLight.shadowDistance(), pixels = VanillaLight.shadowPixels(); TERRAIN.update(client.level, camera.pos, distance, sun); int size = mapSize(distance, pixels); @@ -142,7 +145,7 @@ public final class PixelShadows { } } depthReady = !lunar; - if (!VanillaLight.pixelShadows()) { renderedFrames++; return; } + if (!VanillaLight.pixelShadows() || strength <= .001f) { renderedFrames++; return; } var main = client.gameRenderer.mainRenderTarget(); var nearest = RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST); try (var pass = encoder.createRenderPass(() -> "Sanctuary pixel shadows", main.getColorTextureView(), Optional.empty())) { diff --git a/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/Sunshafts.java b/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/Sunshafts.java index 382b99a..7f48c2d 100644 --- a/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/Sunshafts.java +++ b/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/Sunshafts.java @@ -45,11 +45,21 @@ public final class Sunshafts { if(!PixelShadows.depthReady()){release();return;} var main=mc.gameRenderer.mainRenderTarget();int width=Math.max(1,(main.width+3)/4),height=Math.max(1,(main.height+3)/4); if(light==null||light.width!=width||light.height!=height){release();light=new TextureTarget("Sanctuary sunshafts",width,height,GpuFormat.RGBA16_FLOAT,null);} - if(settings==null)settings=new MappableRingBuffer(()->"Sanctuary sunshafts",GpuBuffer.USAGE_UNIFORM|GpuBuffer.USAGE_MAP_WRITE,32); - var fog=mc.gameRenderer.gameRenderState().levelRenderState.cameraRenderState.fogData; + if(settings==null)settings=new MappableRingBuffer(()->"Sanctuary sunshafts",GpuBuffer.USAGE_UNIFORM|GpuBuffer.USAGE_MAP_WRITE,48); + var camera=mc.gameRenderer.gameRenderState().levelRenderState.cameraRenderState; + var fog=camera.fogData; + float mist=fogDensity(fog.environmentalEnd); + float rain=mc.level.getRainLevel(camera.cameraEntityPartialTicks); + float thunder=mc.level.getThunderLevel(camera.cameraEntityPartialTicks); + var sun=PixelShadows.sunDirection(mc.gameRenderer.gameRenderState().levelRenderState.skyRenderState.sunAngle); + float daylight=Math.clamp((sun.y-.08f)/.16f,0,1)*(1-.75f*rain)*(1-.65f*thunder); + float density=1+2.5f*mist+.8f*rain; + float spread=Math.max(mist,.35f*rain); + float extinction=1+.7f*mist+.25f*rain; settings.rotate();try(var mapped=settings.currentBuffer().map(false,true)){ Std140Builder.intoBuffer(mapped.data()).putVec4(.85f+.15f*fog.color.x,.78f+.15f*fog.color.y,.66f+.15f*fog.color.z,0) - .putVec4(VanillaLight.sunshaftIntensity()/100f,Math.min(64,VanillaLight.shadowDistance()*.5f),0,0); + .putVec4(VanillaLight.sunshaftIntensity()/100f,Math.min(64,VanillaLight.shadowDistance()*.5f),0,0) + .putVec4(density,spread,extinction,daylight); } var encoder=RenderSystem.getDevice().createCommandEncoder();var nearest=RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST); try(var pass=encoder.createRenderPass(()->"Sanctuary solar volume",light.getColorTextureView(),Optional.empty())){ @@ -62,6 +72,12 @@ public final class Sunshafts { } applied=true;frames++; } + /** Environmental fog has its own end distance; changing render distance is not weather. */ + static float fogDensity(float environmentalEnd){ + if(!Float.isFinite(environmentalEnd))return 0; + float t=Math.clamp((192-environmentalEnd)/112,0,1); + return t*t*(3-2*t); + } public static void release(){if(light!=null){light.destroyBuffers();light=null;}if(settings!=null){settings.close();settings=null;}applied=false;} static boolean applied(){return applied;} static boolean allocated(){return light!=null;} diff --git a/mods/sanctuary/src/main/resources/assets/sanctuary/lang/en_us.json b/mods/sanctuary/src/main/resources/assets/sanctuary/lang/en_us.json index 367b55d..6ad000e 100644 --- a/mods/sanctuary/src/main/resources/assets/sanctuary/lang/en_us.json +++ b/mods/sanctuary/src/main/resources/assets/sanctuary/lang/en_us.json @@ -2307,6 +2307,6 @@ "sanctuary.shaders.sunshafts": "Sunshafts", "sanctuary.shaders.sunshafts_help": "Light beams through openings in the scenery. Independent of bloom; range follows shadows (up to 64 blocks).", "sanctuary.shaders.sunshaft_intensity": "Sunshaft intensity", - "sanctuary.shaders.sunshaft_intensity_help": "Strength of light scattered in the air. Zero disables the effect.", + "sanctuary.shaders.sunshaft_intensity_help": "Strength of light scattered in the air. Beams automatically thicken with fog and precipitation. Zero disables the effect.", "sanctuary.shaders.sunshaft_intensity_value": "Sunshafts: %s %%" } diff --git a/mods/sanctuary/src/main/resources/assets/sanctuary/lang/fr_fr.json b/mods/sanctuary/src/main/resources/assets/sanctuary/lang/fr_fr.json index bae2ac3..1433666 100644 --- a/mods/sanctuary/src/main/resources/assets/sanctuary/lang/fr_fr.json +++ b/mods/sanctuary/src/main/resources/assets/sanctuary/lang/fr_fr.json @@ -2307,6 +2307,6 @@ "sanctuary.shaders.sunshafts": "Rayons solaires", "sanctuary.shaders.sunshafts_help": "Faisceaux de lumière dans les ouvertures du décor. Indépendants du bloom, portée liée aux ombres (64 blocs maximum).", "sanctuary.shaders.sunshaft_intensity": "Intensité des rayons", - "sanctuary.shaders.sunshaft_intensity_help": "Force de la lumière dans l’air. Zéro désactive le calcul.", + "sanctuary.shaders.sunshaft_intensity_help": "Force de la lumière dans l’air. Les rayons s’épaississent automatiquement avec le brouillard et les précipitations. Zéro désactive le calcul.", "sanctuary.shaders.sunshaft_intensity_value": "Rayons : %s %%" } diff --git a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/sunshaft_gather.fsh b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/sunshaft_gather.fsh index 4fdb452..9bcf506 100644 --- a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/sunshaft_gather.fsh +++ b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/sunshaft_gather.fsh @@ -33,12 +33,15 @@ void main(){ float span=max(0.0,stop-start);if(span<=0.0)break; float t=(start+stop)*.5; float distanceFade=1.0-smoothstep(range*.7,range,t); - total+=sunlight(ray*t)*span*exp(-t/24.0)*distanceFade; + total+=sunlight(ray*t)*span*exp(-t/24.0*ShaftAtmosphere.z)*distanceFade; } - float phase=.12+.88*pow(max(0.0,dot(ray,SunStrength.xyz)),4.0); - float weather=clamp(SunStrength.w/.38,0.0,1.0); + // Thick air scatters into wider viewing angles, while extinguishing distant light. + // Keep the shadow footprint unchanged: weather cannot open a hole in a solid wall. + float base=mix(.12,.30,ShaftAtmosphere.y); + float phase=base+(1.0-base)*pow(max(0.0,dot(ray,SunStrength.xyz)),mix(4.0,2.0,ShaftAtmosphere.y)); + float weather=ShaftAtmosphere.w; float fogDistance=max(abs(ray.x),max(abs(ray.y),abs(ray.z)))*end; float nativeFog=1.0-smoothstep(DistanceClipFog.z,max(DistanceClipFog.z+.01,DistanceClipFog.w),fogDistance); - float amount=total*.018*phase*weather*ShaftSettings.x*nativeFog; + float amount=total*.018*phase*weather*ShaftSettings.x*nativeFog*ShaftAtmosphere.x; fragColor=vec4(ShaftColor.rgb*amount,end); } diff --git a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/include/sunshafts.glsl b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/include/sunshafts.glsl index b37d4d0..1722c6b 100644 --- a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/include/sunshafts.glsl +++ b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/include/sunshafts.glsl @@ -6,7 +6,10 @@ layout(std140) uniform PixelShadows { vec4 DistanceClipFog; vec4 EnvironmentFog; }; -layout(std140) uniform ShaftParams { vec4 ShaftColor; vec4 ShaftSettings; }; +layout(std140) uniform ShaftParams { + vec4 ShaftColor; vec4 ShaftSettings; + vec4 ShaftAtmosphere; // density, angular diffusion, extinction, available daylight +}; uniform sampler2D WorldDepth; vec2 shaftTexel(vec2 uv){ vec2 size=vec2(textureSize(WorldDepth,0)); diff --git a/packwiz/pack.toml b/packwiz/pack.toml index 4a47d50..8a1f763 100644 --- a/packwiz/pack.toml +++ b/packwiz/pack.toml @@ -1,6 +1,6 @@ name = "Sanctuary" author = "KOKA99CAB" -version = "beta.137" +version = "beta.138" pack-format = "packwiz:1.1.0" [index]