Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
216d3f75b5 | ||
|
|
e48fc656c7 | ||
|
|
7e21781a9a | ||
|
|
d941b91265 | ||
|
|
078d3e6833 | ||
|
|
afa77f3d62 |
@@ -20,12 +20,10 @@ 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.130** ajoute un bloom réglable, les inclusions émissives des
|
||||
minerais et une **Précision** commune aux ombres et au highlight. Par défaut :
|
||||
ombres activées à 128 blocs, highlight à 32 blocs, précision 16 pixels/bloc.
|
||||
**Options → Shaders → Sanctuary · Vanilla Light**. Les réglages personnalisés
|
||||
sont conservés. Le [ticket de livraison](docs/shader-beta130.md) précise le
|
||||
résultat, les vérifications et l'état de distribution.
|
||||
La version **beta.133** renforce le **SSGI · couleurs diffusées** : atténuation
|
||||
spatiale revue et composition lumineuse plus visible dès 35 %.
|
||||
**Options → Shaders** ; intensité réglable, préférences existantes conservées.
|
||||
Le [ticket de livraison](docs/shader-beta133.md) décrit le rendu et ses limites.
|
||||
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.
|
||||
@@ -36,11 +34,36 @@ La [carte native beta.005](docs/atlas-beta005.md) prend le relais après
|
||||
l’étude de Xaero et des alternatives libres.
|
||||
|
||||
|
||||
## beta.133 — SSGI renforcé
|
||||
|
||||
Couleurs voisines plus visibles, même budget de rayons et de cibles GPU.
|
||||
[Contrat et vérifications](docs/shader-beta133.md) ·
|
||||
[Pack normal](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.133/Sanctuary-beta.133.mrpack) ·
|
||||
[Pack de test](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.133/Sanctuary-Test-beta.133.mrpack).
|
||||
|
||||
## beta.132 — SSGI : diffusion locale des couleurs
|
||||
|
||||
Rebond diffus des couleurs visibles, intensité réglable et reconstruction
|
||||
préservant les silhouettes. Expérimental, initialement désactivé.
|
||||
[Contrat et vérifications](docs/shader-beta132.md) ·
|
||||
[Pack normal](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.132/Sanctuary-beta.132.mrpack) ·
|
||||
[Pack de test](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.132/Sanctuary-Test-beta.132.mrpack).
|
||||
|
||||
## beta.131 — Émissifs indépendants et précision étendue
|
||||
|
||||
Bloom, émissifs et highlight activés initialement ; halo facultatif et cinq
|
||||
précisions communes de 8 à 128 pixels/bloc.
|
||||
[Contrat et vérifications](docs/shader-beta131.md) ·
|
||||
[Pack normal](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.131/Sanctuary-beta.131.mrpack) ·
|
||||
[Pack de test](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.131/Sanctuary-Test-beta.131.mrpack).
|
||||
|
||||
## beta.130 — Bloom, minerais émissifs et précision commune
|
||||
|
||||
Halo réglable des sources lumineuses et inclusions des vingt textures de
|
||||
minerais ; réglages par défaut 32/128/16 et ombres activées.
|
||||
[Contrat et vérifications](docs/shader-beta130.md).
|
||||
[Contrat et vérifications](docs/shader-beta130.md) ·
|
||||
[Pack normal](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.130/Sanctuary-beta.130.mrpack) ·
|
||||
[Pack de test](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.130/Sanctuary-Test-beta.130.mrpack).
|
||||
|
||||
## beta.129 — Reflets CTM et ombres jusqu'à 256 blocs
|
||||
|
||||
|
||||
@@ -14,6 +14,41 @@ JAR Sanctuary sont des pièces jointes de releases Gitea. Aucun JAR de mod ou
|
||||
dépendance téléchargée n'entre dans l'historique Git. Le canal Beta est distinct
|
||||
de l'ancien pack 26.2.
|
||||
|
||||
## beta.132 — SSGI expérimental
|
||||
|
||||
Diffusion locale des couleurs visibles, rayon 2,5 blocs, intensité réglable,
|
||||
initialement OFF. [Contrat et vérifications](shader-beta132.md).
|
||||
[Release beta.132](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.132)
|
||||
publiée ; canal `9edaaedc0205c525ecce9de66b5ca7827c56c28f`. Deux synchronisations isolées,
|
||||
puis deux dans la même instance Sanctuary Beta réussies. Les 923 fichiers
|
||||
personnels suivis gardent leurs hashes. Aucun monde personnel ouvert ;
|
||||
sauvegarde préalable `sanctuary-backups/before-beta.132/`.
|
||||
Packs normal/Test/template vérifiés dans `sanctuary-beta/build/`.
|
||||
|
||||
## beta.131 — émissifs indépendants et précision étendue
|
||||
|
||||
Bloom, émissifs et highlight ON initialement. Couper le halo ou régler son
|
||||
intensité à zéro garde les inclusions lumineuses. Précisions 64/128 ajoutées,
|
||||
défaut 16 conservé. [Contrat et vérifications](shader-beta131.md).
|
||||
[Release beta.131](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.131)
|
||||
publiée ; canal `efb18c274d95d988a9566b8c52751469f002cdda`. Deux synchronisations isolées,
|
||||
puis deux dans la même instance Sanctuary Beta réussies. Les 923 fichiers
|
||||
personnels suivis sont conservés, préférences et sauvegardes comprises.
|
||||
Aucun monde personnel ouvert ; sauvegarde `sanctuary-backups/before-beta.131/`.
|
||||
Archives normal/Test/template vérifiées dans `sanctuary-beta/build/`.
|
||||
|
||||
## beta.130 — bloom et précision commune
|
||||
|
||||
Bloom réglable, masques sélectifs des vingt textures de minerais, précision
|
||||
commune aux ombres et au highlight. Réglages initiaux 32/128/16 et Pixel Shadows
|
||||
activé ; anciennes préférences conservées. [Contrat et vérifications](shader-beta130.md).
|
||||
[Release beta.130](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.130)
|
||||
publiée ; canal `68e06353bf880af04c1c72d4e3a159f11f94b619`. Deux synchronisations isolées
|
||||
puis deux dans la même instance **Sanctuary Beta** réussies. Les 923 fichiers
|
||||
personnels suivis, réglages de shaders compris, gardent leurs hashes. Aucun
|
||||
monde personnel ouvert ; sauvegarde préalable `sanctuary-backups/before-beta.130/`.
|
||||
Packs normal/Test et template vérifiés et copiés dans `sanctuary-beta/build/`.
|
||||
|
||||
## beta.129 — reflets CTM et portées 16–256 blocs
|
||||
|
||||
Reflets connectés entre tous les matériaux coplanaires, portée indépendante,
|
||||
|
||||
+14
-1
@@ -115,4 +115,17 @@ Archives vérifiées dans `build/` et recopiées dans le dossier habituel
|
||||
|
||||
JAR : `1ac4d1b1339361fb665f03ca3bd359163db5037ffd90e684c5a461759f6076e7`.
|
||||
|
||||
Publication et synchronisation de l'instance à effectuer après le commit source.
|
||||
[Release beta.130](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.130)
|
||||
publiée depuis `534dfec4f88169a200702b50d1ba87d85c107bee`. Canal packwiz
|
||||
`68e06353bf880af04c1c72d4e3a159f11f94b619`. Les téléchargements publics sont vérifiés par SHA-256.
|
||||
Deux synchronisations isolées, puis deux dans la même instance **Sanctuary
|
||||
Beta**, réussissent. Un seul JAR Sanctuary beta.130 est actif. Les
|
||||
**923 fichiers personnels suivis** conservent leurs hashes, dont les mondes,
|
||||
packs et préférences de shaders. Sauvegarde préalable dans
|
||||
`sanctuary-backups/before-beta.130/` de l'instance. Aucun monde personnel ouvert.
|
||||
|
||||
Reçus locaux ignorés : `build/shader130-artifact.json`,
|
||||
`build/shader130-isolated.json`, `build/shader130-prism.json` et
|
||||
`build/shader130-publication.log`. Le checkout original conserve ses
|
||||
modifications préexistantes ; seuls les trois artefacts versionnés vérifiés
|
||||
sont recopiés dans son dossier `build/`.
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# SHADER-131 — émissifs indépendants et réglages initiaux
|
||||
|
||||
Demande : activer par défaut bloom, minerais émissifs et highlight ; garder
|
||||
les émissifs avec halo coupé ou intensité du bloom à zéro, sans bouton ajouté ;
|
||||
ajouter 64 et 128 pixels/bloc en options. Précision initiale 16, distances
|
||||
highlight 32 et ombres 128 conservées. Préférences personnalisées préservées.
|
||||
Branche `codex/emissive-beta131`, socle beta.130, Minecraft 26.3 / Java 25.
|
||||
|
||||
Le futur effet demandé est une légère teinte diffusée depuis les blocs
|
||||
voisins, pas des reflets reconnaissables. Étude seulement pour ce ticket ;
|
||||
aucun SSR ou éclairage indirect expérimental activé.
|
||||
|
||||
## Comportement et validation
|
||||
|
||||
Le curseur à zéro et l'interrupteur Bloom coupent uniquement le halo.
|
||||
Le réglage existant des minerais contrôle leurs inclusions nettes séparément.
|
||||
Sans halo, les cibles de diffusion GPU sont libérées ; l'émission garde
|
||||
seulement les ressources nécessaires. Tout désactiver libère l'ensemble.
|
||||
La grille commune propose 8, 16, 32, 64 et 128, sans augmenter le plafond
|
||||
4096 de la carte d'ombres. Libellés FR/EN et essais clients natifs sur les
|
||||
inclusions, l'absence de halo, les transitions et les nouvelles précisions.
|
||||
Aucun changement de lumière serveur, de génération ou de sauvegardes.
|
||||
|
||||
Le cœur émissif et le halo utilisent maintenant deux chemins indépendants.
|
||||
Les textures et leur analyse restent celles de beta.130. Avec le halo coupé,
|
||||
la carte d'émission est effacée avant les faces, le cœur net est composé,
|
||||
et les passes de ciel, réduction, diffusion et application du halo sont
|
||||
omises. Le retour au bloom recrée ses cibles. Aucun bouton ajouté, aides
|
||||
FR/EN adaptées et choix explicitement désactivés toujours conservés.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Suite client native réussie en **3 min 46 s**, monde plat neuf de graine 122,
|
||||
macOS / Apple M1 / OpenGL. Cinq précisions sauvegardées et chargées, bornes
|
||||
et seuils de normalisation, carte d'ombres plafonnée et arêtes CTM rendues
|
||||
à 64/128. Le gain mesuré des traits diminue de 0,02778 à précision 32 à
|
||||
0,02094 à 64, puis 0,01013 à 128 dans la même scène.
|
||||
|
||||
Les inclusions restent nettes sans halo : le rendu Bloom OFF correspond
|
||||
au rendu intensité zéro. Aucun gain mesuré hors du masque source, ressources
|
||||
de diffusion absentes ; couper aussi les émissifs libère l'ensemble et
|
||||
restitue l'image initiale. Réactiver le bloom restitue son image précédente.
|
||||
Captures OFF/zéro inspectées. Valeurs initiales ON vérifiées ; un ancien
|
||||
choix explicitement OFF reste OFF. Les essais beta.130 (vingt masques,
|
||||
occultation, sources, rechargement), lune, feuillage et realtime passent.
|
||||
|
||||
Le premier essai rencontrait une assertion historique attendant le highlight
|
||||
OFF lorsqu'une clé était absente. Elle attend désormais la nouvelle valeur
|
||||
ON ; les cas de préférences explicitement désactivées restent testés.
|
||||
|
||||
Commande : `./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true
|
||||
-PsanctuaryPixelShadows122ClientTests=true -PsanctuaryClientNoVsync=true
|
||||
-PsanctuaryQuickTests=true`. Log : `build/shader131-client-full.log` ; captures
|
||||
sous `mods/sanctuary/build/run/clientGameTest/screenshots/`.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack -x :sanctuary:runGameTest`
|
||||
réussit en **2 min 30 s**, 126 tâches. Les archives normal/Test, leurs sources
|
||||
et le template sont vérifiés. Six entrées de production changent : moteur
|
||||
Bloom, préférences, interface et libellés. Gameplay, shaders GLSL et textures
|
||||
restent identiques à beta.130. Aucun résultat Windows/Vulkan
|
||||
revendiqué ; le serveur GameTest dédié reste exclu conformément au refus
|
||||
antérieur de son EULA.
|
||||
|
||||
## Piste suivante — diffusion des couleurs
|
||||
|
||||
Choix confirmé : légère teinte des surfaces voisines. Une estimation SSGI
|
||||
utilise la couleur et la profondeur visibles pour des rebonds diffus ; voir
|
||||
la [documentation primaire Unity](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@17.0/manual/Override-Screen-Space-GI.html).
|
||||
Proposition à expérimenter : faible intensité, courte portée autour des
|
||||
surfaces, calcul réduit, rejet par profondeur pour ne pas teinter au travers
|
||||
des murs. Vérifier les bords de l'écran, les blocs hors champ, la stabilité
|
||||
en mouvement et les textures avant de fixer une qualité. Cette piste n'est
|
||||
pas implémentée dans beta.131.
|
||||
|
||||
## Livraison
|
||||
|
||||
- `Sanctuary-beta.131.mrpack` : 10386312 octets ; SHA-256 `e02067e424bf4f54512d5cfb1b5cde0fe232aa6bdadc0d38981cd15817a6387a`.
|
||||
- `Sanctuary-Test-beta.131.mrpack` : 10405238 octets ; SHA-256 `298387cce5a5b7cdc09b3a9d24859b674a736b25abcbafa7995b813757641265`.
|
||||
|
||||
JAR : `87210da23f7781efccdbdbf5d4e2d3c1ff1857a7f326027b93d6a301c7dfdb0d`.
|
||||
|
||||
Archives vérifiées recopiées dans `sanctuary-beta/build/`. [Release beta.131](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.131)
|
||||
publiée depuis `078d3e6833daa7de7f6c6d0e1a6c17d5dbefe075`. Canal packwiz
|
||||
`efb18c274d95d988a9566b8c52751469f002cdda`. Téléchargements publics vérifiés par hash.
|
||||
Deux synchronisations isolées, puis deux dans la même instance **Sanctuary Beta**
|
||||
réussissent. Un seul JAR Sanctuary beta.131 ; les **923 fichiers personnels
|
||||
suivis** conservent leurs hashes, dont les préférences de shaders et les mondes.
|
||||
Sauvegarde préalable `sanctuary-backups/before-beta.131/` ; aucun monde personnel
|
||||
ouvert. Reçus ignorés : `build/shader131-artifact.json`,
|
||||
`build/shader131-isolated.json`, `build/shader131-prism.json` et
|
||||
`build/shader131-publication.log`. Le checkout original conserve ses changements.
|
||||
@@ -0,0 +1,106 @@
|
||||
# SHADER-132 — diffusion locale SSGI expérimentale
|
||||
|
||||
Demande : légère teinte diffusée des surfaces voisines, sans reflets miroir
|
||||
ni shader d'eau. Branche `codex/ssgi-beta132`, socle beta.131, Minecraft 26.3 /
|
||||
Java 25. Aucun changement de génération, sauvegarde ou lumière serveur.
|
||||
|
||||
## Résultat
|
||||
|
||||
**Options → Shaders → SSGI · couleurs diffusées** active un rebond diffus
|
||||
local. L'effet expérimental est initialement OFF, avec une intensité préparée
|
||||
à **35 %** ; le curseur propose 0–100. À zéro ou OFF, ses cibles et uniformes
|
||||
GPU sont libérés. Les autres effets et leurs préférences sont conservés.
|
||||
|
||||
La couleur d'une surface visible peut teinter les surfaces proches qui lui
|
||||
font face. Le calcul porte à **2,5 blocs** du récepteur et s'estompe entre
|
||||
24 et 32 blocs de la caméra. Il utilise une copie de la couleur native après
|
||||
les ombres, avec la profondeur et des normales reconstruites. Le premier
|
||||
obstacle arrête chaque rayon, même si son épaisseur empêche de retenir une
|
||||
intersection. Les plans coplanaires ne s'éclairent pas eux-mêmes.
|
||||
|
||||
Douze directions hémisphériques fixes, au maximum dix étapes par rayon,
|
||||
sont évaluées à demi-résolution en largeur et hauteur. La reconstruction
|
||||
plein écran est guidée par la profondeur pour conserver les silhouettes et
|
||||
les détails des textures. Le cœur des émissifs, le highlight et le bloom
|
||||
sont composés ensuite : pas de halo réinjecté dans le rebond ni de boucle
|
||||
de rétroaction d'une image sur l'autre. Aucune lecture GPU vers CPU ni
|
||||
recherche de chunks en production. Pas de bruit temporel ou d'historique.
|
||||
|
||||
Deux cibles, RGBA8 plein écran et RGBA16 flottant à demi-résolution, soit
|
||||
environ six octets par pixel de l'écran, plus 176 octets d'uniformes par
|
||||
segment du tampon natif. Redimensionnement, arrêt, déconnexion et
|
||||
rechargement libèrent/recréent les ressources possédées. Ce budget n'est
|
||||
pas une garantie de FPS ; le SSGI coûte davantage qu'un simple réglage de
|
||||
colorimétrie.
|
||||
|
||||
## Limites de cette version
|
||||
|
||||
L'estimation utilise seulement la couleur et la profondeur visibles. Les
|
||||
blocs hors champ ou cachés n'apportent pas leur couleur. Le résultat peut
|
||||
changer lorsqu'ils entrent ou sortent de l'image ; une atténuation près
|
||||
des bords limite la coupure. Les surfaces transparentes absentes de la
|
||||
profondeur ne sont pas reconstruites. Petits objets et intersections plus
|
||||
minces que l'échantillonnage peuvent manquer. Il s'agit d'une approximation
|
||||
diffuse locale, pas d'une simulation complète d'éclairage global ni de
|
||||
reflets spéculaires. Aucune modification de l'eau et aucune source hors
|
||||
champ déduite ou générée.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Test ciblé natif réussi en **1 min** sur macOS / Apple M1 / OpenGL, nouveau
|
||||
monde plat de graine 122. À intensité 90, les échantillons du sol blanc
|
||||
montrent un gain rouge cumulé de 8 968 contre 20 bleu devant le mur rouge,
|
||||
puis 9 168 bleu contre 23 rouge devant le mur bleu. Le sol seul est inchangé.
|
||||
OFF/zéro restituent l'image de base et libèrent les cibles ; intensité 20
|
||||
réduit le gain. Changer la couleur du mur complètement occulté laisse
|
||||
l'image inchangée. Rechargement, caméra fixe, mouvement infinitésimal et
|
||||
retour à la caméra d'origine passent, sans accumulation de couleur.
|
||||
|
||||
Deux ajustements de fixture ont précédé ce résultat : inclure dans les
|
||||
mesures le blanc légèrement bleuté par l'éclairage natif, et formater les
|
||||
petits angles de `/tp` en décimal car la commande refuse la notation
|
||||
scientifique. Aucun comportement du jeu modifié pour ces corrections.
|
||||
|
||||
Log ciblé : `build/shader132-client-ssgi.log`. La suite complète réussit en
|
||||
**4 min 18 s** et vérifie aussi une fenêtre 961 × 541, le retour à sa taille
|
||||
initiale, le SSGI avec ombres/highlight/bloom/émissifs actifs, ainsi que
|
||||
l'arrêt global du shader. Les captures françaises et anglaises passent.
|
||||
Les contrôles précédents des cinq précisions, ombres, lune, feuillage, CTM,
|
||||
bloom et émissifs restent réussis. Log : `build/shader132-client-full.log`.
|
||||
|
||||
Commandes : `./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true
|
||||
-PsanctuaryPixelShadows122ClientTests=true -PsanctuaryClientNoVsync=true
|
||||
-PsanctuaryQuickTests=true`. Pour le test ciblé, remplacer la propriété
|
||||
PixelShadows par `-PsanctuarySsgi132ClientTests=true`.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack -x :sanctuary:runGameTest`
|
||||
réussit en **2 min 25 s**, 126 tâches. Les packs normal/Test contiennent le
|
||||
même JAR ; sources archivées et template de ressources correspondent aux
|
||||
sources. Comparaison avec beta.131 : **11 entrées de production** modifiées,
|
||||
limitées au SSGI, à ses options et à ses points d'appel. Gameplay et textures
|
||||
des gemmes inchangés, aucun monde ni test embarqué. Aucun résultat
|
||||
Windows/Vulkan revendiqué.
|
||||
Le serveur GameTest dédié reste exclu conformément au refus antérieur de
|
||||
son EULA. Les captures panoramiques et les milieux de brume sous l'eau/lave
|
||||
ou neige poudreuse suspendent cette passe, comme les autres effets de
|
||||
profondeur Sanctuary.
|
||||
|
||||
## Livraison
|
||||
|
||||
- `Sanctuary-beta.132.mrpack` : 10394026 octets, SHA-256 `a7c0823e00f36925919645e32e7792242ec1d01015b41bee5b4f6285b9fc533d`.
|
||||
- `Sanctuary-Test-beta.132.mrpack` : 10412949 octets, SHA-256 `9bc30a7d6d5834f9323f15f0c39d3f49d200e1a97d739266ab373c9e1ba4a8fd`.
|
||||
|
||||
JAR : `83eb2d091a7acd85eaa8b3b6a00a40ea483f9eeb2a30c033bdf68b4d717c5142`.
|
||||
|
||||
Archives vérifiées recopiées dans `sanctuary-beta/build/`. [Release beta.132](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.132)
|
||||
publiée depuis `7e21781a9a08c669c7d6b65653bd9d408887689c`. Canal packwiz
|
||||
`9edaaedc0205c525ecce9de66b5ca7827c56c28f` ; téléchargements publics vérifiés par SHA-256.
|
||||
Deux synchronisations isolées, puis deux dans la même instance **Sanctuary Beta**,
|
||||
réussissent. Un seul JAR beta.132 actif ; les **923 fichiers personnels suivis**,
|
||||
dont sauvegardes et préférences de shaders, gardent leurs hashes. Sauvegarde
|
||||
préalable `sanctuary-backups/before-beta.132/` ; aucun monde personnel ouvert.
|
||||
Le checkout original conserve ses changements préexistants.
|
||||
|
||||
Reçus locaux ignorés : `build/shader132-artifact.json`,
|
||||
`build/shader132-isolated.json`, `build/shader132-prism.json`,
|
||||
`build/shader132-publication.log`.
|
||||
@@ -0,0 +1,89 @@
|
||||
# SHADER-133 — SSGI visible au réglage courant
|
||||
|
||||
Demande : le rebond diffus de beta.132 est trop discret pour être distingué.
|
||||
Branche `codex/ssgi-strength-beta133`, socle beta.132, Minecraft 26.3 / Java 25.
|
||||
|
||||
## Changement
|
||||
|
||||
La collecte conserve douze directions, dix étapes, une demi-résolution et
|
||||
un rayon de 2,5 blocs. L'atténuation spatiale devient linéaire plutôt que
|
||||
quadratique. Le mélange est reconstruit en lumière approximativement linéaire,
|
||||
puis réencodé une seule fois. Un gain artistique plus fort rend la couleur
|
||||
lisible et une courbe progressive évite de couper brutalement les canaux
|
||||
à blanc. Les surfaces sans rebond restent intactes. La force demeure réglable
|
||||
de 0 à 100, avec le même réglage initial de 35 ; aucune préférence enregistrée
|
||||
n'est écrasée. SSGI reste optionnel, initialement désactivé.
|
||||
|
||||
Aucun rayon, cible GPU ou texture de matériau supplémentaire. La composition
|
||||
ajoute une exponentielle et une racine carrée par canal ; le coût exact en FPS
|
||||
reste à mesurer sur la machine du joueur. Les normales de géométrie orientent
|
||||
déjà le rebond. Pas de PBR ni de normales procédurales de textures ajoutés.
|
||||
|
||||
## Piste matériaux
|
||||
|
||||
Pour une étape distincte, des normales de faible amplitude calculées au
|
||||
chargement des textures, mises en cache, et une rugosité par famille de matériaux
|
||||
permettraient d'étudier du relief et des reflets orientés. Une simple conversion
|
||||
luminosité → hauteur ne connaît pas le relief réel : un motif peint peut devenir
|
||||
une bosse artificielle. Commencer sur pierre, briques et bois avec une amplitude
|
||||
réglable serait préférable à une génération aveugle pour tous les blocs.
|
||||
La passe actuelle ne possède ni atlas de normales, ni identifiants de matériaux,
|
||||
ni albédo non éclairé ; des images seules ne suffiraient pas à les exploiter.
|
||||
|
||||
## Limites conservées
|
||||
|
||||
Approximation diffuse à partir de la couleur déjà éclairée, pas un transport
|
||||
physique complet. Seules les surfaces visibles contribuent, avec variations
|
||||
possibles aux entrées/sorties d'écran. Premier obstacle bloquant, pas d'historique
|
||||
ni de bruit temporel ; aucune modification d'eau, monde ou lumière serveur.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Suite client native complète réussie en **4 min 29 s**, sur macOS / Apple M1 /
|
||||
OpenGL, dans un monde de test plat (graine 122). Les comparaisons ON/OFF à 35 %
|
||||
mesurent, sur les pixels neutres du sol :
|
||||
|
||||
| Mur | Pixels avec gain ≥ 8/255 | Pixels avec gain ≥ 16/255 | Gain cumulé du canal dominant |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Rouge | 16 487 | 7 311 | 305 221 |
|
||||
| Bleu | 14 954 | 7 328 | 309 991 |
|
||||
|
||||
À 90 %, les gains dominants atteignent 654 683 et 655 867, contre 8 968 et
|
||||
9 168 au même réglage dans la fixture beta.132. Ce rapport concerne uniquement
|
||||
cette scène et ces pixels, pas une promesse de gain uniforme dans tous les mondes.
|
||||
Les captures confirment une bande colorée au pied du mur. Un premier réglage
|
||||
intermédiaire a été rejeté car aucun pixel du sol ne dépassait 8/255 à 35 % ;
|
||||
un second a passé les contrôles ciblés, puis la force a encore été augmentée
|
||||
après examen visuel. Les valeurs finales sont celles de la suite complète.
|
||||
|
||||
Le test impose maintenant plus de 5 000 pixels à 8/255 et plus de 500 à 16/255
|
||||
au réglage 35, pour les deux couleurs. Intensité, OFF/zéro, absence de rebond
|
||||
coplanaire, occultation d'un mur changeant de couleur, caméra fixe, déplacement
|
||||
infinitésimal, retour de caméra, redimensionnement et rechargement passent.
|
||||
Ombres, lune, feuillage, CTM, cinq précisions, bloom, émissifs indépendants et
|
||||
interfaces FR/EN restent validés, y compris avec le SSGI actif.
|
||||
|
||||
Commande : `./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true
|
||||
-PsanctuaryPixelShadows122ClientTests=true -PsanctuaryClientNoVsync=true
|
||||
-PsanctuaryQuickTests=true`. Log : `build/shader133-client-full.log`.
|
||||
Le nom de la propriété ciblée `sanctuarySsgi132ClientTests` est conservé.
|
||||
|
||||
Comparateur local autonome : `build/SSGI-beta.133-comparaison.html`, captures
|
||||
natives OFF/35/90 des murs rouge/bleu, sans retouche. Pas de validation
|
||||
Windows/Vulkan ni de mesure de coût FPS revendiquée.
|
||||
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack -x :sanctuary:runGameTest`
|
||||
réussit en **2 min 21 s**, 126 tâches. Le serveur GameTest dédié reste exclu
|
||||
conformément au refus EULA antérieur. Log : `build/shader133-build.log`.
|
||||
Les archives normal/Test contiennent le même JAR ; sources archivées et template
|
||||
de ressources correspondent aux sources. Comparaison avec beta.132 : **5 entrées
|
||||
de production** changées (classe du compositeur, deux shaders, libellés FR/EN).
|
||||
Gameplay, textures, sauvegardes et dépendances inchangés.
|
||||
|
||||
## Artefacts
|
||||
|
||||
- `Sanctuary-beta.133.mrpack` : 10394282 octets, SHA-256 `0c90a070da7f969706abeeec1bdfb0830bb52244c20fccbca4fd876f3fbe0564`.
|
||||
- `Sanctuary-Test-beta.133.mrpack` : 10413204 octets, SHA-256 `e01f0355bf2cce18156fe94a069e142bedc498624ba0b6e2729ae0f23ea73e80`.
|
||||
|
||||
JAR : `a01cae4a98606007b533ca09ec9f072f19a1db2e9b8de466f4ad075d5c1fc185`.
|
||||
+2
-2
@@ -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.130
|
||||
pack_version=beta.130
|
||||
mod_version=beta.133
|
||||
pack_version=beta.133
|
||||
resource_pack_version=beta.121
|
||||
maven_group=fr.koka.sanctuary
|
||||
jei_version=30.32.0-sanctuary.3
|
||||
|
||||
@@ -93,6 +93,10 @@ tasks.named('runGameTest') {
|
||||
}
|
||||
|
||||
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 bloom130Client = providers.gradleProperty('sanctuaryBloom130ClientTests').map { it.toBoolean() }.getOrElse(false)
|
||||
inputs.property('sanctuaryBloom130ClientTests', bloom130Client)
|
||||
if (bloom130Client) filter { line -> line.replace('fr.koka.sanctuary.gametest.SanctuaryClientRenderTests', 'fr.koka.sanctuary.client.shader.Bloom130ClientChecks') }
|
||||
|
||||
+18
-5
@@ -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);});
|
||||
c.runOnClient(m->{preferences();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());
|
||||
@@ -40,6 +40,19 @@ public final class Bloom130ClientChecks implements FabricClientGameTest {
|
||||
c.runOnClient(m->{VanillaLight.setOreEmission(true);VanillaLight.setOreIntensity(100);});frames(c);
|
||||
var source=emission(c);int orePixels=luminous(source);check(orePixels>100,"Actual exposed ore inclusions enter the GPU mask: "+orePixels);
|
||||
var on=capture(c,"bloom-ores-on");double oreGain=gain(off,on,null);check(oreGain>1000,"Ores visibly glow in the native night scene: "+oreGain);
|
||||
c.runOnClient(m->VanillaLight.setBloom(false));frames(c);
|
||||
var coreOnly=capture(c,"emissive-bloom-off");
|
||||
check(gain(off,coreOnly,null)>1000,"Emissive cores survive bloom OFF");
|
||||
check(gain(off,coreOnly,source)<1,"No halo outside source texels with bloom OFF");
|
||||
c.runOnClient(m->check(!Bloom.haloAllocated(),"Bloom OFF frees blur targets and uniforms"));
|
||||
c.runOnClient(m->{VanillaLight.setBloom(true);VanillaLight.setBloomIntensity(0);});frames(c);
|
||||
unchanged(coreOnly,capture(c,"emissive-bloom-zero"),"Zero bloom matches OFF while retaining emissive cores");
|
||||
c.runOnClient(m->check(!Bloom.haloAllocated(),"Zero bloom frees blur resources"));
|
||||
c.runOnClient(m->VanillaLight.setOreEmission(false));c.waitTicks(8);
|
||||
c.runOnClient(m->check(!Bloom.allocated()&&!Bloom.applied(),"Zero halo and disabled cores release everything"));
|
||||
unchanged(off,capture(c,null),"Disabling both restores the original scene");
|
||||
c.runOnClient(m->{VanillaLight.setOreEmission(true);VanillaLight.setBloomIntensity(35);});frames(c);
|
||||
unchanged(on,capture(c,null),"Re-enabling bloom restores its original image");
|
||||
c.runOnClient(m->VanillaLight.setBloomIntensity(10));frames(c);double low=gain(off,capture(c,null),source);
|
||||
c.runOnClient(m->VanillaLight.setBloomIntensity(90));frames(c);double high=gain(off,capture(c,"bloom-strength-90"),source);
|
||||
check(high>Math.max(30,low*2),"Bloom strength changes the halo outside source pixels: low="+low+" high="+high);
|
||||
@@ -83,15 +96,15 @@ public final class Bloom130ClientChecks implements FabricClientGameTest {
|
||||
c.runOnClient(m->{VanillaLight.setBloom(true);VanillaLight.setBloomIntensity(0);});c.waitTicks(6);c.runOnClient(m->check(!Bloom.applied()&&!Bloom.allocated(),"Zero bloom releases GPU resources"));
|
||||
unchanged(coveredOff,capture(c,null),"Zero bloom leaves no residual glow");
|
||||
c.runOnClient(m->{VanillaLight.setBloomIntensity(35);VanillaLight.setBloom(false);VanillaLight.setOreEmission(true);VanillaLight.setOreIntensity(50);});
|
||||
System.out.println("BLOOM130_PASS masks for 20 ore textures; selective night glow; halo intensity/spread; no through-wall glow; live edits; torch/glowstone/lamp/lava/sun; resource reload; defaults/migration; orePixels="+orePixels+" oreGain="+oreGain+" haloLow="+low+" haloHigh="+high+" sunHalo="+sunlight);
|
||||
System.out.println("EMISSIVE131_PASS independent cores with halo OFF/zero; blur resources released; defaults; masks for 20 ore textures; selective night glow; halo intensity/spread; no through-wall glow; live edits; torch/glowstone/lamp/lava/sun; resource reload; defaults/migration; orePixels="+orePixels+" oreGain="+oreGain+" haloLow="+low+" haloHigh="+high+" sunHalo="+sunlight);
|
||||
}
|
||||
private static BlockPos orePos(int i){return new BlockPos(8+i%5,65,8+i/5);}
|
||||
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.bloomIntensity()==35&&VanillaLight.bloomRadius()==50,"Bloom is opt-in with usable defaults");
|
||||
Files.writeString(path,"{\"pixelShadows\":false,\"shadowDistance\":64,\"shadowPixels\":8,\"edgeDistance\":16}");forgetPreferences();
|
||||
check(!VanillaLight.pixelShadows()&&VanillaLight.shadowDistance()==64&&VanillaLight.shadowPixels()==8&&VanillaLight.edgeDistance()==16,"Existing user choices are retained");
|
||||
check(VanillaLight.bloom()&&VanillaLight.oreEmission()&&VanillaLight.edgeHighlights()&&VanillaLight.bloomIntensity()==35&&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");}
|
||||
VanillaLight.setBloomIntensity(35);VanillaLight.setBloomRadius(50);VanillaLight.setOreIntensity(50);VanillaLight.setShadowPixels(16);
|
||||
}catch(java.io.IOException e){throw new AssertionError(e);}}
|
||||
|
||||
+7
-1
@@ -41,6 +41,11 @@ final class EdgeHighlights129ClientChecks {
|
||||
var fine=measure(off,capture(c,"edges-shared-precision-32"));
|
||||
check(coarse.brighter()>low.brighter()*1.4 && fine.brighter()<low.brighter()*.8,
|
||||
"One precision setting controls the visible highlight width: coarse="+coarse+" middle="+low+" fine="+fine);
|
||||
for(int pixels:new int[]{64,128}){
|
||||
c.runOnClient(m -> VanillaLight.setShadowPixels(pixels));frames(c);
|
||||
var finer=measure(off,capture(c,"edges-shared-precision-"+pixels));
|
||||
check(finer.meanGain()<fine.meanGain() && finer.darker()==0,"High precision refines CTM without darkening: "+pixels+" "+finer);
|
||||
}
|
||||
c.runOnClient(m -> VanillaLight.setShadowPixels(16));frames(c);
|
||||
|
||||
for (int distance : new int[]{16,32,64,128,256}) {
|
||||
@@ -139,7 +144,7 @@ final class EdgeHighlights129ClientChecks {
|
||||
// Real prior-version JSON, without either new field. Preserve its existing settings.
|
||||
Files.writeString(path,"{\"enabled\":true,\"saturation\":100,\"pixelShadows\":true,\"shadowPixels\":16,\"shadowDistance\":32}");
|
||||
forgetPreferences();
|
||||
check(!VanillaLight.edgeHighlights() && VanillaLight.edgeIntensity()==30 && VanillaLight.edgeDistance()==32,"Old configuration defaults highlights OFF at intensity 30");
|
||||
check(VanillaLight.edgeHighlights() && VanillaLight.edgeIntensity()==30 && VanillaLight.edgeDistance()==32,"Missing preference uses the new highlight ON default at intensity 30");
|
||||
check(VanillaLight.pixelShadows() && VanillaLight.saturation()==100,"Migration retains existing shader choices");
|
||||
for(int value:new int[]{-100,0,30,100,1000}) {
|
||||
VanillaLight.setEdgeIntensity(value);forgetPreferences();
|
||||
@@ -154,6 +159,7 @@ final class EdgeHighlights129ClientChecks {
|
||||
VanillaLight.setEdgeHighlights(true);forgetPreferences();
|
||||
check(VanillaLight.edgeHighlights(),"Toggle persists independently");
|
||||
VanillaLight.setEdgeHighlights(false);
|
||||
VanillaLight.setBloom(false);VanillaLight.setOreEmission(false);
|
||||
} catch(java.io.IOException e) { throw new AssertionError(e); }
|
||||
}
|
||||
|
||||
|
||||
+13
-8
@@ -36,9 +36,10 @@ 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;
|
||||
private final boolean bloomOnly,ssgiOnly;
|
||||
public PixelShadows122ClientChecks(){this(false);}
|
||||
PixelShadows122ClientChecks(boolean bloomOnly){this.bloomOnly=bloomOnly;}
|
||||
PixelShadows122ClientChecks(boolean bloomOnly){this(bloomOnly,false);}
|
||||
PixelShadows122ClientChecks(boolean bloomOnly,boolean ssgiOnly){this.bloomOnly=bloomOnly;this.ssgiOnly=ssgiOnly;}
|
||||
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);
|
||||
@@ -133,6 +134,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(ssgiOnly){Ssgi132ClientChecks.run(c,server);return;}
|
||||
if(bloomOnly){Bloom130ClientChecks.run(c,server);return;}
|
||||
// Keep VanillaLight enabled for both frames: changing its fog/grade would invalidate this oracle.
|
||||
shadows(c, false);
|
||||
@@ -149,7 +151,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
||||
check(PixelShadows.shadowMap() != null, "Daylight allocates a real shadow target");
|
||||
check(PixelShadows.shadowMap().width == PixelShadows.mapSize(32, 16), "Shadow map follows selected quality");
|
||||
});
|
||||
for (int pixels : new int[]{8, 32, 16}) {
|
||||
for (int pixels : new int[]{8, 32, 64, 128, 16}) {
|
||||
c.runOnClient(m -> VanillaLight.setShadowPixels(pixels));
|
||||
activeFrames(c);
|
||||
c.runOnClient(m -> check(PixelShadows.shadowMap().width == PixelShadows.mapSize(32, pixels),
|
||||
@@ -241,12 +243,13 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
||||
RealtimeShadows129ClientChecks.run(c, server);
|
||||
EdgeHighlights129ClientChecks.run(c, server);
|
||||
Bloom130ClientChecks.run(c, server);
|
||||
Ssgi132ClientChecks.run(c, server);
|
||||
c.getInput().pressKey(InputConstants.KEY_F1);
|
||||
for (String locale : new String[]{"fr_fr", "en_us"}) {
|
||||
c.runOnClient(m -> {
|
||||
m.getLanguageManager().setSelected(locale);
|
||||
m.getLanguageManager().onResourceManagerReload(m.getResourceManager());
|
||||
for (String key : new String[]{"pixel_shadows", "shadow_pixels", "shadow_distance", "edge_highlights", "edge_intensity", "edge_distance", "precision", "bloom", "bloom_radius", "ore_emission"}) {
|
||||
for (String key : new String[]{"pixel_shadows", "shadow_pixels", "shadow_distance", "edge_highlights", "edge_intensity", "edge_distance", "precision", "ssgi", "ssgi_intensity", "bloom", "bloom_radius", "ore_emission"}) {
|
||||
String fullKey = "sanctuary.shaders." + key;
|
||||
check(!Component.translatable(fullKey).getString().equals(fullKey), "Translated " + locale + " " + key);
|
||||
}
|
||||
@@ -258,7 +261,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
||||
});
|
||||
});
|
||||
c.waitTicks(3);
|
||||
c.takeScreenshot("sanctuary-beta130-shader-options-" + locale);
|
||||
c.takeScreenshot("sanctuary-beta133-shader-options-" + locale);
|
||||
}
|
||||
c.runOnClient(m -> m.gui.setScreen(null));
|
||||
}
|
||||
@@ -423,11 +426,13 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
||||
}
|
||||
|
||||
private static void checkPreferences() {
|
||||
VanillaLight.setSsgi(false);
|
||||
VanillaLight.setBloom(false);
|
||||
VanillaLight.setOreEmission(false);
|
||||
VanillaLight.setEnabled(true);
|
||||
VanillaLight.setEdgeHighlights(false);
|
||||
VanillaLight.setSaturation(100);
|
||||
for (int[] pair : new int[][]{{Integer.MIN_VALUE, 8}, {11, 8}, {12, 16}, {16, 16}, {23, 16}, {24, 32}, {Integer.MAX_VALUE, 32}}) {
|
||||
for (int[] pair : new int[][]{{Integer.MIN_VALUE, 8}, {11, 8}, {12, 16}, {16, 16}, {23, 16}, {24, 32}, {47,32}, {48,64}, {64,64}, {95,64}, {96,128}, {128,128}, {Integer.MAX_VALUE,128}}) {
|
||||
VanillaLight.setShadowPixels(pair[0]);
|
||||
forgetPreferences();
|
||||
check(VanillaLight.shadowPixels() == pair[1], "Persisted pixel density rounds/clamps " + pair[0]);
|
||||
@@ -478,7 +483,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
||||
static void look(ClientGameTestContext c, TestServerContext server, float yaw, float pitch) {
|
||||
server.runOnServer(s -> {
|
||||
var p = s.getPlayerList().getPlayers().getFirst();
|
||||
command(s, "tp @a " + p.getX() + " " + p.getY() + " " + p.getZ() + " " + yaw + " " + pitch);
|
||||
command(s, "tp @a " + p.getX() + " " + p.getY() + " " + p.getZ() + String.format(java.util.Locale.ROOT," %.8f %.8f",yaw,pitch));
|
||||
});
|
||||
c.waitFor(m -> Math.abs(m.player.getXRot() - pitch) < .00001
|
||||
&& Math.abs(m.player.getYRot() - yaw) < .00001, 200);
|
||||
@@ -523,7 +528,7 @@ public final class PixelShadows122ClientChecks implements FabricClientGameTest {
|
||||
}
|
||||
|
||||
static Frame capture(ClientGameTestContext c, String name) {
|
||||
if (name != null) c.takeScreenshot("sanctuary-beta130-shader-" + name);
|
||||
if (name != null) c.takeScreenshot("sanctuary-beta133-shader-" + name);
|
||||
var result = new CompletableFuture<Frame>();
|
||||
c.runOnClient(m -> Screenshot.takeScreenshot(m.gameRenderer.mainRenderTarget(), image -> {
|
||||
try (image) {
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package fr.koka.sanctuary.client.shader;
|
||||
|
||||
import static fr.koka.sanctuary.client.shader.PixelShadows122ClientChecks.*;
|
||||
import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest;
|
||||
import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext;
|
||||
import net.fabricmc.fabric.api.client.gametest.v1.context.TestServerContext;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
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);});
|
||||
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());
|
||||
}});
|
||||
camera(c,server,10.5,69,5.5,65);look(c,server,0,40);settle(c);
|
||||
var flatOff=capture(c,"ssgi-flat-off");enable(c,true);unchanged(flatOff,capture(c,"ssgi-flat-on"),"Coplanar ground does not bounce light onto itself");
|
||||
wall(server,12,Blocks.CONCRETE.red().defaultBlockState());settle(c);enable(c,false);var redOff=capture(c,"ssgi-red-off");
|
||||
c.runOnClient(m->VanillaLight.setSsgiIntensity(35));
|
||||
enable(c,true);var defaultRed=measure(redOff,capture(c,"ssgi-red-default-35"));
|
||||
System.out.println("SSGI133_DEFAULT "+defaultRed);
|
||||
check(defaultRed.visible>5000&&defaultRed.strong>500,"Default intensity produces 8/255 gain over 5000 pixels and 16/255 over 500 pixels: "+defaultRed);
|
||||
c.runOnClient(m->VanillaLight.setSsgiIntensity(90));frames(c);
|
||||
var redOn=capture(c,"ssgi-red-on");var red=measure(redOff,redOn);System.out.println("SSGI133_RED "+red);
|
||||
check(red.changed>30&&red.red>red.blue*1.5+20,"Visible red wall tints neutral ground: "+red);
|
||||
unchanged(redOn,capture(c,null),"Stationary SSGI has no animated noise or accumulating feedback");
|
||||
look(c,server,0.00002f,40.00002f);frames(c);unchanged(redOn,capture(c,null),"Subpixel motion keeps actual red bounce stable");
|
||||
look(c,server,5,40);frames(c);capture(c,"ssgi-camera-turned");
|
||||
look(c,server,0,40);frames(c);unchanged(redOn,capture(c,null),"Returning the camera leaves no temporal color trails");
|
||||
c.runOnClient(m->VanillaLight.setSsgiIntensity(20));frames(c);var low=measure(redOff,capture(c,"ssgi-strength-20"));
|
||||
check(red.red>low.red*1.5,"Intensity adjusts native color bounce: "+red+" / "+low);
|
||||
c.runOnClient(m->VanillaLight.setSsgiIntensity(0));c.waitTicks(6);unchanged(redOff,capture(c,null),"Zero intensity restores source image");
|
||||
c.runOnClient(m->check(!ScreenSpaceGI.allocated()&&!ScreenSpaceGI.applied(),"Zero frees SSGI targets"));
|
||||
c.runOnClient(m->VanillaLight.setSsgiIntensity(90));frames(c);
|
||||
int oldWidth=c.computeOnClient(m->m.getWindow().getScreenWidth()),oldHeight=c.computeOnClient(m->m.getWindow().getScreenHeight());
|
||||
c.runOnClient(m->m.getWindow().setWindowed(961,541));
|
||||
c.waitFor(m->m.gameRenderer.mainRenderTarget().width!=redOn.width(),200);frames(c);
|
||||
enable(c,false);var resizedOff=capture(c,null);enable(c,true);var resized=measure(resizedOff,capture(c,"ssgi-resized"));
|
||||
check(resized.changed>30&&resized.red>resized.blue*1.5+20,"Resizing reallocates a working SSGI pass: "+resized);
|
||||
c.runOnClient(m->m.getWindow().setWindowed(oldWidth,oldHeight));
|
||||
c.waitFor(m->m.gameRenderer.mainRenderTarget().width==redOn.width(),200);frames(c);
|
||||
wall(server,12,Blocks.CONCRETE.blue().defaultBlockState());settle(c);enable(c,false);var blueOff=capture(c,"ssgi-blue-off");enable(c,true);var blue=measure(blueOff,capture(c,"ssgi-blue-on"));
|
||||
c.runOnClient(m->VanillaLight.setSsgiIntensity(35));frames(c);
|
||||
var defaultBlue=measure(blueOff,capture(c,"ssgi-blue-default-35"));
|
||||
check(defaultBlue.visible>5000&&defaultBlue.strong>500&&defaultBlue.blue>defaultBlue.red*1.5+20,"Default blue bounce is visible too: "+defaultBlue);
|
||||
c.runOnClient(m->VanillaLight.setSsgiIntensity(90));frames(c);
|
||||
System.out.println("SSGI133_BLUE "+blue+" default="+defaultBlue);check(blue.changed>30&&blue.blue>blue.red*1.5+20,"Live blue wall changes bounced hue: "+blue);
|
||||
wall(server,11,Blocks.CONCRETE.white().defaultBlockState());settle(c);enable(c,false);var coveredOff=capture(c,null);enable(c,true);var covered=capture(c,"ssgi-covered-blue");
|
||||
wall(server,12,Blocks.CONCRETE.red().defaultBlockState());settle(c);unchanged(covered,capture(c,"ssgi-covered-red"),"Hidden wall color cannot pass through foreground wall");
|
||||
var reload=c.computeOnClient(net.minecraft.client.Minecraft::reloadResourcePacks);c.waitFor(m->reload.isDone()&&m.gui.overlay()==null,1200);reload.join();frames(c);unchanged(covered,capture(c,"ssgi-reloaded"),"Resource reload restores SSGI");
|
||||
look(c,server,0.00002f,40.00002f);frames(c);unchanged(covered,capture(c,null),"Subpixel camera movement keeps bounce stable");
|
||||
look(c,server,0,40);frames(c);enable(c,false);unchanged(coveredOff,capture(c,null),"OFF leaves no residual tint");
|
||||
c.runOnClient(m->check(!ScreenSpaceGI.allocated(),"OFF frees SSGI resources"));
|
||||
c.runOnClient(m->{VanillaLight.setSsgiIntensity(35);VanillaLight.setPixelShadows(true);VanillaLight.setEdgeHighlights(true);VanillaLight.setBloom(true);VanillaLight.setOreEmission(true);});
|
||||
enable(c,true);c.waitFor(m->PixelShadows.applied()&&PixelShadows.ready()&&EdgeHighlights.applied()&&Bloom.applied(),1200);
|
||||
capture(c,"ssgi-combined-shader");
|
||||
c.runOnClient(m->VanillaLight.setEnabled(false));c.waitTicks(8);
|
||||
c.runOnClient(m->check(!ScreenSpaceGI.allocated(),"Main shader OFF releases SSGI too"));
|
||||
c.runOnClient(m->VanillaLight.setEnabled(true));frames(c);enable(c,false);
|
||||
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 wall(TestServerContext server,int z,BlockState state){server.runOnServer(s->{for(int x=6;x<=15;x++)for(int y=65;y<=(z==11?72:69);y++)s.overworld().setBlockAndUpdate(new BlockPos(x,y,z),state);});}
|
||||
private static void settle(ClientGameTestContext c){c.waitTicks(30);c.waitFor(m->m.levelRenderer.hasRenderedAllSections(),1200);c.waitTicks(8);}
|
||||
private static void enable(ClientGameTestContext c,boolean enabled){c.runOnClient(m->VanillaLight.setSsgi(enabled));if(enabled)frames(c);else c.waitTicks(8);}
|
||||
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");
|
||||
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);}}
|
||||
private record Gain(int changed,int visible,int strong,double red,double green,double blue){}
|
||||
private static Gain measure(Frame off,Frame on){int count=0,visible=0,strong=0;double r=0,g=0,b=0;for(int i=0;i<off.pixels().length;i++){
|
||||
int a=off.pixels()[i],ar=a>>16&255,ag=a>>8&255,ab=a&255;
|
||||
if(Math.max(ar,Math.max(ag,ab))-Math.min(ar,Math.min(ag,ab))>25||ar<60||ar>245)continue;
|
||||
int z=on.pixels()[i];int dr=(z>>16&255)-ar,dg=(z>>8&255)-ag,db=(z&255)-ab;
|
||||
if(Math.max(dr,Math.max(dg,db))>=16)strong++;
|
||||
if(Math.max(dr,Math.max(dg,db))>=8)visible++;
|
||||
if(Math.max(dr,Math.max(dg,db))>1)count++;r+=Math.max(0,dr);g+=Math.max(0,dg);b+=Math.max(0,db);
|
||||
}return new Gain(count,visible,strong,r,g,b);}
|
||||
}
|
||||
@@ -61,15 +61,21 @@ public final class Bloom {
|
||||
public static void dirtyChunk(int x,int z){TERRAIN.dirtyChunk(x,z);}
|
||||
public static void render(){
|
||||
applied=false;var mc=Minecraft.getInstance();
|
||||
if(!VanillaLight.active()||!VanillaLight.bloom()||VanillaLight.bloomIntensity()==0||mc.level==null){release();return;}
|
||||
boolean halo=VanillaLight.bloom()&&VanillaLight.bloomIntensity()>0;
|
||||
boolean cores=VanillaLight.oreEmission()&&VanillaLight.oreIntensity()>0;
|
||||
if(!VanillaLight.active()||(!halo&&!cores)||mc.level==null){release();return;}
|
||||
if(PanoramaCapture.rendering())return;
|
||||
var state=mc.gameRenderer.gameRenderState().levelRenderState;var camera=state.cameraRenderState;
|
||||
if(camera.fogType!=FogType.NONE)return;
|
||||
var main=mc.gameRenderer.mainRenderTarget();
|
||||
if(emission==null||emission.width!=main.width||emission.height!=main.height){
|
||||
targetsClose();emission=target("emission",main.width,main.height);small=target("bloom low",Math.max(1,main.width/8),Math.max(1,main.height/8));ping=target("bloom ping",small.width,small.height);
|
||||
targetsClose();emission=target("emission",main.width,main.height);
|
||||
}
|
||||
if(scene==null){scene=uniforms("bloom scene",144);horizontal=uniforms("bloom horizontal",16);vertical=uniforms("bloom vertical",16);}
|
||||
if(scene==null)scene=uniforms("bloom scene",144);
|
||||
if(halo){
|
||||
if(small==null){small=target("bloom low",Math.max(1,main.width/8),Math.max(1,main.height/8));ping=target("bloom ping",small.width,small.height);}
|
||||
if(horizontal==null){horizontal=uniforms("bloom horizontal",16);vertical=uniforms("bloom vertical",16);}
|
||||
}else haloClose();
|
||||
var entries=TERRAIN.update();var atlas=TERRAIN.materials.atlas();
|
||||
var vp=new Matrix4f(projection).mul(camera.viewRotationMatrix);var inverse=new Matrix4f(vp).invert();
|
||||
var sun=PixelShadows.sunDirection(state.skyRenderState.sunAngle);var moon=PixelShadows.sunDirection(state.skyRenderState.moonAngle);
|
||||
@@ -81,27 +87,33 @@ public final class Bloom {
|
||||
.putVec4(sun.x,sun.y,sun.z,celestial).putVec4(moon.x,moon.y,moon.z,celestial*.35f)
|
||||
.putVec4(fog.renderDistanceStart,fog.renderDistanceEnd,fog.environmentalStart,fog.environmentalEnd).putVec4(fog.color.w,0,0,0);
|
||||
}
|
||||
float radius=.25f+VanillaLight.bloomRadius()*.0075f;
|
||||
horizontal.rotate();try(var mapped=horizontal.currentBuffer().map(false,true)){Std140Builder.intoBuffer(mapped.data()).putVec4(radius/small.width,0,0,0);}
|
||||
vertical.rotate();try(var mapped=vertical.currentBuffer().map(false,true)){Std140Builder.intoBuffer(mapped.data()).putVec4(0,radius/small.height,0,0);}
|
||||
if(halo){
|
||||
float radius=.25f+VanillaLight.bloomRadius()*.0075f;
|
||||
horizontal.rotate();try(var mapped=horizontal.currentBuffer().map(false,true)){Std140Builder.intoBuffer(mapped.data()).putVec4(radius/small.width,0,0,0);}
|
||||
vertical.rotate();try(var mapped=vertical.currentBuffer().map(false,true)){Std140Builder.intoBuffer(mapped.data()).putVec4(0,radius/small.height,0,0);}
|
||||
}
|
||||
// Gather transforms before opening any pass (the native dynamic ring may resize).
|
||||
var transforms=new ArrayList<GpuBufferSlice>(entries.size());int max=0;
|
||||
for(var entry:entries){var pos=entry.origin();var offset=new Vector3f((float)(pos.getX()-camera.pos.x),(float)(pos.getY()-camera.pos.y),(float)(pos.getZ()-camera.pos.z));
|
||||
transforms.add(RenderSystem.getDynamicUniforms().writeTransform(new Matrix4f(vp).translate(offset),new Vector4f(1,1,1,1),offset,new Matrix4f()));max=Math.max(max,entry.indices());}
|
||||
var indices=RenderSystem.getSequentialBuffer(PrimitiveTopology.QUADS);indices.requestIndexCount(max);
|
||||
var nearest=RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST);
|
||||
screen(SKY,emission.getColorTextureView(),main.getColorTextureView(),true,null);
|
||||
if(halo)screen(SKY,emission.getColorTextureView(),main.getColorTextureView(),true,null);
|
||||
else try(var clear=RenderSystem.getDevice().createCommandEncoder().createRenderPass(()->"Sanctuary clear emission",emission.getColorTextureView(),Optional.of(new Vector4f(0,0,0,0)))){ }
|
||||
|
||||
if(atlas!=null&&!entries.isEmpty())try(var pass=RenderSystem.getDevice().createCommandEncoder().createRenderPass(()->"Sanctuary visible emitters",emission.getColorTextureView(),Optional.empty())){
|
||||
pass.setPipeline(RenderSystem.getCompiledPipeline(EMITTERS));pass.setUniform("BloomScene",scene.currentBuffer());pass.setUniform("WorldDepth",main.getDepthTextureView(),nearest);
|
||||
pass.setUniform("BlockAtlas",mc.getTextureManager().getTexture(TextureAtlas.LOCATION_BLOCKS).getTextureView(),nearest);pass.setUniform("EmissionMasks",atlas.getTextureView(),nearest);
|
||||
pass.setIndexBuffer(indices.getBuffer(),indices.type());
|
||||
for(int i=0;i<entries.size();i++){var entry=entries.get(i);pass.setUniform("DynamicTransforms",transforms.get(i));pass.setVertexBuffer(0,entry.vertices().slice());pass.drawIndexed(entry.indices(),1,0,0,0);}
|
||||
}
|
||||
screen(DOWNSAMPLE,small.getColorTextureView(),emission.getColorTextureView(),false,null);
|
||||
screen(BLUR,ping.getColorTextureView(),small.getColorTextureView(),false,horizontal);
|
||||
screen(BLUR,small.getColorTextureView(),ping.getColorTextureView(),false,vertical);
|
||||
screen(CORE,main.getColorTextureView(),emission.getColorTextureView(),false,null);
|
||||
screen(APPLY,main.getColorTextureView(),small.getColorTextureView(),true,null);
|
||||
if(halo){
|
||||
screen(DOWNSAMPLE,small.getColorTextureView(),emission.getColorTextureView(),false,null);
|
||||
screen(BLUR,ping.getColorTextureView(),small.getColorTextureView(),false,horizontal);
|
||||
screen(BLUR,small.getColorTextureView(),ping.getColorTextureView(),false,vertical);
|
||||
}
|
||||
if(cores)screen(CORE,main.getColorTextureView(),emission.getColorTextureView(),false,null);
|
||||
if(halo)screen(APPLY,main.getColorTextureView(),small.getColorTextureView(),true,null);
|
||||
applied=true;frames++;
|
||||
}
|
||||
private static void screen(RenderPipeline pipeline,GpuTextureView output,GpuTextureView input,boolean useScene,MappableRingBuffer blur){
|
||||
@@ -113,8 +125,13 @@ public final class Bloom {
|
||||
}
|
||||
private static TextureTarget target(String name,int w,int h){return new TextureTarget("Sanctuary "+name,w,h,name.equals("emission")?GpuFormat.RGBA8_UNORM:GpuFormat.RGBA16_FLOAT,null);}
|
||||
private static MappableRingBuffer uniforms(String name,int size){return new MappableRingBuffer(()->"Sanctuary "+name,GpuBuffer.USAGE_UNIFORM|GpuBuffer.USAGE_MAP_WRITE,size);}
|
||||
private static void targetsClose(){if(emission!=null){emission.destroyBuffers();small.destroyBuffers();ping.destroyBuffers();emission=small=ping=null;}}
|
||||
public static void release(){TERRAIN.close();targetsClose();if(scene!=null){scene.close();horizontal.close();vertical.close();scene=horizontal=vertical=null;}applied=false;}
|
||||
private static void haloClose(){
|
||||
if(small!=null){small.destroyBuffers();ping.destroyBuffers();small=ping=null;}
|
||||
if(horizontal!=null){horizontal.close();vertical.close();horizontal=vertical=null;}
|
||||
}
|
||||
private static void targetsClose(){haloClose();if(emission!=null){emission.destroyBuffers();emission=null;}}
|
||||
public static void release(){TERRAIN.close();targetsClose();if(scene!=null){scene.close();scene=null;}applied=false;}
|
||||
static boolean haloAllocated(){return small!=null||horizontal!=null;}
|
||||
static boolean applied(){return applied;}
|
||||
static boolean allocated(){return emission!=null;}
|
||||
static boolean ready(){return TERRAIN.settled();}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package fr.koka.sanctuary.client.shader;
|
||||
|
||||
import com.mojang.blaze3d.buffers.Std140Builder;
|
||||
import com.mojang.blaze3d.pipeline.TextureTarget;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.renderpearl.api.GpuFormat;
|
||||
import com.mojang.renderpearl.api.buffers.GpuBuffer;
|
||||
import com.mojang.renderpearl.api.pipeline.*;
|
||||
import com.mojang.renderpearl.api.textures.FilterMode;
|
||||
import fr.koka.sanctuary.client.panorama.PanoramaCapture;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MappableRingBuffer;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.level.material.FogType;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
/** Bounded, single-bounce diffuse lighting from this frame's visible surfaces. No GPU readback or history. */
|
||||
public final class ScreenSpaceGI {
|
||||
private static final BindGroupLayout LAYOUT=BindGroupLayout.builder()
|
||||
.withUniform("GIParams",UniformType.UNIFORM_BUFFER)
|
||||
.withUniform("WorldDepth",UniformType.COMBINED_IMAGE_SAMPLER)
|
||||
.withUniform("SceneColor",UniformType.COMBINED_IMAGE_SAMPLER)
|
||||
.withUniform("IndirectLight",UniformType.COMBINED_IMAGE_SAMPLER).build();
|
||||
private static RenderPipeline pipeline(String name,boolean composite){
|
||||
return RenderPipelines.register(RenderPipeline.builder().withLocation(Identifier.parse("sanctuary:pipeline/"+name))
|
||||
.withVertexShader("core/screenquad").withFragmentShader(Identifier.parse("sanctuary:core/"+name))
|
||||
.withBindGroupLayout(LAYOUT).withPrimitiveTopology(PrimitiveTopology.TRIANGLES).withCull(false)
|
||||
.withColorTargetState(new ColorTargetState(composite?Optional.of(new BlendFunction(BlendFactor.ONE,BlendFactor.ZERO,BlendFactor.ZERO,BlendFactor.ONE)):Optional.empty(),
|
||||
composite?GpuFormat.RGBA8_UNORM:GpuFormat.RGBA16_FLOAT,composite?ColorTargetState.WRITE_COLOR:ColorTargetState.WRITE_ALL))
|
||||
.withDepthStencilState(Optional.empty()).build());
|
||||
}
|
||||
private static final RenderPipeline GATHER=pipeline("ssgi_gather",false),APPLY=pipeline("ssgi_apply",true);
|
||||
private static final Matrix4f projection=new Matrix4f();
|
||||
private static TextureTarget source,indirect;
|
||||
private static MappableRingBuffer uniforms;
|
||||
private static boolean applied;
|
||||
private static int frames;
|
||||
private ScreenSpaceGI(){}
|
||||
public static void captureProjection(Matrix4f matrix){projection.set(matrix);}
|
||||
public static void render(){
|
||||
applied=false;var mc=Minecraft.getInstance();
|
||||
if(!VanillaLight.active()||!VanillaLight.ssgi()||VanillaLight.ssgiIntensity()==0||mc.level==null){release();return;}
|
||||
if(PanoramaCapture.rendering())return;
|
||||
var camera=mc.gameRenderer.gameRenderState().levelRenderState.cameraRenderState;
|
||||
if(camera.fogType!=FogType.NONE)return;
|
||||
var main=mc.gameRenderer.mainRenderTarget();
|
||||
if(source==null||source.width!=main.width||source.height!=main.height){
|
||||
release();source=new TextureTarget("Sanctuary SSGI source",main.width,main.height,GpuFormat.RGBA8_UNORM,null);
|
||||
indirect=new TextureTarget("Sanctuary SSGI indirect",Math.max(1,(main.width+1)/2),Math.max(1,(main.height+1)/2),GpuFormat.RGBA16_FLOAT,null);
|
||||
}
|
||||
if(uniforms==null)uniforms=new MappableRingBuffer(()->"Sanctuary SSGI",GpuBuffer.USAGE_UNIFORM|GpuBuffer.USAGE_MAP_WRITE,176);
|
||||
var vp=new Matrix4f(projection).mul(camera.viewRotationMatrix);var fog=camera.fogData;
|
||||
uniforms.rotate();try(var mapped=uniforms.currentBuffer().map(false,true)){
|
||||
Std140Builder.intoBuffer(mapped.data()).putMat4f(new Matrix4f(vp).invert()).putMat4f(vp)
|
||||
.putVec4(VanillaLight.ssgiIntensity()/100f,RenderSystem.getDevice().getDeviceInfo().isZZeroToOne()?1:0,2.5f,32)
|
||||
.putVec4(fog.renderDistanceStart,fog.renderDistanceEnd,fog.environmentalStart,fog.environmentalEnd)
|
||||
.putVec4(fog.color.w,0,0,0);
|
||||
}
|
||||
// Snapshot once, before adding indirect light: no feedback, no texture sampled while attached for writing.
|
||||
var encoder=RenderSystem.getDevice().createCommandEncoder();
|
||||
encoder.copyTextureToTexture(main.getColorTexture(),source.getColorTexture(),0,0,0,0,0,main.width,main.height);
|
||||
var nearest=RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST);
|
||||
try(var pass=encoder.createRenderPass(()->"Sanctuary SSGI gather",indirect.getColorTextureView(),Optional.empty())){
|
||||
pass.setPipeline(RenderSystem.getCompiledPipeline(GATHER));pass.setUniform("GIParams",uniforms.currentBuffer());
|
||||
pass.setUniform("WorldDepth",main.getDepthTextureView(),nearest);pass.setUniform("SceneColor",source.getColorTextureView(),nearest);
|
||||
// Unused by gather; bind a separate texture, never its own output.
|
||||
pass.setUniform("IndirectLight",source.getColorTextureView(),nearest);pass.draw(3,1,0,0);
|
||||
}
|
||||
try(var pass=encoder.createRenderPass(()->"Sanctuary SSGI composite",main.getColorTextureView(),Optional.empty())){
|
||||
pass.setPipeline(RenderSystem.getCompiledPipeline(APPLY));pass.setUniform("GIParams",uniforms.currentBuffer());
|
||||
pass.setUniform("WorldDepth",main.getDepthTextureView(),nearest);pass.setUniform("SceneColor",source.getColorTextureView(),nearest);
|
||||
pass.setUniform("IndirectLight",indirect.getColorTextureView(),nearest);pass.draw(3,1,0,0);
|
||||
}
|
||||
applied=true;frames++;
|
||||
}
|
||||
public static void release(){if(source!=null){source.destroyBuffers();indirect.destroyBuffers();source=indirect=null;}if(uniforms!=null){uniforms.close();uniforms=null;}applied=false;}
|
||||
static boolean applied(){return applied;}
|
||||
static boolean allocated(){return source!=null;}
|
||||
static int renderedFrames(){return frames;}
|
||||
}
|
||||
+4
-1
@@ -19,7 +19,10 @@ public final class ShaderOptionsScreen extends OptionsSubScreen {
|
||||
new OptionInstance.IntRange(0,200),VanillaLight.saturation(),VanillaLight::setSaturation));
|
||||
list.addBig(new OptionInstance<Integer>("sanctuary.shaders.precision",OptionInstance.cachedConstantTooltip(
|
||||
Component.translatable("sanctuary.shaders.precision_help")),(caption,value)->Component.translatable("sanctuary.shaders.precision_value",value),
|
||||
new OptionInstance.Enum<>(List.of(8,16,32),Codec.INT),VanillaLight.shadowPixels(),VanillaLight::setShadowPixels));
|
||||
new OptionInstance.Enum<>(List.of(8,16,32,64,128),Codec.INT),VanillaLight.shadowPixels(),VanillaLight::setShadowPixels));
|
||||
list.addBig(OptionInstance.createBoolean("sanctuary.shaders.ssgi",OptionInstance.cachedConstantTooltip(
|
||||
Component.translatable("sanctuary.shaders.ssgi_help")),VanillaLight.ssgi(),VanillaLight::setSsgi));
|
||||
slider("ssgi_intensity",VanillaLight.ssgiIntensity(),VanillaLight::setSsgiIntensity);
|
||||
list.addBig(OptionInstance.createBoolean("sanctuary.shaders.bloom",OptionInstance.cachedConstantTooltip(
|
||||
Component.translatable("sanctuary.shaders.bloom_help")),VanillaLight.bloom(),VanillaLight::setBloom));
|
||||
slider("bloom_intensity",VanillaLight.bloomIntensity(),VanillaLight::setBloomIntensity);
|
||||
|
||||
@@ -18,12 +18,14 @@ public final class VanillaLight {
|
||||
boolean enabled=true;
|
||||
int saturation=142;
|
||||
boolean pixelShadows=true;
|
||||
boolean bloom=false;
|
||||
boolean bloom=true;
|
||||
boolean ssgi=false;
|
||||
int ssgiIntensity=35;
|
||||
int bloomIntensity=35;
|
||||
int bloomRadius=50;
|
||||
boolean oreEmission=true;
|
||||
int oreIntensity=50;
|
||||
boolean edgeHighlights=false;
|
||||
boolean edgeHighlights=true;
|
||||
int edgeIntensity=30;
|
||||
int edgeDistance=32;
|
||||
int shadowPixels=16;
|
||||
@@ -35,13 +37,14 @@ public final class VanillaLight {
|
||||
try{if(Files.isRegularFile(path()))preferences=new GsonBuilder().create().fromJson(Files.readString(path()),Preferences.class);}
|
||||
catch(Exception e){SanctuaryMod.LOGGER.warn("Cannot read Sanctuary shader preference",e);}
|
||||
if(preferences==null)preferences=new Preferences();
|
||||
preferences.ssgiIntensity=Math.clamp(preferences.ssgiIntensity,0,100);
|
||||
preferences.bloomIntensity=Math.clamp(preferences.bloomIntensity,0,100);
|
||||
preferences.bloomRadius=Math.clamp(preferences.bloomRadius,0,100);
|
||||
preferences.oreIntensity=Math.clamp(preferences.oreIntensity,0,100);
|
||||
preferences.edgeIntensity=Math.clamp(preferences.edgeIntensity,0,100);
|
||||
preferences.edgeDistance=normalizeShadowDistance(preferences.edgeDistance);
|
||||
preferences.saturation=Math.clamp(preferences.saturation,0,200);
|
||||
preferences.shadowPixels=normalizeShadowValue(preferences.shadowPixels,8,16,32);
|
||||
preferences.shadowPixels=normalizePrecision(preferences.shadowPixels);
|
||||
preferences.shadowDistance=normalizeShadowDistance(preferences.shadowDistance);
|
||||
}
|
||||
return preferences.enabled;
|
||||
@@ -55,6 +58,10 @@ public final class VanillaLight {
|
||||
public static int edgeIntensity(){enabled();return preferences.edgeIntensity;}
|
||||
public static void setEdgeHighlights(boolean value){enabled();preferences.edgeHighlights=value;save();}
|
||||
public static void setEdgeIntensity(int value){enabled();preferences.edgeIntensity=Math.clamp(value,0,100);save();}
|
||||
public static boolean ssgi(){enabled();return preferences.ssgi;}
|
||||
public static int ssgiIntensity(){enabled();return preferences.ssgiIntensity;}
|
||||
public static void setSsgi(boolean value){enabled();preferences.ssgi=value;save();}
|
||||
public static void setSsgiIntensity(int value){enabled();preferences.ssgiIntensity=Math.clamp(value,0,100);save();}
|
||||
public static boolean bloom(){enabled();return preferences.bloom;}
|
||||
public static int bloomIntensity(){enabled();return preferences.bloomIntensity;}
|
||||
public static int bloomRadius(){enabled();return preferences.bloomRadius;}
|
||||
@@ -70,16 +77,18 @@ public final class VanillaLight {
|
||||
public static int shadowDistance(){enabled();return preferences.shadowDistance;}
|
||||
public static void setSaturation(int value){enabled();preferences.saturation=Math.clamp(value,0,200);save();}
|
||||
public static void setPixelShadows(boolean value){enabled();preferences.pixelShadows=value;save();}
|
||||
public static void setShadowPixels(int value){enabled();preferences.shadowPixels=normalizeShadowValue(value,8,16,32);save();}
|
||||
public static void setShadowPixels(int value){enabled();preferences.shadowPixels=normalizePrecision(value);save();}
|
||||
public static void setShadowDistance(int value){enabled();preferences.shadowDistance=normalizeShadowDistance(value);save();}
|
||||
public static void setEnabled(boolean value){
|
||||
enabled();preferences.enabled=value;
|
||||
save();
|
||||
}
|
||||
/** Nearest supported value, clamped at either end; midpoint ties round up. */
|
||||
private static int normalizeShadowValue(int value,int low,int middle,int high){
|
||||
if(value<(low+middle)/2)return low;
|
||||
return value<(middle+high)/2?middle:high;
|
||||
private static int normalizePrecision(int value){
|
||||
if(value<12)return 8;
|
||||
if(value<24)return 16;
|
||||
if(value<48)return 32;
|
||||
return value<96?64:128;
|
||||
}
|
||||
private static int normalizeShadowDistance(int value){
|
||||
if(value<24)return 16;
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package fr.koka.sanctuary.mixin.client;
|
||||
|
||||
import fr.koka.sanctuary.client.shader.PixelShadows;
|
||||
import fr.koka.sanctuary.client.shader.Bloom;
|
||||
import fr.koka.sanctuary.client.shader.ScreenSpaceGI;
|
||||
import net.minecraft.client.renderer.extract.LevelExtractor;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -17,5 +18,5 @@ abstract class PixelShadowDirtyMixin {
|
||||
PixelShadows.dirtyBlock(pos); Bloom.dirtyBlock(pos);
|
||||
}
|
||||
@Inject(method="allChanged", at=@At("HEAD"))
|
||||
private void sanctuary$reload(CallbackInfo ci) { PixelShadows.release(); Bloom.release(); }
|
||||
private void sanctuary$reload(CallbackInfo ci) { PixelShadows.release(); Bloom.release(); ScreenSpaceGI.release(); }
|
||||
}
|
||||
|
||||
+4
-2
@@ -6,6 +6,7 @@ import com.mojang.renderpearl.api.buffers.GpuBufferSlice;
|
||||
import fr.koka.sanctuary.client.shader.PixelShadows;
|
||||
import fr.koka.sanctuary.client.shader.EdgeHighlights;
|
||||
import fr.koka.sanctuary.client.shader.Bloom;
|
||||
import fr.koka.sanctuary.client.shader.ScreenSpaceGI;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.ProjectionMatrixBuffer;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -21,10 +22,11 @@ abstract class PixelShadowRendererMixin {
|
||||
PixelShadows.captureProjection(matrix);
|
||||
EdgeHighlights.captureProjection(matrix);
|
||||
Bloom.captureProjection(matrix);
|
||||
ScreenSpaceGI.captureProjection(matrix);
|
||||
return original.call(buffer, matrix);
|
||||
}
|
||||
@Inject(method="renderLevel", at=@At(value="INVOKE", target="Lnet/minecraft/client/renderer/GameRenderer;render3dHud(Lnet/minecraft/client/renderer/state/level/CameraRenderState;Lnet/minecraft/client/renderer/state/level/PlayerRenderState;Lnet/minecraft/client/renderer/state/OptionsRenderState;Z)V"))
|
||||
private void sanctuary$shadows(CallbackInfo ci) { PixelShadows.render(); EdgeHighlights.render(); Bloom.render(); }
|
||||
private void sanctuary$shadows(CallbackInfo ci) { PixelShadows.render(); ScreenSpaceGI.render(); EdgeHighlights.render(); Bloom.render(); }
|
||||
@Inject(method={"close", "setLevel"}, at=@At("HEAD"))
|
||||
private void sanctuary$close(CallbackInfo ci) { PixelShadows.release(); EdgeHighlights.release(); Bloom.release(); }
|
||||
private void sanctuary$close(CallbackInfo ci) { PixelShadows.release(); EdgeHighlights.release(); Bloom.release(); ScreenSpaceGI.release(); }
|
||||
}
|
||||
|
||||
@@ -2258,7 +2258,7 @@
|
||||
"sanctuary.discovery.granted": "Discoveries added for %s: %s blocks, %s items, %s species. Linked recipes updated; reopen catalogues.",
|
||||
"sanctuary.discovery.invalid": "Choose a discoverable block, item or species.",
|
||||
"sanctuary.shaders.edge_highlights": "Edge highlights",
|
||||
"sanctuary.shaders.edge_highlights_help": "Connected highlights along terrain contours. Flat surfaces connect, even across different blocks. Preserves textures. OFF by default; requires Vanilla Light.",
|
||||
"sanctuary.shaders.edge_highlights_help": "Connected highlights along terrain contours. Flat surfaces connect, even across different blocks. Preserves textures. ON by default; requires Vanilla Light.",
|
||||
"sanctuary.shaders.edge_intensity": "Edge intensity",
|
||||
"sanctuary.shaders.edge_intensity_help": "Highlight strength along edges. Independent of cast shadows.",
|
||||
"sanctuary.shaders.edge_intensity_value": "Edge intensity: %s %%",
|
||||
@@ -2266,7 +2266,7 @@
|
||||
"sanctuary.shaders.pixel_shadows_help": "Sun shadows and subtle moon shadows on a pixel grid. Dappled foliage and stable contours. Enabled by default at 128 blocks; requires Vanilla Light.",
|
||||
"sanctuary.shaders.shadow_pixels": "Shadow precision",
|
||||
"sanctuary.shaders.shadow_pixels_value": "%s pixels/block",
|
||||
"sanctuary.shaders.shadow_pixels_help": "Grid precision: 8, 16 or 32 pixels per block. Higher values make shadow pixels finer. Requires Vanilla Light and pixel shadows.",
|
||||
"sanctuary.shaders.shadow_pixels_help": "Grid precision: 8, 16, 32, 64 or 128 pixels per block. Higher values make shadow pixels finer. Requires Vanilla Light and pixel shadows.",
|
||||
"sanctuary.shaders.shadow_distance": "Shadow distance",
|
||||
"sanctuary.shaders.shadow_distance_value": "%s blocks",
|
||||
"sanctuary.shaders.shadow_distance_help": "Shadow range: 16, 32, 64, 128 or 256 blocks, within loaded terrain. Higher distances cost more performance. Requires Vanilla Light and pixel shadows.",
|
||||
@@ -2274,19 +2274,24 @@
|
||||
"sanctuary.shaders.edge_distance_value": "%s blocks",
|
||||
"sanctuary.shaders.edge_distance_help": "Independent range: 16, 32, 64, 128 or 256 blocks. Limited to visible terrain and render distance. Distant contours become naturally finer.",
|
||||
"sanctuary.shaders.precision": "Precision",
|
||||
"sanctuary.shaders.precision_help": "Shared grid for shadows and edge highlights: 8, 16 or 32 pixels per block.",
|
||||
"sanctuary.shaders.precision_help": "Shared grid for shadows and edge highlights: 8, 16, 32, 64 or 128 pixels per block. The shadow map retains its resolution cap.",
|
||||
"sanctuary.shaders.precision_value": "%s pixels/block",
|
||||
"sanctuary.shaders.bloom": "Bloom",
|
||||
"sanctuary.shaders.bloom_help": "Glow around the sun and luminous surfaces. Ores can also become emissive. Requires Vanilla Light. OFF by default.",
|
||||
"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 disables bloom and frees its resources.",
|
||||
"sanctuary.shaders.bloom_intensity_help": "Halo strength. 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.",
|
||||
"sanctuary.shaders.bloom_radius_value": "Spread: %s%%",
|
||||
"sanctuary.shaders.ore_emission": "Emissive ores",
|
||||
"sanctuary.shaders.ore_emission_help": "With bloom: makes ore inclusions glow while preserving their host rock. Visual effect only.",
|
||||
"sanctuary.shaders.ore_emission_help": "Makes ore inclusions glow while preserving their host rock, even with bloom off or at zero. ON by default. Visual effect only.",
|
||||
"sanctuary.shaders.ore_intensity": "Ore emission",
|
||||
"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_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_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%%"
|
||||
}
|
||||
|
||||
@@ -2258,7 +2258,7 @@
|
||||
"sanctuary.discovery.granted": "Découvertes ajoutées pour %s : %s blocs, %s objets, %s espèces. Recettes liées actualisées ; rouvre les catalogues.",
|
||||
"sanctuary.discovery.invalid": "Choisis un bloc, un objet ou une espèce découvrable.",
|
||||
"sanctuary.shaders.edge_highlights": "Reflets sur les arêtes",
|
||||
"sanctuary.shaders.edge_highlights_help": "Reflets connectés sur les contours du relief. Les surfaces plates se raccordent, même entre blocs différents. Préserve les textures. Désactivé par défaut ; nécessite Vanilla Light.",
|
||||
"sanctuary.shaders.edge_highlights_help": "Reflets connectés sur les contours du relief. Les surfaces plates se raccordent, même entre blocs différents. Préserve les textures. Activé par défaut ; nécessite Vanilla Light.",
|
||||
"sanctuary.shaders.edge_intensity": "Intensité des arêtes",
|
||||
"sanctuary.shaders.edge_intensity_help": "Force du reflet sur les arêtes. Le réglage est indépendant des ombres portées.",
|
||||
"sanctuary.shaders.edge_intensity_value": "Intensité des arêtes : %s %%",
|
||||
@@ -2266,7 +2266,7 @@
|
||||
"sanctuary.shaders.pixel_shadows_help": "Ombres du soleil et ombres légères de la lune, sur une grille de pixels. Feuillage irrégulier et contours stables. Activées par défaut, portée 128 blocs ; nécessite Vanilla Light.",
|
||||
"sanctuary.shaders.shadow_pixels": "Précision des ombres",
|
||||
"sanctuary.shaders.shadow_pixels_value": "%s pixels/bloc",
|
||||
"sanctuary.shaders.shadow_pixels_help": "Précision de la grille : 8, 16 ou 32 pixels par bloc. Plus la valeur est élevée, plus les pixels des ombres sont fins. Nécessite Vanilla Light et les ombres pixelisées.",
|
||||
"sanctuary.shaders.shadow_pixels_help": "Précision de la grille : 8, 16, 32, 64 ou 128 pixels par bloc. Plus la valeur est élevée, plus les pixels des ombres sont fins. Nécessite Vanilla Light et les ombres pixelisées.",
|
||||
"sanctuary.shaders.shadow_distance": "Distance des ombres",
|
||||
"sanctuary.shaders.shadow_distance_value": "%s blocs",
|
||||
"sanctuary.shaders.shadow_distance_help": "Portée des ombres : 16, 32, 64, 128 ou 256 blocs, dans le terrain chargé. Une distance élevée coûte davantage en performances. Nécessite Vanilla Light et les ombres pixelisées.",
|
||||
@@ -2274,19 +2274,24 @@
|
||||
"sanctuary.shaders.edge_distance_value": "%s blocs",
|
||||
"sanctuary.shaders.edge_distance_help": "Portée indépendante : 16, 32, 64, 128 ou 256 blocs. Limitée au terrain visible et à la distance de rendu. Les contours lointains s’affinent naturellement.",
|
||||
"sanctuary.shaders.precision": "Précision",
|
||||
"sanctuary.shaders.precision_help": "Grille commune aux ombres et aux reflets sur les arêtes : 8, 16 ou 32 pixels par bloc.",
|
||||
"sanctuary.shaders.precision_help": "Grille commune aux ombres et aux reflets sur les arêtes : 8, 16, 32, 64 ou 128 pixels par bloc. La carte d’ombres conserve sa résolution maximale.",
|
||||
"sanctuary.shaders.precision_value": "%s pixels/bloc",
|
||||
"sanctuary.shaders.bloom": "Bloom",
|
||||
"sanctuary.shaders.bloom_help": "Halo du soleil et des surfaces lumineuses. Les minerais peuvent aussi devenir émissifs. Nécessite Vanilla Light. Désactivé par défaut.",
|
||||
"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 désactive le bloom et libère ses ressources.",
|
||||
"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_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.",
|
||||
"sanctuary.shaders.bloom_radius_value": "Diffusion : %s %%",
|
||||
"sanctuary.shaders.ore_emission": "Minerais émissifs",
|
||||
"sanctuary.shaders.ore_emission_help": "Avec le bloom : fait briller les inclusions des minerais sans éclairer leur roche. Effet visuel uniquement.",
|
||||
"sanctuary.shaders.ore_emission_help": "Fait briller les inclusions des minerais sans éclairer leur roche, même avec le bloom coupé ou à zéro. Activé par défaut. Effet visuel uniquement.",
|
||||
"sanctuary.shaders.ore_intensity": "Émission des minerais",
|
||||
"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_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_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 %%"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : require
|
||||
#include <sanctuary:ssgi.glsl>
|
||||
layout(location=0) in vec2 texCoord;
|
||||
layout(location=0) out vec4 fragColor;
|
||||
void main(){
|
||||
float depth=texture(WorldDepth,texCoord).r;
|
||||
if(depth<=0.0)discard;
|
||||
vec3 p=giPosition(texCoord,depth),normal=giNormal(texCoord,p);
|
||||
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++){
|
||||
vec2 uv=(base+vec2(x,y)+0.5)/size;
|
||||
vec4 sampleLight=texture(IndirectLight,uv);
|
||||
if(sampleLight.a<=0.0)continue;
|
||||
vec2 surfaceUV=giTexel(uv);vec3 q=giPosition(surfaceUV,texture(WorldDepth,surfaceUV).r);
|
||||
float plane=abs(dot(q-p,normal));
|
||||
// Bilateral reconstruction never blends a wall's light into the foreground silhouette.
|
||||
if(plane>0.06||length(q-p)>0.5)continue;
|
||||
float weight=(x==0?1.0-f.x:f.x)*(y==0?1.0-f.y:f.y)*(1.0-smoothstep(0.01,0.06,plane));
|
||||
bounce+=sampleLight.rgb*weight;total+=weight;
|
||||
}
|
||||
if(total<=0.0001)discard;
|
||||
if(max(bounce.r,max(bounce.g,bounce.b))<=0.0)discard;
|
||||
vec3 scene=texture(SceneColor,texCoord).rgb;
|
||||
vec3 linearScene=scene*scene;
|
||||
// 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 reflected=linearScene*irradiance;
|
||||
vec3 lit=linearScene+(1.0-linearScene)*(1.0-exp(-reflected));
|
||||
fragColor=vec4(sqrt(clamp(lit,0.0,1.0)),0.0);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : require
|
||||
#include <sanctuary:ssgi.glsl>
|
||||
layout(location=0) in vec2 texCoord;
|
||||
layout(location=0) out vec4 fragColor;
|
||||
void main(){
|
||||
fragColor=vec4(0.0);
|
||||
vec2 uv=giTexel(texCoord);float depth=texture(WorldDepth,uv).r;
|
||||
if(depth<=0.0)return;
|
||||
vec3 p=giPosition(uv,depth);float distance=length(p);
|
||||
if(distance>=GISettings.w)return;
|
||||
vec3 n=giNormal(uv,p);
|
||||
if(length(n)<0.9)return;
|
||||
vec3 tangent=normalize(cross(n,abs(n.y)<0.9?vec3(0,1,0):vec3(0,0,1)));
|
||||
vec3 bitangent=cross(n,tangent),sum=vec3(0.0);
|
||||
// Fixed hemisphere directions; no per-frame stochastic noise or temporal history.
|
||||
for(int ray=0;ray<12;ray++){
|
||||
float angle=6.2831853*(float(ray<8?ray:ray-8)+0.5)/float(ray<8?8:4);
|
||||
float elevation=ray<8?0.4:0.85;
|
||||
vec3 direction=n*elevation+(tangent*cos(angle)+bitangent*sin(angle))*sqrt(1.0-elevation*elevation);
|
||||
vec3 start=p+n*0.045;
|
||||
for(int step=1;step<=10;step++){
|
||||
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;
|
||||
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;
|
||||
if(hitDepth<=0.0)continue;
|
||||
vec3 hit=giPosition(hitUV,hitDepth);float behind=length(probe)-length(hit);
|
||||
if(behind<=0.015)continue;
|
||||
// First occluder terminates the ray even when too thick: never continue behind a wall.
|
||||
vec3 delta=hit-p;float separation=length(delta);
|
||||
if(behind<0.35&&separation>0.08&&separation<GISettings.z&&dot(delta,n)>0.03){
|
||||
vec3 hitNormal=giNormal(hitUV,hit);
|
||||
float facing=max(0.0,dot(hitNormal,-normalize(delta)));
|
||||
float edge=smoothstep(0.0,0.06,min(min(hitUV.x,hitUV.y),min(1.0-hitUV.x,1.0-hitUV.y)));
|
||||
vec3 color=texture(SceneColor,hitUV).rgb;
|
||||
float falloff=1.0-separation/GISettings.z;
|
||||
// Decode the scene approximately to linear light; one spatial falloff is enough.
|
||||
sum+=color*color*facing*falloff*edge*giFog(hit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fragColor=vec4(sum/12.0*giFog(p)*(1.0-smoothstep(GISettings.w*0.75,GISettings.w,distance)),distance);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
uniform sampler2D WorldDepth;
|
||||
uniform sampler2D SceneColor;
|
||||
uniform sampler2D IndirectLight;
|
||||
layout(std140) uniform GIParams {
|
||||
mat4 InverseViewProjection;
|
||||
mat4 ViewProjection;
|
||||
vec4 GISettings; // strength, zero-to-one clip, local radius, view range
|
||||
vec4 GIFog;
|
||||
vec4 GIEnvironment;
|
||||
};
|
||||
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;}
|
||||
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)<length(p-l)?r-p:p-l;
|
||||
vec3 dy=length(u-p)<length(p-d)?u-p:p-d;
|
||||
vec3 n=cross(dx,dy);n/=max(length(n),0.00001);
|
||||
return dot(n,-p)<0.0?-n:n;
|
||||
}
|
||||
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);
|
||||
return 1.0-max(a,b)*GIEnvironment.x;
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
name = "Sanctuary"
|
||||
author = "KOKA99CAB"
|
||||
version = "beta.130"
|
||||
version = "beta.133"
|
||||
pack-format = "packwiz:1.1.0"
|
||||
|
||||
[index]
|
||||
|
||||
Reference in New Issue
Block a user