fix: stabilize close-range SSGI and update shader defaults for beta.135
Build Sanctuary / build (push) Canceled after 0s
Build Sanctuary / build (push) Canceled after 0s
This commit is contained in:
@@ -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
|
L’économie, les claims et les dimensions décrits dans la vision restent
|
||||||
à implémenter.
|
à implémenter.
|
||||||
|
|
||||||
La version **beta.134** ajoute le **PBR · relief des matériaux** optionnel :
|
La version **beta.135** corrige le SSGI en contre-plongée et près des surfaces :
|
||||||
normales procédurales mises en cache, rugosité par matériau et reflets orientés.
|
normales indépendantes de la taille des pixels et atténuation progressive au
|
||||||
Le SSGI reçoit un filtre spatial guidé par la profondeur et des normales de faces
|
contact. Bloom initial à **20 %**, PBR et SSGI **activés par défaut**.
|
||||||
stabilisées. **Options → Shaders** ; réglages existants conservés.
|
**Options → Shaders** ; réglages existants conservés.
|
||||||
Le [ticket de livraison](docs/shader-beta134.md) décrit le rendu et ses limites.
|
Le [ticket de livraison](docs/shader-beta135.md) décrit le rendu et ses limites.
|
||||||
Release publiée et même instance **Sanctuary Beta** synchronisée, sauvegardes
|
Contrôles ciblés et archives vérifiés ; livraison prête.
|
||||||
et réglages conservés.
|
|
||||||
La [livraison précédente](docs/clay-workshop-integration-beta110.md) réunit déjà
|
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**.
|
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.
|
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.
|
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é
|
## beta.134 — PBR optionnel et SSGI filtré
|
||||||
|
|
||||||
Relief et reflets des matériaux ; lissage de la lumière indirecte.
|
Relief et reflets des matériaux ; lissage de la lumière indirecte.
|
||||||
|
|||||||
@@ -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`.
|
||||||
+2
-2
@@ -9,8 +9,8 @@ loom_version=1.17.20
|
|||||||
fabric_api_version=0.160.5+26.3
|
fabric_api_version=0.160.5+26.3
|
||||||
|
|
||||||
# Release counter: beta.001, .002, .003, ... (see docs/versioning.md).
|
# Release counter: beta.001, .002, .003, ... (see docs/versioning.md).
|
||||||
mod_version=beta.134
|
mod_version=beta.135
|
||||||
pack_version=beta.134
|
pack_version=beta.135
|
||||||
resource_pack_version=beta.121
|
resource_pack_version=beta.121
|
||||||
maven_group=fr.koka.sanctuary
|
maven_group=fr.koka.sanctuary
|
||||||
jei_version=30.32.0-sanctuary.3
|
jei_version=30.32.0-sanctuary.3
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ tasks.named('processGametestResources') {
|
|||||||
def ssgi132Client = providers.gradleProperty('sanctuarySsgi132ClientTests').map { it.toBoolean() }.getOrElse(false)
|
def ssgi132Client = providers.gradleProperty('sanctuarySsgi132ClientTests').map { it.toBoolean() }.getOrElse(false)
|
||||||
inputs.property('sanctuarySsgi132ClientTests', ssgi132Client)
|
inputs.property('sanctuarySsgi132ClientTests', ssgi132Client)
|
||||||
if (ssgi132Client) filter { line -> line.replace('fr.koka.sanctuary.gametest.SanctuaryClientRenderTests', 'fr.koka.sanctuary.client.shader.Ssgi132ClientChecks') }
|
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)
|
def bloom130Client = providers.gradleProperty('sanctuaryBloom130ClientTests').map { it.toBoolean() }.getOrElse(false)
|
||||||
inputs.property('sanctuaryBloom130ClientTests', bloom130Client)
|
inputs.property('sanctuaryBloom130ClientTests', bloom130Client)
|
||||||
|
|||||||
+2
-2
@@ -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");}
|
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);}
|
result.add("minecraft:nether_quartz_ore");result.add("minecraft:nether_gold_ore");return List.copyOf(result);}
|
||||||
static void run(ClientGameTestContext c,TestServerContext server){
|
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");
|
server.runOnServer(s->{command(s,"time set 18000");
|
||||||
for(int x=4;x<=20;x++)for(int z=4;z<=20;z++){
|
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());
|
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{
|
private static void preferences(){try{
|
||||||
var path=FabricLoader.getInstance().getConfigDir().resolve("sanctuary-shaders.json");Files.deleteIfExists(path);forgetPreferences();
|
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.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();
|
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");
|
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");}
|
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");}
|
||||||
|
|||||||
+1
@@ -160,6 +160,7 @@ final class EdgeHighlights129ClientChecks {
|
|||||||
check(VanillaLight.edgeHighlights(),"Toggle persists independently");
|
check(VanillaLight.edgeHighlights(),"Toggle persists independently");
|
||||||
VanillaLight.setEdgeHighlights(false);
|
VanillaLight.setEdgeHighlights(false);
|
||||||
VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);
|
VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);
|
||||||
|
VanillaLight.setPbr(false);VanillaLight.setSsgi(false);
|
||||||
} catch(java.io.IOException e) { throw new AssertionError(e); }
|
} catch(java.io.IOException e) { throw new AssertionError(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -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());
|
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);
|
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 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);
|
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);
|
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{
|
private static void preferences(){try{
|
||||||
var p=FabricLoader.getInstance().getConfigDir().resolve("sanctuary-shaders.json");Files.writeString(p,"{\"ssgi\":true,\"ssgiIntensity\":35,\"bloom\":false}");forgetPreferences();
|
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");}
|
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);
|
VanillaLight.setPbrIntensity(50);
|
||||||
}catch(Exception e){throw new AssertionError(e);}}
|
}catch(Exception e){throw new AssertionError(e);}}
|
||||||
|
|||||||
+22
-4
@@ -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. */
|
/** Real terrain/depth-map checks on a new flat seed 122 world; GPU readback is test-only. */
|
||||||
public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
||||||
private final boolean bloomOnly,ssgiOnly,pbrOnly;
|
private final boolean bloomOnly,ssgiOnly,pbrOnly,closeupOnly;
|
||||||
public PixelShadows122ClientChecks(){this(false);}
|
public PixelShadows122ClientChecks(){this(false);}
|
||||||
PixelShadows122ClientChecks(boolean bloomOnly){this(bloomOnly,false);}
|
PixelShadows122ClientChecks(boolean bloomOnly){this(bloomOnly,false);}
|
||||||
PixelShadows122ClientChecks(boolean bloomOnly,boolean ssgiOnly){this(bloomOnly,ssgiOnly,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 int FLOOR = 64;
|
||||||
private static final AABB PILLAR = new AABB(0, FLOOR + 1, 0, 2, FLOOR + 9, 2);
|
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);
|
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"));
|
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);
|
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(pbrOnly){Pbr134ClientChecks.run(c,server);return;}
|
||||||
if(ssgiOnly){Ssgi132ClientChecks.run(c,server);return;}
|
if(ssgiOnly){Ssgi132ClientChecks.run(c,server);return;}
|
||||||
if(bloomOnly){Bloom130ClientChecks.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.waitTicks(3);
|
||||||
c.takeScreenshot("sanctuary-beta134-shader-options-" + locale);
|
c.takeScreenshot("sanctuary-beta135-shader-options-" + locale);
|
||||||
}
|
}
|
||||||
c.runOnClient(m -> m.gui.setScreen(null));
|
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));
|
? 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() {
|
private static void checkPreferences() {
|
||||||
|
checkDefaults135();
|
||||||
|
VanillaLight.setPbr(false);
|
||||||
VanillaLight.setSsgi(false);
|
VanillaLight.setSsgi(false);
|
||||||
VanillaLight.setBloom(false);
|
VanillaLight.setBloom(false);
|
||||||
VanillaLight.setOreEmission(false);
|
VanillaLight.setOreEmission(false);
|
||||||
@@ -531,7 +549,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Frame capture(ClientGameTestContext c, String name) {
|
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<Frame>();
|
var result = new CompletableFuture<Frame>();
|
||||||
c.runOnClient(m -> Screenshot.takeScreenshot(m.gameRenderer.mainRenderTarget(), image -> {
|
c.runOnClient(m -> Screenshot.takeScreenshot(m.gameRenderer.mainRenderTarget(), image -> {
|
||||||
try (image) {
|
try (image) {
|
||||||
|
|||||||
+3
-2
@@ -13,7 +13,7 @@ import java.nio.file.Files;
|
|||||||
public final class Ssgi132ClientChecks implements FabricClientGameTest {
|
public final class Ssgi132ClientChecks implements FabricClientGameTest {
|
||||||
public void runTest(ClientGameTestContext c){new PixelShadows122ClientChecks(false,true).runTest(c);}
|
public void runTest(ClientGameTestContext c){new PixelShadows122ClientChecks(false,true).runTest(c);}
|
||||||
static void run(ClientGameTestContext c,TestServerContext server){
|
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++){
|
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());
|
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());
|
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->check(!ScreenSpaceGI.allocated(),"Main shader OFF releases SSGI too"));
|
||||||
c.runOnClient(m->VanillaLight.setEnabled(true));frames(c);enable(c,false);
|
c.runOnClient(m->VanillaLight.setEnabled(true));frames(c);enable(c,false);
|
||||||
motion(c,server);
|
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);
|
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){
|
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 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{
|
private static void preferences(){try{
|
||||||
var p=FabricLoader.getInstance().getConfigDir().resolve("sanctuary-shaders.json");Files.writeString(p,"{\"bloom\":false,\"shadowPixels\":128}");forgetPreferences();
|
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");}
|
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);
|
VanillaLight.setSsgiIntensity(90);
|
||||||
}catch(Exception e){throw new AssertionError(e);}}
|
}catch(Exception e){throw new AssertionError(e);}}
|
||||||
|
|||||||
+78
@@ -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<a.height()*7/8;y++){
|
||||||
|
double sum=0;int count=0;
|
||||||
|
for(int x=a.width()/4;x<a.width()*3/4;x++){
|
||||||
|
int i=y*a.width()+x,pa=a.pixels()[i],pb=b.pixels()[i];
|
||||||
|
int r=pa>>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(count<a.width()/8)continue;
|
||||||
|
double mean=sum/count;if(rows>0)max=Math.max(max,Math.abs(mean-previous));
|
||||||
|
previous=mean;gain+=mean;rows++;
|
||||||
|
}
|
||||||
|
return new Rows(max,gain/Math.max(rows,1),rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,11 +19,11 @@ public final class VanillaLight {
|
|||||||
int saturation=142;
|
int saturation=142;
|
||||||
boolean pixelShadows=true;
|
boolean pixelShadows=true;
|
||||||
boolean bloom=true;
|
boolean bloom=true;
|
||||||
boolean pbr=false;
|
boolean pbr=true;
|
||||||
int pbrIntensity=50;
|
int pbrIntensity=50;
|
||||||
boolean ssgi=false;
|
boolean ssgi=true;
|
||||||
int ssgiIntensity=35;
|
int ssgiIntensity=35;
|
||||||
int bloomIntensity=35;
|
int bloomIntensity=20;
|
||||||
int bloomRadius=50;
|
int bloomRadius=50;
|
||||||
boolean oreEmission=true;
|
boolean oreEmission=true;
|
||||||
int oreIntensity=50;
|
int oreIntensity=50;
|
||||||
|
|||||||
@@ -2279,7 +2279,7 @@
|
|||||||
"sanctuary.shaders.bloom": "Bloom",
|
"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_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": "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_intensity_value": "Bloom: %s%%",
|
||||||
"sanctuary.shaders.bloom_radius": "Bloom spread",
|
"sanctuary.shaders.bloom_radius": "Bloom spread",
|
||||||
"sanctuary.shaders.bloom_radius_help": "Halo size around luminous pixels. Texture details stay sharp.",
|
"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_help": "Inclusion brightness. Does not change world light levels or mob spawning.",
|
||||||
"sanctuary.shaders.ore_intensity_value": "Ores: %s%%",
|
"sanctuary.shaders.ore_intensity_value": "Ores: %s%%",
|
||||||
"sanctuary.shaders.ssgi": "SSGI · color bounce",
|
"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": "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_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.ssgi_intensity_value": "SSGI: %s%%",
|
||||||
"sanctuary.shaders.pbr": "PBR · material relief",
|
"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": "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_help": "Material relief and highlights. Zero stops rendering and frees the cache. Original textures remain unchanged.",
|
||||||
"sanctuary.shaders.pbr_intensity_value": "PBR: %s%%"
|
"sanctuary.shaders.pbr_intensity_value": "PBR: %s%%"
|
||||||
|
|||||||
@@ -2279,7 +2279,7 @@
|
|||||||
"sanctuary.shaders.bloom": "Bloom",
|
"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_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": "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_intensity_value": "Bloom : %s %%",
|
||||||
"sanctuary.shaders.bloom_radius": "Diffusion du bloom",
|
"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.",
|
"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_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.ore_intensity_value": "Minerais : %s %%",
|
||||||
"sanctuary.shaders.ssgi": "SSGI · couleurs diffusées",
|
"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": "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_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.ssgi_intensity_value": "SSGI : %s %%",
|
||||||
"sanctuary.shaders.pbr": "PBR · relief des matériaux",
|
"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": "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_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 %%"
|
"sanctuary.shaders.pbr_intensity_value": "PBR : %s %%"
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
layout(location=0) in vec2 texCoord;
|
layout(location=0) in vec2 texCoord;
|
||||||
layout(location=0) out vec4 fragColor;
|
layout(location=0) out vec4 fragColor;
|
||||||
void main(){
|
void main(){
|
||||||
float depth=texture(WorldDepth,texCoord).r;
|
vec2 surface=giTexel(texCoord);float depth=texture(WorldDepth,surface).r;
|
||||||
if(depth<=0.0)discard;
|
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);
|
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;
|
vec3 bounce=vec3(0.0);float total=0.0;
|
||||||
for(int y=0;y<2;y++)for(int x=0;x<2;x++){
|
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
|
// 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
|
// of the transport on bright blocks. Compose in approximate linear light and
|
||||||
// encode once; a soft shoulder keeps strong bounce below channel clipping.
|
// 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 reflected=linearScene*irradiance;
|
||||||
vec3 lit=linearScene+(1.0-linearScene)*(1.0-exp(-reflected));
|
vec3 lit=linearScene+(1.0-linearScene)*(1.0-exp(-reflected));
|
||||||
fragColor=vec4(sqrt(clamp(lit,0.0,1.0)),0.0);
|
fragColor=vec4(sqrt(clamp(lit,0.0,1.0)),0.0);
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ void main(){
|
|||||||
float travel=GISettings.z*float(step)/10.0;
|
float travel=GISettings.z*float(step)/10.0;
|
||||||
vec3 probe=start+direction*travel;
|
vec3 probe=start+direction*travel;
|
||||||
vec4 clip=ViewProjection*vec4(probe,1.0);
|
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;
|
vec2 hitUV=clip.xy/clip.w*0.5+0.5;
|
||||||
if(any(lessThan(hitUV,vec2(0.002)))||any(greaterThan(hitUV,vec2(0.998))))break;
|
if(any(lessThan(hitUV,vec2(0.002)))||any(greaterThan(hitUV,vec2(0.998))))break;
|
||||||
hitUV=giTexel(hitUV);float hitDepth=texture(WorldDepth,hitUV).r;
|
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.
|
// remainder instead of re-injecting bright grey ground into every nearby face.
|
||||||
vec3 linear=color*color;
|
vec3 linear=color*color;
|
||||||
vec3 chroma=linear-vec3(min(linear.r,min(linear.g,linear.b)));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
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;
|
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){
|
vec3 giNormal(vec2 uv,vec3 p){
|
||||||
vec2 pixel=1.0/vec2(textureSize(WorldDepth,0));
|
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 l=p-giNeighbor(uv-vec2(pixel.x,0),p),r=giNeighbor(uv+vec2(pixel.x,0),p)-p;
|
||||||
vec3 r=giPosition(uv+vec2(pixel.x,0),texture(WorldDepth,uv+vec2(pixel.x,0)).r);
|
vec3 d=p-giNeighbor(uv-vec2(0,pixel.y),p),u=giNeighbor(uv+vec2(0,pixel.y),p)-p;
|
||||||
vec3 d=giPosition(uv-vec2(0,pixel.y),texture(WorldDepth,uv-vec2(0,pixel.y)).r);
|
vec3 dx=dot(l,l)>0.0&&(dot(l,l)<dot(r,r)||dot(r,r)==0.0)?l:r;
|
||||||
vec3 u=giPosition(uv+vec2(0,pixel.y),texture(WorldDepth,uv+vec2(0,pixel.y)).r);
|
vec3 dy=dot(d,d)>0.0&&(dot(d,d)<dot(u,u)||dot(u,u)==0.0)?d:u;
|
||||||
vec3 dx=length(r-p)<length(p-l)?r-p:p-l;
|
// Normalize directions BEFORE the cross product. Pixel area approaches zero
|
||||||
vec3 dy=length(u-p)<length(p-d)?u-p:p-d;
|
// near a surface (or at high resolution); it is not a validity criterion.
|
||||||
vec3 n=cross(dx,dy);n/=max(length(n),0.00001);
|
float xx=dot(dx,dx),yy=dot(dy,dy);
|
||||||
// Keep voxel planes exact: tiny depth-reconstruction errors must not rotate an entire ray fan.
|
if(xx<1e-20||yy<1e-20)return vec3(0);
|
||||||
|
vec3 n=cross(dx*inversesqrt(xx),dy*inversesqrt(yy));
|
||||||
|
float nn=dot(n,n);if(nn<1e-12)return vec3(0);n*=inversesqrt(nn);
|
||||||
vec3 a=abs(n);
|
vec3 a=abs(n);
|
||||||
if(max(a.x,max(a.y,a.z))>.995){
|
if(max(a.x,max(a.y,a.z))>.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));
|
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;
|
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 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 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);
|
float b=clamp((length(p)-GIFog.z)/max(GIFog.w-GIFog.z,0.001),0.0,1.0);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
name = "Sanctuary"
|
name = "Sanctuary"
|
||||||
author = "KOKA99CAB"
|
author = "KOKA99CAB"
|
||||||
version = "beta.134"
|
version = "beta.135"
|
||||||
pack-format = "packwiz:1.1.0"
|
pack-format = "packwiz:1.1.0"
|
||||||
|
|
||||||
[index]
|
[index]
|
||||||
|
|||||||
Reference in New Issue
Block a user