[{"data":1,"prerenderedAt":468},["ShallowReactive",2],{"cases":3},[4,191,320],{"id":5,"title":6,"body":7,"description":178,"extension":179,"meta":180,"navigation":186,"path":187,"seo":188,"stem":189,"__hash__":190},"content\u002Fcases\u002Fhero-shader-nebuleuse.md","Hero shader — nébuleuse mouse-reactive",{"type":8,"value":9,"toc":171},"minimark",[10,14,18,24,27,32,55,58,89,93,149,153],[11,12,6],"h1",{"id":13},"hero-shader-nébuleuse-mouse-reactive",[15,16,17],"p",{},"Un fond plein écran pensé pour un hero de site studio. Une nébuleuse bleue\u002Fviolette respire doucement grâce à un bruit de Simplex auto-warpé, et le curseur tire la texture vers lui avec une légère aberration chromatique.",[19,20,21],"shader-frame",{},[22,23],"hero-shader",{},[15,25,26],{},"Bouge la souris sur le cadre ci-dessus.",[28,29,31],"h2",{"id":30},"démarche","Démarche",[15,33,34,35,39,40,44,45,44,48,44,51,54],{},"Un ",[36,37,38],"strong",{},"fragment shader"," rend la totalité de la scène — pas de géométrie 3D, juste un seul triangle plein écran avec OGL (",[41,42,43],"code",{},"Renderer",", ",[41,46,47],{},"Program",[41,49,50],{},"Mesh",[41,52,53],{},"Triangle","). Toute la richesse vient du shader.",[15,56,57],{},"Trois couches superposées :",[59,60,61,73,83],"ul",{},[62,63,64,67,68,72],"li",{},[36,65,66],{},"Nébuleuse flowing"," — fBM (5 octaves) ",[69,70,71],"em",{},"auto-warpé"," : on utilise la sortie du bruit comme coordonnée d'entrée du bruit suivant. Résultat : des courants fluides qui s'enroulent sur eux-mêmes plutôt qu'une texture statique qui scrolle.",[62,74,75,78,79,82],{},[36,76,77],{},"Déformation locale souris"," — ",[41,80,81],{},"smoothstep(0.55, 0.0, d)"," donne une influence radiale lisse autour du curseur, qui tire les UVs vers le pointeur comme une lentille.",[62,84,85,88],{},[36,86,87],{},"Chromatic aberration"," — trois échantillonnages du champ à des offsets opposés en X, un par canal RGB. Subtil au repos, prononcé près de la souris.",[28,90,92],{"id":91},"détails-techniques","Détails techniques",[59,94,95,105,115,128,140],{},[62,96,97,100,101,104],{},[36,98,99],{},"Palette"," — 4 arrêts : violet profond → violet → bleu électrique → lavande. Interpolés avec ",[41,102,103],{},"smoothstep"," sur des plages qui se recouvrent, pour un dégradé qui ne « marche » pas.",[62,106,107,110,111,114],{},[36,108,109],{},"Lissage de la souris"," — exponential smoothing indépendant du framerate (",[41,112,113],{},"1 - exp(-dt * 6)","). Le retard de ~100 ms donne cette sensation « lourde » et premium.",[62,116,117,78,120,123,124,127],{},[36,118,119],{},"Pause économe",[41,121,122],{},"IntersectionObserver"," arrête le RAF quand le canvas sort du viewport. Idem quand l'onglet est caché (",[41,125,126],{},"document.hidden",").",[62,129,130,78,136,139],{},[36,131,132,133],{},"Respect ",[41,134,135],{},"prefers-reduced-motion",[41,137,138],{},"uInfluence"," passe à 0, la souris ne déforme plus, la nébuleuse continue à flotter doucement.",[62,141,142,78,145,148],{},[36,143,144],{},"Nettoyage GL",[41,146,147],{},"WEBGL_lose_context.loseContext()"," à l'unmount pour libérer le contexte proprement.",[28,150,152],{"id":151},"pile","Pile",[59,154,155,165,168],{},[62,156,157,164],{},[158,159,163],"a",{"href":160,"rel":161},"https:\u002F\u002Fgithub.com\u002Foframe\u002Fogl",[162],"nofollow","OGL"," — ~10 kB gzippé, API minimale, parfait pour un shader plein écran sans Three.js",[62,166,167],{},"WebGL 1 (GLSL ES 1.0) — compatible mobile, pas de dépendance à WebGL 2",[62,169,170],{},"Vue 3 + TypeScript strict",{"title":172,"searchDepth":173,"depth":173,"links":174},"",2,[175,176,177],{"id":30,"depth":173,"text":31},{"id":91,"depth":173,"text":92},{"id":151,"depth":173,"text":152},"Fond plein écran en WebGL, gradient bleu\u002Fviolet animé par bruit auto-warpé, distordu autour du curseur avec aberration chromatique.","md",{"tags":181},[182,183,184,185],"webgl","glsl","ogl","shader",true,"\u002Fcases\u002Fhero-shader-nebuleuse",{"title":6,"description":178},"cases\u002Fhero-shader-nebuleuse","HeufJlH9Uj3Z7qDSzjNvmpChvr9-mClYzzTnc2hlWxE",{"id":192,"title":193,"body":194,"description":312,"extension":179,"meta":313,"navigation":186,"path":316,"seo":317,"stem":318,"__hash__":319},"content\u002Fcases\u002Fhero-shader-reveal.md","Hero shader — reveal par traînée",{"type":8,"value":195,"toc":307},[196,199,202,208,211,213,220,243,246,248,288,290],[11,197,193],{"id":198},"hero-shader-reveal-par-traînée",[15,200,201],{},"Au repos, un fond blanc homogène. Dès que la souris bouge, elle « gratte » le blanc pour révéler un paysage coloré caché dessous. La trace s'efface lentement quand on arrête.",[19,203,205],{"bg":204},"#f5f4ef",[206,207],"hero-reveal",{},[15,209,210],{},"Bouge la souris sur le cadre.",[28,212,31],{"id":30},[15,214,215,216,219],{},"Deux passes de rendu par frame, orchestrées par un ",[36,217,218],{},"ping-pong de render targets"," :",[221,222,223,233],"ol",{},[62,224,225,228,229,232],{},[36,226,227],{},"Trail pass"," — lit l'ancienne traînée (texture 1024×1024, canal R), la multiplie par ",[41,230,231],{},"uDecay = 0.98"," (lingering ~1 s), et peint un segment doux de la position précédente de la souris à la position actuelle. Le résultat devient la nouvelle texture de traînée.",[62,234,235,238,239,242],{},[36,236,237],{},"Display pass"," — lit la traînée fraîche, calcule la nébuleuse cachée (même shader que le hero bleu\u002Fviolet), et ",[41,240,241],{},"mix(base, hidden, reveal)"," pour composer l'image finale.",[15,244,245],{},"Le ping-pong (deux RenderTargets alternés) est nécessaire parce qu'on ne peut pas lire et écrire la même texture dans la même passe.",[28,247,92],{"id":91},[59,249,250,260,270,276,282],{},[62,251,252,255,256,259],{},[36,253,254],{},"Segment, pas point"," — la traînée peint un segment ",[41,257,258],{},"mousePrev → mouse",", pas juste un disque à la position actuelle. Quand la souris bouge vite, pas de trous dans le trait.",[62,261,262,265,266,269],{},[36,263,264],{},"Décollage propre"," — flag ",[41,267,268],{},"interacted"," : tant que la souris n'a pas bougé, aucun paint. Le premier mouvement snap les positions pour qu'aucune ligne ne soit tirée du centre.",[62,271,272,275],{},[36,273,274],{},"Edge halo"," dans le display — léger assombrissement au bord du reveal pour un effet « épaisseur » \u002F 3D plutôt que mask net.",[62,277,278,281],{},[36,279,280],{},"Grain conditionnel"," — le grain de film ne s'applique qu'aux zones révélées. La base blanche reste pristine.",[62,283,284,287],{},[36,285,286],{},"Correction d'aspect"," dans la trail pass — sinon le pinceau devient une ellipse horizontale sur les écrans larges.",[28,289,152],{"id":151},[15,291,292,78,295,44,297,44,299,44,301,44,303,306],{},[158,293,163],{"href":160,"rel":294},[162],[41,296,43],{},[41,298,47],{},[41,300,50],{},[41,302,53],{},[41,304,305],{},"RenderTarget",". Deux programmes shaders, un seul triangle plein écran, deux RenderTargets ping-pong. Pas de dépendance 3D ni de framework de postprocessing.",{"title":172,"searchDepth":173,"depth":173,"links":308},[309,310,311],{"id":30,"depth":173,"text":31},{"id":91,"depth":173,"text":92},{"id":151,"depth":173,"text":152},"Base blanche homogène, la souris révèle progressivement une nébuleuse cachée dessous via une traînée qui s'estompe.",{"tags":314},[182,183,184,315,185],"feedback-texture","\u002Fcases\u002Fhero-shader-reveal",{"title":193,"description":312},"cases\u002Fhero-shader-reveal","6v46yuqYz4BhYiY6DhDo3QrO2OTvwwuZv9BTSNswYIw",{"id":321,"title":322,"body":323,"description":460,"extension":179,"meta":461,"navigation":186,"path":464,"seo":465,"stem":466,"__hash__":467},"content\u002Fcases\u002Fshader-image-survol.md","Shader d'image au survol",{"type":8,"value":324,"toc":455},[325,328,331,334,336,342,361,363,444,446],[11,326,322],{"id":327},"shader-dimage-au-survol",[15,329,330],{},"Trois paysages de montagne. Passe la souris (ou le focus clavier) sur une image : elle se déforme autour du curseur, se sépare en canaux rouge \u002F vert \u002F bleu, puis se dissout vers une seconde photo. Au clavier, le focus déclenche exactement le même effet.",[332,333],"shader-image-hover",{},[28,335,31],{"id":30},[15,337,34,338,341],{},[36,339,340],{},"fragment shader GLSL"," fait tout le travail, un vertex shader minimal pose juste un quad plein écran. Trois effets superposés :",[59,343,344,350,355],{},[62,345,346,349],{},[36,347,348],{},"Déformation liquide"," — une poussée radiale autour du pointeur, modulée par la vitesse de la souris et un léger tremblement de bruit.",[62,351,352,354],{},[36,353,87],{}," — les canaux R et B sont échantillonnés à des offsets opposés du canal V, proportionnels à la distance au curseur.",[62,356,357,360],{},[36,358,359],{},"Dissolution organique"," — un bruit fractal (fBM, 4 octaves) décide quels pixels basculent les premiers vers l'image d'arrivée, pour un seuillage doux.",[28,362,92],{"id":91},[59,364,365,375,384,396,420,430],{},[62,366,367,370,371,374],{},[36,368,369],{},"Lissage exponentiel"," indépendant du framerate pour la souris, la vitesse et la progression — la courbe ",[41,372,373],{},"1 - exp(-dt * k)"," donne un suivi qui reste cohérent même si le navigateur drop des frames.",[62,376,377,380,381,383],{},[36,378,379],{},"Mise en veille"," quand la carte sort du viewport (",[41,382,122],{},") et quand elle est au repos (plus rien ne bouge), pour épargner le GPU.",[62,385,386,391,392,395],{},[36,387,388,389],{},"Respect de ",[41,390,135],{}," — un uniforme ",[41,393,394],{},"uReduit"," désactive la déformation et la dissolution bruitée, il reste un simple fondu entre les deux images.",[62,397,398,78,401,404,405,408,409,412,413,408,416,419],{},[36,399,400],{},"Accessibilité clavier",[41,402,403],{},"tabindex=\"0\""," sur chaque carte, ",[41,406,407],{},"focus"," \u002F ",[41,410,411],{},"blur"," déclenchent la même animation que ",[41,414,415],{},"pointerenter",[41,417,418],{},"pointerleave",", avec la souris centrée.",[62,421,422,425,426,429],{},[36,423,424],{},"Chargement non bloquant"," — les textures sont chargées en parallèle via ",[41,427,428],{},"Promise.all",", chaque carte s'initialise dès que ses deux images sont prêtes.",[62,431,432,435,436,439,440,443],{},[36,433,434],{},"Fallback sans WebGL"," — si ",[41,437,438],{},"getContext('webgl')"," renvoie null, on tombe sur un simple ",[41,441,442],{},"drawImage"," 2D de l'image de départ.",[28,445,152],{"id":151},[15,447,448,449,454],{},"WebGL 1 brut (pas de lib), ES modules, Vue 3 pour l'intégration dans le portfolio. Images : photos de paysages de montagne libres de droits ",[158,450,453],{"href":451,"rel":452},"https:\u002F\u002Funsplash.com",[162],"Unsplash",", redimensionnées en 1024 × 1280 (ratio 4:5).",{"title":172,"searchDepth":173,"depth":173,"links":456},[457,458,459],{"id":30,"depth":173,"text":31},{"id":91,"depth":173,"text":92},{"id":151,"depth":173,"text":152},"Trois paysages de montagne se déforment, se décalent en RGB et se dissolvent vers une image d'arrivée quand le curseur les frôle.",{"tags":462},[182,183,185,463],"interaction","\u002Fcases\u002Fshader-image-survol",{"title":322,"description":460},"cases\u002Fshader-image-survol","JbRDX_XJBGQ7d9KCfmHuaDyzZ1K4q5nHjW1h_5DOR2I",1794436389385]