diff --git a/README.md b/README.md
index 5264f49..14c767f 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,12 @@ 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.134** ajoute le **PBR · relief des matériaux** optionnel :
-normales procédurales mises en cache, rugosité par matériau et reflets orientés.
-Le SSGI reçoit un filtre spatial guidé par la profondeur et des normales de faces
-stabilisées. **Options → Shaders** ; réglages existants conservés.
-Le [ticket de livraison](docs/shader-beta134.md) décrit le rendu et ses limites.
-Release publiée et même instance **Sanctuary Beta** synchronisée, sauvegardes
-et réglages conservés.
+La version **beta.135** corrige le SSGI en contre-plongée et près des surfaces :
+normales indépendantes de la taille des pixels et atténuation progressive au
+contact. Bloom initial à **20 %**, PBR et SSGI **activés par défaut**.
+**Options → Shaders** ; réglages existants conservés.
+Le [ticket de livraison](docs/shader-beta135.md) décrit le rendu et ses limites.
+Contrôles ciblés et archives vérifiés ; livraison prête.
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.
@@ -37,6 +36,13 @@ La [carte native beta.005](docs/atlas-beta005.md) prend le relais après
l’étude de Xaero et des alternatives libres.
+## beta.135 — SSGI près des surfaces
+
+Correction des coupures à courte distance, sans flouter les textures.
+[Contrat et vérifications](docs/shader-beta135.md) ·
+[Pack normal](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.135/Sanctuary-beta.135.mrpack) ·
+[Pack de test](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.135/Sanctuary-Test-beta.135.mrpack).
+
## beta.134 — PBR optionnel et SSGI filtré
Relief et reflets des matériaux ; lissage de la lumière indirecte.
diff --git a/docs/shader-beta135.md b/docs/shader-beta135.md
new file mode 100644
index 0000000..c3df53c
--- /dev/null
+++ b/docs/shader-beta135.md
@@ -0,0 +1,101 @@
+# SHADER-135 — SSGI en contre-plongée et près des surfaces
+
+Demande : supprimer les déchirures horizontales sous une surface, notamment
+près de la lave, et atténuer proprement le rebond quand la caméra s'approche
+d'une source ou d'un mur. Branche `codex/shader-closeup-beta135`, socle beta.134.
+Minecraft 26.3 / Java 25 ; aucune modification de monde ou de gameplay.
+
+## Diagnostic et correction
+
+Le calcul des normales comparait l'aire du pixel en unités du monde à un seuil
+fixe. À courte distance ou à haute résolution, cette aire devient naturellement
+très petite : le rejet pouvait couper une surface continue. Les deux directions
+sont maintenant normalisées avant leur produit vectoriel. Les texels de bord
+sont bornés et les voisins sans profondeur ne créent plus de fausse surface.
+
+La composition utilise le centre exact du texel de profondeur. Le rebond
+s'atténue entre 0,4 et 0,06 bloc de distance au **plan** receveur, et lorsque la
+vue devient presque tangente. Cette distance évite une découpe circulaire sur
+un mur plat. L'image native reste nette ; aucune texture du décor n'est floutée.
+
+Les sondes sortant du volume de projection sont rejetées. Leur contribution
+s'atténue près du plan de caméra et de la limite d'épaisseur d'un impact, au lieu
+d'une contribution pleine jusqu'à cette limite. Trois ressources GLSL modifiées ;
+aucune passe, cible GPU, lecture CPU, histoire temporelle ou option ajoutée.
+Les 12 rayons × 10 pas et les deux passes de filtrage spatial restent inchangés.
+
+## Réglages initiaux
+
+Demande ajoutée pendant la livraison : **bloom 20 %**, **PBR ON** et **SSGI ON**.
+Les intensités PBR 50 et SSGI 35 restent inchangées. Les champs absents du fichier
+de préférences prennent ces nouvelles valeurs ; les choix explicites du joueur
+restent conservés. Aides françaises et anglaises mises à jour. Vérification
+native supplémentaire des préférences vierges, partielles et personnalisées.
+
+## Reproduction native
+
+Deux scènes isolées : stone au-dessus de la lave, puis coin à 0,5 bloc d'une
+paroi rouge. Caméra en contre-plongée, sept distances au plafond de 2 à 0,04 bloc,
+comparaisons OFF/35/100 avant/après. Dans la première scène, à 0,1 bloc et
+intensité 100, le saut maximal de gain rouge moyen entre lignes de pixels neutres
+passe de **14,88 à 0,052** niveaux sur 255. Dans le coin, à 0,4 bloc et intensité
+35, le rebond auparavant rejeté revient (gain moyen de **6,01** niveaux), puis
+s'atténue à l'approche du contact. Ces chiffres concernent ces scènes uniquement.
+
+Les deux essais ciblés corrigés réussissent en 1 min 18 s et 1 min 17 s sur
+Apple M1 / OpenGL. Images avant/après conservées dans `build/closeup-*` ;
+comparateur autonome `build/Shader-beta.135-comparaison.html`.
+
+## Régression et livraison
+
+Les contrôles natifs de la suite générale réussissent jusqu'au parcours SSGI :
+ombres, lune, feuillage, temps réel, CTM, émissifs/bloom, couleurs de rebond,
+occlusion, réglages, rechargement et déplacement latéral de 12 blocs à 35/100.
+Log : `build/shader135-client-main.log`. La suite n'est pas présentée comme
+entièrement réussie : elle s'est arrêtée sur une attente incorrecte du nouveau
+test, qui demandait du rebond alors que sa source était sortie du champ.
+
+Le scénario corrigé distingue la haute résolution avec source visible de la
+vue très relevée sans source visible. Le bassin possède un fond pour isoler la
+lave des essais suivants ; les particules du scénario précédent sont effacées
+avant la comparaison PBR statique. Une première tentative avait aussi rencontré
+le refus natif de remettre l'horloge à sa valeur actuelle ; la préparation de
+l'heure est désormais répétable. Ces changements concernent uniquement les tests.
+
+Le contrôle final ciblé **réussit en 2 min 52 s** : les deux scènes, 35/100,
+sept distances, 1280 × 720 avec source visible, source hors champ, mouvement
+minuscule, rendu combiné, puis contrôles PBR complets (cache, intensités,
+matériaux, soleil, toit, OFF/zéro, redimensionnement et rechargement).
+Log : `build/shader135-client-final.log` ; marqueurs `SSGI135_CLOSEUP_PASS` et
+`PBR134_PASS`. Les seuils de proximité et PBR sont conservés.
+Le contrôle supplémentaire des nouveaux défauts et du PBR **réussit en
+1 min 16 s** : préférences vierges, champs absents, valeurs personnelles conservées,
+puis rendu PBR et combinaison des effets. Log : `build/shader135-defaults-client.log`.
+`./gradlew check build assemblePack assembleTestPack -x :sanctuary:runGameTest`
+réussit en **2 min 18 s**, 126 tâches. Le serveur GameTest dédié demeure exclu
+conformément au refus EULA antérieur. Les classes des scénarios clients compilent
+aussi après leur isolation explicite des effets activés par défaut.
+Log : `build/shader135-build.log` ; compilation : `build/shader135-test-compile.log`.
+
+## Archives vérifiées
+
+Comparaison à beta.134 : **6 entrées de production** modifiées, limitées aux
+trois ressources SSGI, aux valeurs initiales et aux aides FR/EN. Gameplay et
+textures des gemmes inchangés. Packs normal/Test : JAR Sanctuary identique ;
+archives de sources et template conformes aux sources. Reçu local ignoré :
+`build/shader135-artifact.json`.
+
+
+## Limites
+
+Le SSGI utilise seulement les surfaces visibles : les sources hors champ restent
+absentes et un grand changement de vue peut modifier le rebond. L'atténuation
+proche est volontaire ; ce n'est pas une simulation volumétrique. Le brouillard
+natif de submersion continue de suspendre le SSGI, comme en beta.134. Aucun
+résultat Windows/Vulkan ni mesure de FPS revendiqués.
+
+- `Sanctuary-beta.135.mrpack` : 10422391 octets ; SHA-256 `36ac278f9f5ecd2cefd69da16ea7d153ff44c5fc8e6bf0b3a3d6b71f5d767af6`.
+
+- `Sanctuary-Test-beta.135.mrpack` : 10441314 octets ; SHA-256 `e096374f7ea0856b20392e5d277e94f6c3d411723d7ffc10ceccdd9f0bf5c4d4`.
+
+JAR : `a68ccd04a92a962c46a76b26a80cb44a4faf7a13a562094dde51010ade594c10`.
diff --git a/gradle.properties b/gradle.properties
index 52d65ca..65d94de 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.134
-pack_version=beta.134
+mod_version=beta.135
+pack_version=beta.135
resource_pack_version=beta.121
maven_group=fr.koka.sanctuary
jei_version=30.32.0-sanctuary.3
diff --git a/mods/sanctuary/build.gradle b/mods/sanctuary/build.gradle
index 13d7973..652a97e 100644
--- a/mods/sanctuary/build.gradle
+++ b/mods/sanctuary/build.gradle
@@ -99,6 +99,9 @@ tasks.named('processGametestResources') {
def ssgi132Client = providers.gradleProperty('sanctuarySsgi132ClientTests').map { it.toBoolean() }.getOrElse(false)
inputs.property('sanctuarySsgi132ClientTests', ssgi132Client)
if (ssgi132Client) filter { line -> line.replace('fr.koka.sanctuary.gametest.SanctuaryClientRenderTests', 'fr.koka.sanctuary.client.shader.Ssgi132ClientChecks') }
+ def closeup135Client = providers.gradleProperty('sanctuarySsgiCloseup135ClientTests').map { it.toBoolean() }.getOrElse(false)
+ inputs.property('sanctuarySsgiCloseup135ClientTests', closeup135Client)
+ if (closeup135Client) filter { line -> line.replace('fr.koka.sanctuary.gametest.SanctuaryClientRenderTests', 'fr.koka.sanctuary.client.shader.SsgiCloseup135ClientChecks') }
def bloom130Client = providers.gradleProperty('sanctuaryBloom130ClientTests').map { it.toBoolean() }.getOrElse(false)
inputs.property('sanctuaryBloom130ClientTests', bloom130Client)
diff --git a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Bloom130ClientChecks.java b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Bloom130ClientChecks.java
index 272deff..a81ff1c 100644
--- a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Bloom130ClientChecks.java
+++ b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Bloom130ClientChecks.java
@@ -23,7 +23,7 @@ public final class Bloom130ClientChecks implements FabricClientGameTest {
String ns=name.equals("ruby")||name.equals("sapphire")?"sanctuary:":"minecraft:";result.add(ns+name+"_ore");result.add(ns+"deepslate_"+name+"_ore");}
result.add("minecraft:nether_quartz_ore");result.add("minecraft:nether_gold_ore");return List.copyOf(result);}
static void run(ClientGameTestContext c,TestServerContext server){
- c.runOnClient(m->{preferences();analyseTextures();VanillaLight.setSaturation(100);VanillaLight.setPixelShadows(false);VanillaLight.setEdgeHighlights(false);VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);});
+ c.runOnClient(m->{preferences();VanillaLight.setPbr(false);VanillaLight.setSsgi(false);analyseTextures();VanillaLight.setSaturation(100);VanillaLight.setPixelShadows(false);VanillaLight.setEdgeHighlights(false);VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);});
server.runOnServer(s->{command(s,"time set 18000");
for(int x=4;x<=20;x++)for(int z=4;z<=20;z++){
s.overworld().setBlockAndUpdate(new BlockPos(x,64,z),Blocks.CONCRETE.black().defaultBlockState());
@@ -102,7 +102,7 @@ public final class Bloom130ClientChecks implements FabricClientGameTest {
private static void preferences(){try{
var path=FabricLoader.getInstance().getConfigDir().resolve("sanctuary-shaders.json");Files.deleteIfExists(path);forgetPreferences();
check(VanillaLight.pixelShadows()&&VanillaLight.shadowDistance()==128&&VanillaLight.shadowPixels()==16&&VanillaLight.edgeDistance()==32,"Fresh requested defaults: shadows ON, 128; highlights 32; shared precision 16");
- check(VanillaLight.bloom()&&VanillaLight.oreEmission()&&VanillaLight.edgeHighlights()&&VanillaLight.bloomIntensity()==35&&VanillaLight.bloomRadius()==50,"Bloom, ore emission and edge highlights default to ON");
+ check(VanillaLight.bloom()&&VanillaLight.oreEmission()&&VanillaLight.edgeHighlights()&&VanillaLight.bloomIntensity()==20&&VanillaLight.bloomRadius()==50,"Bloom, ore emission and edge highlights default to ON");
Files.writeString(path,"{\"bloom\":false,\"oreEmission\":false,\"edgeHighlights\":false,\"pixelShadows\":false,\"shadowDistance\":64,\"shadowPixels\":8,\"edgeDistance\":16}");forgetPreferences();
check(!VanillaLight.bloom()&&!VanillaLight.oreEmission()&&!VanillaLight.edgeHighlights()&&!VanillaLight.pixelShadows()&&VanillaLight.shadowDistance()==64&&VanillaLight.shadowPixels()==8&&VanillaLight.edgeDistance()==16,"Existing user choices are retained");
for(int n:new int[]{-10,0,35,100,400}){VanillaLight.setBloomIntensity(n);VanillaLight.setBloomRadius(n);VanillaLight.setOreIntensity(n);forgetPreferences();check(VanillaLight.bloomIntensity()==Math.clamp(n,0,100)&&VanillaLight.bloomRadius()==Math.clamp(n,0,100)&&VanillaLight.oreIntensity()==Math.clamp(n,0,100),"Persisted bloom controls are bounded");}
diff --git a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/EdgeHighlights129ClientChecks.java b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/EdgeHighlights129ClientChecks.java
index 2734323..a7aaa8a 100644
--- a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/EdgeHighlights129ClientChecks.java
+++ b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/EdgeHighlights129ClientChecks.java
@@ -160,6 +160,7 @@ final class EdgeHighlights129ClientChecks {
check(VanillaLight.edgeHighlights(),"Toggle persists independently");
VanillaLight.setEdgeHighlights(false);
VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);
+ VanillaLight.setPbr(false);VanillaLight.setSsgi(false);
} catch(java.io.IOException e) { throw new AssertionError(e); }
}
diff --git a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Pbr134ClientChecks.java b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Pbr134ClientChecks.java
index 18f45b5..d46a66d 100644
--- a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Pbr134ClientChecks.java
+++ b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Pbr134ClientChecks.java
@@ -19,6 +19,8 @@ public final class Pbr134ClientChecks implements FabricClientGameTest {
for(int y=65;y<=102;y++)s.overworld().setBlockAndUpdate(new BlockPos(x,y,z),Blocks.AIR.defaultBlockState());
}});
camera(c,server,10.5,69,5.5,65);look(c,server,0,40);settle(c);
+ // The preceding lava/mob fixture may leave animated particles after its blocks are removed.
+ c.runOnClient(m->m.particleEngine.clearParticles());c.waitTicks(2);
var off=capture(c,"pbr-off");enable(c,true);var on=capture(c,"pbr-50");
var delta=difference(off,on);System.out.println("PBR134_DIFF "+delta);check(delta.changed>500&&delta.brighter>100&&delta.darker>100,"Texture relief has lit and shaded pixels: "+delta);
int analyses=c.computeOnClient(m->ProceduralPbr.analyses());c.waitTicks(20);
@@ -59,7 +61,7 @@ public final class Pbr134ClientChecks implements FabricClientGameTest {
}
private static void preferences(){try{
var p=FabricLoader.getInstance().getConfigDir().resolve("sanctuary-shaders.json");Files.writeString(p,"{\"ssgi\":true,\"ssgiIntensity\":35,\"bloom\":false}");forgetPreferences();
- check(!VanillaLight.pbr()&&VanillaLight.pbrIntensity()==50&&VanillaLight.ssgi()&&!VanillaLight.bloom(),"PBR optional default preserves old choices");
+ check(VanillaLight.pbr()&&VanillaLight.pbrIntensity()==50&&VanillaLight.ssgi()&&!VanillaLight.bloom(),"Missing PBR setting defaults ON while preserving explicit old choices");
for(int n:new int[]{-1,0,50,100,101}){VanillaLight.setPbr(true);VanillaLight.setPbrIntensity(n);forgetPreferences();check(VanillaLight.pbr()&&VanillaLight.pbrIntensity()==Math.clamp(n,0,100),"PBR preference persistence/clamp");}
VanillaLight.setPbrIntensity(50);
}catch(Exception e){throw new AssertionError(e);}}
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 2bd036f..91689ef 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
@@ -36,11 +36,12 @@ import net.minecraft.world.phys.Vec3;
/** Real terrain/depth-map checks on a new flat seed 122 world; GPU readback is test-only. */
public final class PixelShadows122ClientChecks implements FabricClientGameTest {
- private final boolean bloomOnly,ssgiOnly,pbrOnly;
+ private final boolean bloomOnly,ssgiOnly,pbrOnly,closeupOnly;
public PixelShadows122ClientChecks(){this(false);}
PixelShadows122ClientChecks(boolean bloomOnly){this(bloomOnly,false);}
PixelShadows122ClientChecks(boolean bloomOnly,boolean ssgiOnly){this(bloomOnly,ssgiOnly,false);}
- PixelShadows122ClientChecks(boolean bloomOnly,boolean ssgiOnly,boolean pbrOnly){this.bloomOnly=bloomOnly;this.ssgiOnly=ssgiOnly;this.pbrOnly=pbrOnly;}
+ PixelShadows122ClientChecks(boolean bloomOnly,boolean ssgiOnly,boolean pbrOnly){this(bloomOnly,ssgiOnly,pbrOnly,false);}
+ PixelShadows122ClientChecks(boolean bloomOnly,boolean ssgiOnly,boolean pbrOnly,boolean closeupOnly){this.bloomOnly=bloomOnly;this.ssgiOnly=ssgiOnly;this.pbrOnly=pbrOnly;this.closeupOnly=closeupOnly;}
private static final int FLOOR = 64;
private static final AABB PILLAR = new AABB(0, FLOOR + 1, 0, 2, FLOOR + 9, 2);
private static final BlockPos SLAB = new BlockPos(5, FLOOR + 3, -5);
@@ -135,6 +136,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
c.runOnClient(m -> check(VanillaLight.active(), "Native Sanctuary shader is active without an external engine"));
float morning = c.computeOnClient(m -> m.gameRenderer.gameRenderState().levelRenderState.skyRenderState.sunAngle);
+ if(closeupOnly){SsgiCloseup135ClientChecks.run(c,server);Pbr134ClientChecks.run(c,server);return;}
if(pbrOnly){Pbr134ClientChecks.run(c,server);return;}
if(ssgiOnly){Ssgi132ClientChecks.run(c,server);return;}
if(bloomOnly){Bloom130ClientChecks.run(c,server);return;}
@@ -264,7 +266,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
});
});
c.waitTicks(3);
- c.takeScreenshot("sanctuary-beta134-shader-options-" + locale);
+ c.takeScreenshot("sanctuary-beta135-shader-options-" + locale);
}
c.runOnClient(m -> m.gui.setScreen(null));
}
@@ -428,7 +430,23 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
? java.util.stream.Stream.concat(java.util.stream.Stream.of(widget), widgets(child)) : java.util.stream.Stream.of(widget));
}
+ private static void checkDefaults135() {
+ try {
+ var path=FabricLoader.getInstance().getConfigDir().resolve("sanctuary-shaders.json");
+ Files.deleteIfExists(path);forgetPreferences();
+ check(VanillaLight.bloom()&&VanillaLight.bloomIntensity()==20&&VanillaLight.pbr()&&VanillaLight.ssgi(),"Fresh defaults: bloom20, PBR and SSGI ON");
+ check(VanillaLight.pbrIntensity()==50&&VanillaLight.ssgiIntensity()==35,"Existing effect strengths retained");
+ Files.writeString(path,"{\"pbr\":false,\"ssgi\":false,\"bloomIntensity\":73}");forgetPreferences();
+ check(!VanillaLight.pbr()&&!VanillaLight.ssgi()&&VanillaLight.bloomIntensity()==73,"Explicit personal toggles and intensity survive changed defaults");
+ Files.writeString(path,"{\"bloom\":false}");forgetPreferences();
+ check(!VanillaLight.bloom()&&VanillaLight.bloomIntensity()==20&&VanillaLight.pbr()&&VanillaLight.ssgi(),"Missing fields get new defaults without replacing explicit values");
+ System.out.println("SHADER135_DEFAULTS_PASS fresh, partial and personal preferences");
+ } catch(IOException e){throw new AssertionError(e);}
+ }
+
private static void checkPreferences() {
+ checkDefaults135();
+ VanillaLight.setPbr(false);
VanillaLight.setSsgi(false);
VanillaLight.setBloom(false);
VanillaLight.setOreEmission(false);
@@ -531,7 +549,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
}
static Frame capture(ClientGameTestContext c, String name) {
- if (name != null) c.takeScreenshot("sanctuary-beta134-shader-" + name);
+ if (name != null) c.takeScreenshot("sanctuary-beta135-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/Ssgi132ClientChecks.java b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Ssgi132ClientChecks.java
index c930094..bddd748 100644
--- a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Ssgi132ClientChecks.java
+++ b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/Ssgi132ClientChecks.java
@@ -13,7 +13,7 @@ import java.nio.file.Files;
public final class Ssgi132ClientChecks implements FabricClientGameTest {
public void runTest(ClientGameTestContext c){new PixelShadows122ClientChecks(false,true).runTest(c);}
static void run(ClientGameTestContext c,TestServerContext server){
- c.runOnClient(m->{preferences();VanillaLight.setEnabled(true);VanillaLight.setSaturation(100);VanillaLight.setSsgi(false);VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);VanillaLight.setPixelShadows(false);VanillaLight.setEdgeHighlights(false);});
+ c.runOnClient(m->{preferences();VanillaLight.setPbr(false);VanillaLight.setEnabled(true);VanillaLight.setSaturation(100);VanillaLight.setSsgi(false);VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);VanillaLight.setPixelShadows(false);VanillaLight.setEdgeHighlights(false);});
server.runOnServer(s->{command(s,"time set 6000");for(int x=-5;x<=24;x++)for(int z=-5;z<=24;z++){
s.overworld().setBlockAndUpdate(new BlockPos(x,64,z),Blocks.CONCRETE.white().defaultBlockState());
for(int y=65;y<=102;y++)s.overworld().setBlockAndUpdate(new BlockPos(x,y,z),Blocks.AIR.defaultBlockState());
@@ -63,6 +63,7 @@ public final class Ssgi132ClientChecks implements FabricClientGameTest {
c.runOnClient(m->check(!ScreenSpaceGI.allocated(),"Main shader OFF releases SSGI too"));
c.runOnClient(m->VanillaLight.setEnabled(true));frames(c);enable(c,false);
motion(c,server);
+ SsgiCloseup135ClientChecks.run(c,server);
System.out.println("SSGI133_PASS red/blue diffuse bounce; intensity; flat rejection; foreground occlusion; static stability; tiny camera motion; resize; reload; combined shader; OFF/zero; preferences; red="+red+" blue="+blue);
}
private static void motion(ClientGameTestContext c,TestServerContext server){
@@ -109,7 +110,7 @@ public final class Ssgi132ClientChecks implements FabricClientGameTest {
private static void frames(ClientGameTestContext c){int start=c.computeOnClient(m->ScreenSpaceGI.renderedFrames());c.waitFor(m->ScreenSpaceGI.applied()&&ScreenSpaceGI.renderedFrames()>start+10,1200);c.waitTicks(6);}
private static void preferences(){try{
var p=FabricLoader.getInstance().getConfigDir().resolve("sanctuary-shaders.json");Files.writeString(p,"{\"bloom\":false,\"shadowPixels\":128}");forgetPreferences();
- check(!VanillaLight.ssgi()&&VanillaLight.ssgiIntensity()==35&&!VanillaLight.bloom()&&VanillaLight.shadowPixels()==128,"Legacy choices preserved; experimental SSGI defaults OFF at 35");
+ check(VanillaLight.ssgi()&&VanillaLight.ssgiIntensity()==35&&!VanillaLight.bloom()&&VanillaLight.shadowPixels()==128,"Legacy choices preserved; missing SSGI setting defaults ON at 35");
for(int i:new int[]{-10,0,35,90,140}){VanillaLight.setSsgiIntensity(i);VanillaLight.setSsgi(true);forgetPreferences();check(VanillaLight.ssgi()&&VanillaLight.ssgiIntensity()==Math.clamp(i,0,100),"SSGI persists and clamps");}
VanillaLight.setSsgiIntensity(90);
}catch(Exception e){throw new AssertionError(e);}}
diff --git a/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/SsgiCloseup135ClientChecks.java b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/SsgiCloseup135ClientChecks.java
new file mode 100644
index 0000000..10216c8
--- /dev/null
+++ b/mods/sanctuary/src/clientGametest/java/fr/koka/sanctuary/client/shader/SsgiCloseup135ClientChecks.java
@@ -0,0 +1,78 @@
+package fr.koka.sanctuary.client.shader;
+
+import static fr.koka.sanctuary.client.shader.PixelShadows122ClientChecks.*;
+import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext;
+import net.fabricmc.fabric.api.client.gametest.v1.context.TestServerContext;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.block.Blocks;
+
+/** Native low-angle and near-plane regression fixture; never opens a personal world. */
+public final class SsgiCloseup135ClientChecks implements net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest {
+ public void runTest(ClientGameTestContext c){new PixelShadows122ClientChecks(false,false,false,true).runTest(c);}
+ static void run(ClientGameTestContext c,TestServerContext server){
+ c.runOnClient(m->{VanillaLight.setEnabled(true);VanillaLight.setSaturation(100);VanillaLight.setSsgi(false);VanillaLight.setPbr(false);VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);VanillaLight.setPixelShadows(false);VanillaLight.setEdgeHighlights(false);});
+ server.runOnServer(s->{
+ // Minecraft 26.3 rejects setting a clock to its existing value.
+ command(s,"time add 1");command(s,"time set 6000");
+ for(int x=-5;x<=24;x++)for(int z=-5;z<=24;z++){
+ // Contain lava: no flow below the platform into the following PBR fixture.
+ s.overworld().setBlockAndUpdate(new BlockPos(x,63,z),Blocks.STONE.defaultBlockState());
+ for(int y=64;y<=102;y++)s.overworld().setBlockAndUpdate(new BlockPos(x,y,z),y==64?Blocks.STONE.defaultBlockState():Blocks.AIR.defaultBlockState());
+ if(x>=5&&x<=16&&z>=4&&z<=14)s.overworld().setBlockAndUpdate(new BlockPos(x,64,z),Blocks.LAVA.defaultBlockState());
+ if(x>=5&&x<=16&&z>=8&&z<=14)s.overworld().setBlockAndUpdate(new BlockPos(x,68,z),Blocks.STONE.defaultBlockState());
+ if(x>=5&&x<=16&&z==12)for(int y=65;y<=67;y++)s.overworld().setBlockAndUpdate(new BlockPos(x,y,z),Blocks.CONCRETE.red().defaultBlockState());
+ }
+ });c.waitTicks(40);c.waitFor(m->m.levelRenderer.hasRenderedAllSections(),1200);
+ for(int scene=0;scene<2;scene++)for(int intensity:new int[]{35,100}){
+ c.runOnClient(m->VanillaLight.setSsgiIntensity(intensity));
+ for(int step=0;step<7;step++){
+ // Eye approaches the underside, crossing the old fixed normal-area threshold.
+ double gap=new double[]{2.0,1.0,.65,.4,.2,.1,.04}[step];
+ camera(c,server,10.5,68-gap-1.62,scene==0?9.5:11.5,65);look(c,server,0,-25);
+ c.runOnClient(m->VanillaLight.setSsgi(false));c.waitTicks(8);
+ var off=capture(c,"closeup-"+scene+"-"+intensity+"-"+step+"-off");
+ c.runOnClient(m->VanillaLight.setSsgi(true));c.waitFor(m->ScreenSpaceGI.applied(),1200);c.waitTicks(12);
+ var on=capture(c,"closeup-"+scene+"-"+intensity+"-"+step+"-on");
+ var stats=rowJump(off,on);
+ System.out.println("SSGI135_CLOSEUP scene="+scene+" intensity="+intensity+" gap="+gap+" "+stats);
+ if(scene==0&&gap<=.1)check(stats.jump<2&&stats.gain<.1,"Near-plane bounce fades without a horizontal bright strip: "+stats);
+ if(scene==1&&gap==.4)check(stats.rows>30&&stats.gain>1,"Close corner retains actual bounce instead of rejecting small world pixels: "+stats);
+ if(gap==.04)check(stats.gain<.05,"At contact, indirect light fades to the source image: "+stats);
+ }
+ }
+ int width=c.computeOnClient(m->m.getWindow().getScreenWidth()),height=c.computeOnClient(m->m.getWindow().getScreenHeight());
+ camera(c,server,10.5,65.98,11.5,65);look(c,server,0,-25);
+ c.runOnClient(m->{VanillaLight.setSsgiIntensity(100);m.getWindow().setWindowed(1280,720);});
+ c.waitFor(m->m.gameRenderer.mainRenderTarget().width!=width,200);c.waitTicks(15);
+ c.runOnClient(m->VanillaLight.setSsgi(false));c.waitTicks(8);var largeOff=capture(c,"closeup-large-off");
+ c.runOnClient(m->VanillaLight.setSsgi(true));c.waitTicks(15);var large=capture(c,"closeup-large-on");
+ var largeStats=rowJump(largeOff,large);check(largeStats.rows>30&&largeStats.gain>1,"High resolution with a visible source retains bounce: "+largeStats);
+ look(c,server,.00002f,-25.00002f);c.waitTicks(10);unchanged(large,capture(c,null),"Closeup has no subpixel normal threshold flicker");
+ look(c,server,0,-65);c.runOnClient(m->VanillaLight.setSsgi(false));c.waitTicks(8);var steepOff=capture(c,"closeup-offscreen-source-off");
+ c.runOnClient(m->VanillaLight.setSsgi(true));c.waitTicks(15);unchanged(steepOff,capture(c,"closeup-offscreen-source-on"),"Looking away from the source does not invent hidden light");
+ c.runOnClient(m->m.getWindow().setWindowed(width,height));c.waitFor(m->m.gameRenderer.mainRenderTarget().width==width,200);c.waitTicks(15);
+ c.runOnClient(m->{VanillaLight.setPbr(true);VanillaLight.setBloom(true);VanillaLight.setOreEmission(true);VanillaLight.setPixelShadows(true);VanillaLight.setEdgeHighlights(true);});
+ c.waitFor(m->ProceduralPbr.applied()&&ProceduralPbr.ready()&&Bloom.applied()&&PixelShadows.applied()&&PixelShadows.ready(),1200);
+ c.waitTicks(15);capture(c,"closeup-combined");
+ c.runOnClient(m->{VanillaLight.setSsgi(false);VanillaLight.setSsgiIntensity(35);VanillaLight.setPbr(false);});
+ System.out.println("SSGI135_CLOSEUP_PASS low-angle lava/stone and seven camera distances at 35/100");
+ }
+ private record Rows(double jump,double gain,int rows){}
+ private static Rows rowJump(Frame a,Frame b){
+ double previous=0,max=0,gain=0;int rows=0;
+ for(int y=a.height()/8;y>16&255,g=pa>>8&255,blue=pa&255;
+ if(Math.max(r,Math.max(g,blue))-Math.min(r,Math.min(g,blue))<12&&g>15){
+ sum+=Math.max(0,(pb>>16&255)-r);count++;
+ }
+ }
+ if(count0)max=Math.max(max,Math.abs(mean-previous));
+ previous=mean;gain+=mean;rows++;
+ }
+ return new Rows(max,gain/Math.max(rows,1),rows);
+ }
+}
diff --git a/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/VanillaLight.java b/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/VanillaLight.java
index ce6ca51..011937b 100644
--- a/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/VanillaLight.java
+++ b/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/shader/VanillaLight.java
@@ -19,11 +19,11 @@ public final class VanillaLight {
int saturation=142;
boolean pixelShadows=true;
boolean bloom=true;
- boolean pbr=false;
+ boolean pbr=true;
int pbrIntensity=50;
- boolean ssgi=false;
+ boolean ssgi=true;
int ssgiIntensity=35;
- int bloomIntensity=35;
+ int bloomIntensity=20;
int bloomRadius=50;
boolean oreEmission=true;
int oreIntensity=50;
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 2d08d73..2e880c1 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
@@ -2279,7 +2279,7 @@
"sanctuary.shaders.bloom": "Bloom",
"sanctuary.shaders.bloom_help": "Glow around the sun and luminous surfaces. ON by default. Turning it off removes halos while keeping emissive ores. Requires Vanilla Light.",
"sanctuary.shaders.bloom_intensity": "Bloom strength",
- "sanctuary.shaders.bloom_intensity_help": "Halo strength. Zero removes halos and their blur passes while keeping emissive ores.",
+ "sanctuary.shaders.bloom_intensity_help": "Halo strength, 20% by default. Zero removes halos and their blur passes while keeping emissive ores.",
"sanctuary.shaders.bloom_intensity_value": "Bloom: %s%%",
"sanctuary.shaders.bloom_radius": "Bloom spread",
"sanctuary.shaders.bloom_radius_help": "Halo size around luminous pixels. Texture details stay sharp.",
@@ -2290,12 +2290,12 @@
"sanctuary.shaders.ore_intensity_help": "Inclusion brightness. Does not change world light levels or mob spawning.",
"sanctuary.shaders.ore_intensity_value": "Ores: %s%%",
"sanctuary.shaders.ssgi": "SSGI · color bounce",
- "sanctuary.shaders.ssgi_help": "Experimental: enhanced color bounce between nearby visible surfaces. Radius 2.5 blocks. Off-screen surfaces do not contribute. OFF by default.",
+ "sanctuary.shaders.ssgi_help": "Experimental: enhanced color bounce between nearby visible surfaces. Radius 2.5 blocks. Off-screen surfaces do not contribute. ON by default.",
"sanctuary.shaders.ssgi_intensity": "SSGI strength",
"sanctuary.shaders.ssgi_intensity_help": "Strength of diffuse color bounce. Zero stops rendering and frees its resources. Does not change world light.",
"sanctuary.shaders.ssgi_intensity_value": "SSGI: %s%%",
"sanctuary.shaders.pbr": "PBR · material relief",
- "sanctuary.shaders.pbr_help": "Experimental: procedural block normals and roughness, sun and moon highlights. Range 32 blocks. OFF by default.",
+ "sanctuary.shaders.pbr_help": "Experimental: procedural block normals and roughness, sun and moon highlights. Range 32 blocks. ON by default.",
"sanctuary.shaders.pbr_intensity": "PBR strength",
"sanctuary.shaders.pbr_intensity_help": "Material relief and highlights. Zero stops rendering and frees the cache. Original textures remain unchanged.",
"sanctuary.shaders.pbr_intensity_value": "PBR: %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 c553e3d..5806654 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
@@ -2279,7 +2279,7 @@
"sanctuary.shaders.bloom": "Bloom",
"sanctuary.shaders.bloom_help": "Halo du soleil et des surfaces lumineuses. Activé par défaut. L’arrêt coupe le halo et conserve les minerais émissifs. Nécessite Vanilla Light.",
"sanctuary.shaders.bloom_intensity": "Intensité du bloom",
- "sanctuary.shaders.bloom_intensity_help": "Force du halo. Zéro supprime le halo et ses passes de diffusion, tout en conservant les minerais émissifs.",
+ "sanctuary.shaders.bloom_intensity_help": "Force du halo, 20 % par défaut. Zéro supprime le halo et ses passes de diffusion, tout en conservant les minerais émissifs.",
"sanctuary.shaders.bloom_intensity_value": "Bloom : %s %%",
"sanctuary.shaders.bloom_radius": "Diffusion du bloom",
"sanctuary.shaders.bloom_radius_help": "Taille du halo autour des pixels lumineux. Les détails des textures restent nets.",
@@ -2290,12 +2290,12 @@
"sanctuary.shaders.ore_intensity_help": "Luminosité des inclusions. Aucun changement de la lumière du monde ou des apparitions de mobs.",
"sanctuary.shaders.ore_intensity_value": "Minerais : %s %%",
"sanctuary.shaders.ssgi": "SSGI · couleurs diffusées",
- "sanctuary.shaders.ssgi_help": "Expérimental : diffusion renforcée des couleurs entre surfaces visibles proches. Rayon 2,5 blocs. Les surfaces hors écran ne contribuent pas. Désactivé par défaut.",
+ "sanctuary.shaders.ssgi_help": "Expérimental : diffusion renforcée des couleurs entre surfaces visibles proches. Rayon 2,5 blocs. Les surfaces hors écran ne contribuent pas. Activé par défaut.",
"sanctuary.shaders.ssgi_intensity": "Intensité du SSGI",
"sanctuary.shaders.ssgi_intensity_help": "Force de la teinte diffusée. Zéro coupe le calcul et libère ses ressources. Aucun changement de lumière du monde.",
"sanctuary.shaders.ssgi_intensity_value": "SSGI : %s %%",
"sanctuary.shaders.pbr": "PBR · relief des matériaux",
- "sanctuary.shaders.pbr_help": "Expérimental : normales procédurales et rugosité des blocs, reflets du soleil et de la lune. Portée 32 blocs. Désactivé par défaut.",
+ "sanctuary.shaders.pbr_help": "Expérimental : normales procédurales et rugosité des blocs, reflets du soleil et de la lune. Portée 32 blocs. Activé par défaut.",
"sanctuary.shaders.pbr_intensity": "Intensité du PBR",
"sanctuary.shaders.pbr_intensity_help": "Relief et reflets des matériaux. Zéro coupe le calcul et libère le cache. Ne modifie pas les textures originales.",
"sanctuary.shaders.pbr_intensity_value": "PBR : %s %%"
diff --git a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/ssgi_apply.fsh b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/ssgi_apply.fsh
index fe31ba8..83a4be2 100644
--- a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/ssgi_apply.fsh
+++ b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/ssgi_apply.fsh
@@ -4,9 +4,10 @@
layout(location=0) in vec2 texCoord;
layout(location=0) out vec4 fragColor;
void main(){
- float depth=texture(WorldDepth,texCoord).r;
+ vec2 surface=giTexel(texCoord);float depth=texture(WorldDepth,surface).r;
if(depth<=0.0)discard;
- vec3 p=giPosition(texCoord,depth),normal=giNormal(texCoord,p);
+ vec3 p=giPosition(surface,depth),normal=giNormal(surface,p);
+ float confidence=giConfidence(p,normal);if(confidence<=0.0)discard;
vec2 size=vec2(textureSize(IndirectLight,0)),coord=texCoord*size-0.5,base=floor(coord),f=fract(coord);
vec3 bounce=vec3(0.0);float total=0.0;
for(int y=0;y<2;y++)for(int x=0;x<2;x++){
@@ -27,7 +28,7 @@ void main(){
// Artistic single-bounce gain: the old gamma-space screen blend hid almost all
// of the transport on bright blocks. Compose in approximate linear light and
// encode once; a soft shoulder keeps strong bounce below channel clipping.
- vec3 irradiance=bounce/total*(GISettings.x*96.0);
+ vec3 irradiance=bounce/total*(GISettings.x*96.0*confidence);
vec3 reflected=linearScene*irradiance;
vec3 lit=linearScene+(1.0-linearScene)*(1.0-exp(-reflected));
fragColor=vec4(sqrt(clamp(lit,0.0,1.0)),0.0);
diff --git a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/ssgi_gather.fsh b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/ssgi_gather.fsh
index d4d2bc7..a6287b5 100644
--- a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/ssgi_gather.fsh
+++ b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/core/ssgi_gather.fsh
@@ -23,7 +23,10 @@ void main(){
float travel=GISettings.z*float(step)/10.0;
vec3 probe=start+direction*travel;
vec4 clip=ViewProjection*vec4(probe,1.0);
- if(clip.w<=0.0)break;
+ if(clip.w<=0.05)break;
+ float probeDepth=clip.z/clip.w;
+ if(GISettings.y<0.5)probeDepth=probeDepth*.5+.5;
+ if(probeDepth<=0.0||probeDepth>=1.0)break;
vec2 hitUV=clip.xy/clip.w*0.5+0.5;
if(any(lessThan(hitUV,vec2(0.002)))||any(greaterThan(hitUV,vec2(0.998))))break;
hitUV=giTexel(hitUV);float hitDepth=texture(WorldDepth,hitUV).r;
@@ -42,7 +45,8 @@ void main(){
// remainder instead of re-injecting bright grey ground into every nearby face.
vec3 linear=color*color;
vec3 chroma=linear-vec3(min(linear.r,min(linear.g,linear.b)));
- sum+=chroma*facing*falloff*edge*giFog(hit);
+ float confidence=smoothstep(.05,.3,clip.w)*(1.0-smoothstep(.12,.35,behind));
+ sum+=chroma*facing*falloff*edge*giFog(hit)*confidence;
}
break;
}
diff --git a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/include/ssgi.glsl b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/include/ssgi.glsl
index 12faea7..6e8914f 100644
--- a/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/include/ssgi.glsl
+++ b/mods/sanctuary/src/main/resources/assets/sanctuary/shaders/include/ssgi.glsl
@@ -12,23 +12,38 @@ vec3 giPosition(vec2 uv,float depth){
vec4 p=InverseViewProjection*vec4(uv*2.0-1.0,GISettings.y>0.5?depth:depth*2.0-1.0,1.0);
return p.xyz/p.w;
}
-vec2 giTexel(vec2 uv){vec2 size=vec2(textureSize(WorldDepth,0));return (floor(uv*size)+0.5)/size;}
+vec2 giTexel(vec2 uv){
+ vec2 size=vec2(textureSize(WorldDepth,0));
+ return (clamp(floor(uv*size),vec2(0),size-1.0)+0.5)/size;
+}
+vec3 giNeighbor(vec2 uv,vec3 center){
+ uv=giTexel(uv);float depth=texture(WorldDepth,uv).r;
+ return depth>0.0?giPosition(uv,depth):center;
+}
vec3 giNormal(vec2 uv,vec3 p){
vec2 pixel=1.0/vec2(textureSize(WorldDepth,0));
- vec3 l=giPosition(uv-vec2(pixel.x,0),texture(WorldDepth,uv-vec2(pixel.x,0)).r);
- vec3 r=giPosition(uv+vec2(pixel.x,0),texture(WorldDepth,uv+vec2(pixel.x,0)).r);
- vec3 d=giPosition(uv-vec2(0,pixel.y),texture(WorldDepth,uv-vec2(0,pixel.y)).r);
- vec3 u=giPosition(uv+vec2(0,pixel.y),texture(WorldDepth,uv+vec2(0,pixel.y)).r);
- vec3 dx=length(r-p)0.0&&(dot(l,l)0.0&&(dot(d,d).995){
n=a.x>a.y&&a.x>a.z?vec3(sign(n.x),0,0):a.y>a.z?vec3(0,sign(n.y),0):vec3(0,0,sign(n.z));
}
return dot(n,-p)<0.0?-n:n;
}
+float giConfidence(vec3 p,vec3 n){
+ // Distance to the receiving plane, not radial distance: no circular cut in
+ // a flat wall. Fade before entering it; also soften nearly tangent views.
+ float planeDistance=abs(dot(p,n));
+ return smoothstep(.06,.4,planeDistance)*smoothstep(.025,.12,planeDistance/max(length(p),.0001));
+}
float giFog(vec3 p){
float a=clamp((max(length(p.xz),abs(p.y))-GIFog.x)/max(GIFog.y-GIFog.x,0.001),0.0,1.0);
float b=clamp((length(p)-GIFog.z)/max(GIFog.w-GIFog.z,0.001),0.0,1.0);
diff --git a/packwiz/pack.toml b/packwiz/pack.toml
index 54036ba..e7f7ef4 100644
--- a/packwiz/pack.toml
+++ b/packwiz/pack.toml
@@ -1,6 +1,6 @@
name = "Sanctuary"
author = "KOKA99CAB"
-version = "beta.134"
+version = "beta.135"
pack-format = "packwiz:1.1.0"
[index]