Affiche les shaders de fragments GLSL comme arrière-plan du site Web. Prend en charge les shaders shaderstoy, les tampons hors écran Ping-Ping-Pong, les boucles de rétroaction, les textures à virgule flottante. Soit avec WebGL 1 ou 2, essaiera de fonctionner partout où il est techniquement possible.
Site Web / Demo : ? https://xemant.github.io/shader-web-background?
❔ ??? Pour poser des questions, allez sur Xemantic Discord Server ☕ ???
J'ai conçu cette bibliothèque pour utiliser des shaders de fragments complexes dans le cadre de mon processus de conception et de développement Web. C'est l'outil qui me permet enfin d'adopter le navigateur Web comme un environnement de codage créatif. Si vous connaissez GLSL, cela pourrait également vous aider à publier votre travail sur le Web. Si vous venez d'un fond de développement Web, vous voudrez peut-être en apprendre un peu plus sur les shaders d'abord, par exemple dans le livre des Shaders. J'espère que les exemples présentés dans cette documentation sont explicites. Si vous le trouvez utile, alors
❤️ Sponsor Xemantic sur github ou https://www.buymeacoffee.com/kazik
Kazik (Morisil) Pogoda
https://xémantique.com/
Table des matières
texture ?Tl; dr:
<!DOCTYPE html >
< html lang =" en " >
< head >
< meta charset =" utf-8 " >
< title > Minimal shader </ title >
< meta name =" viewport " content =" width=device-width, initial-scale=1 " >
< meta http-equiv =" X-UA-Compatible " content =" IE=edge " >
< script src =" https://xemantic.github.io/shader-web-background/dist/shader-web-background.min.js " > </ script >
< script type =" x-shader/x-fragment " id =" image " >
precision highp float ;
uniform float iTime ;
void main ( ) {
gl_FragColor = vec4 (
mod ( gl_FragCoord . x / 256. , 1. ) ,
mod ( ( gl_FragCoord . x + gl_FragCoord . y - iTime * 40. ) / 256. , 1. ) ,
mod ( gl_FragCoord . y / 256. , 1. ) ,
1.
) ;
}
</ script >
< script >
shaderWebBackground . shade ( {
shaders : {
image : {
uniforms : {
iTime : ( gl , loc ) => gl . uniform1f ( loc , performance . now ( ) / 1000 )
}
}
}
} ) ;
</ script >
< style >
.shader-web-background-fallback {
background: url("https://placekitten.com/666/666");
background-position: center;
background-size: cover;
background-attachment: fixed;
}
</ style >
</ head >
< body >
< h1 > shader-web-background minimal example </ h1 >
</ body >
</ html >Si vous préférez apprendre par l'exemple, voici la liste des démos affichées avec leur code source en surbrillance:
https://xemant.github.io/shader-web-background/#demo
Il existe plusieurs façons d'ajuster cette bibliothèque à vos besoins:
Si vous voulez que vos shaders commencent à rendu avant que d'autres ressources ne soient chargées, optez pour cette méthode. Prenez simplement le contenu de:
https://xemant
et mettez-le comme <script> dans le <head> de votre fichier html.
Voir démo minimale pour référence (version en direct).
Ajoutez ce code à la <head> de votre HTML:
< script src =" https://xemantic.github.io/shader-web-background/dist/shader-web-background.min.js " > </ script > À l'avenir, je publierai shader-web-background à NPM. Pour l'instant, vous pouvez simplement télécharger la dernière distribution minifiée avec la carte source et les sources.
Vous aurez besoin d'au moins un shader fragment défini comme ceci:
< script type =" x-shader/x-fragment " id =" image " >
precision highp float ;
void main ( ) {
// ...
}
</ script > Mettez-le dans le <head> de votre HTML. Le type doit être x-shader/x-fragment et l'attribut id est arbitraire.
id unique à chacun de vos shaders si vous en définissez davantage.
< script >
shaderWebBackground.shade( {
shaders : {
image : { }
}
} );
</ script >image du nom du shader doit correspondre à celle définie comme l'attribut id source Shader.
Cette étape n'est pas nécessaire, mais l'ajout d'amélioration de l'expérience pour la petite quantité d'utilisateurs qui ne peuvent toujours pas exécuter les shaders sur leurs appareils.
Définissez le style CSS de secours, par exemple une capture d'écran statique de votre cadre de shader:
< style >
.shader-web-background-fallback {
background: url("https://placekitten.com/666/666");
background-position: center;
background-size: cover;
background-attachment: fixed;
}
</ style > La classe CSS shader-web-background-fallback est appliquée à la racine du document HTML et à la toile.
shader-web-background-fallback , mais cela pourrait ne pas fonctionner sur certains navigateurs. Le gestionnaire d'erreurs personnalisé peut être nécessaire pour la compatibilité croisée.
Voir la section des erreurs de traitement pour plus de détails.
Voir l'API complète du shader-web-background
L'objet de configuration transmis à l'appel ShaderWebBackground.shade (config) dans l'exemple ci-dessus entraînera un pipeline de rendu minimal composé d'un fragment de fragment nommé image . Un nouvel élément statique <canvas id="shader-web-background"> couvrant l'ensemble de la fenêtre sera ajouté à la page avec z-index: -9999 , à afficher derrière d'autres éléments de la page.
Remarque: L'élément <canvas> par défaut sera attaché au document <body> uniquement lorsque l'arborescence DOM entière est construite. De plus, le rendu réel des cadres de shaders ne se produira pas tant que la page sera complètement chargée, même si les shaders sont compilés immédiatement.
Les uniformes fournissent aux shaders l'entrée du monde en dehors du GPU. Décrire ce mécanisme est hors de portée de cette documentation. J'ai décidé de ne pas construire l'abstraction sur cette partie de WebGL, car elle est déjà assez concise. Voir WebGlRenderingContext.Uniformes Documentation.
Supposons que vous souhaitez fournir à votre shader une valeur de temps mesurée en quelques secondes depuis le moment où la page a été chargée. Définissez d'abord un uniforme dans le shader image :
uniform float iTime; Le nom iTime est arbitraire, mais il doit correspondre à ce que vous spécifiez dans la configuration:
shaderWebBackground . shade ( {
shaders : {
image : {
uniforms : {
iTime : ( gl , loc ) => gl . uniform1f ( loc , performance . now ( ) / 1000 )
}
}
}
} ) ; La fonction (gl, loc) => gl.uniform1f(loc, performance.now() / 1000) sera invoquée avant de rendre chaque cadre de shader. Si vous n'êtes pas familier avec les fonctions de flèche JavaScript, c'est un équivalent de:
function ( gl , loc ) {
gl . uniform1f ( loc , performance . now ( ) / 1000 )
} Vérifiez la documentation de la fonction JavaScript standard.Now () qui renvoie le nombre de millisecondes depuis le chargement de la page. Le diviser par 1000 entraînera une valeur à virgule flottante mesurée en secondes.
Résumé: Vous pouvez utiliser ce mécanisme pour adapter n'importe quelle API comme entrée de vos shaders. Vérifiez les démos du projet pour des exemples comment intégrer les entrées comme:
La déclaration d'uniforme "texture" utilise le type sampler2D :
uniform sampler2D iWebCam; Le nom uniforme est arbitraire. Par exemple, Shadertoy est des textures contraignantes sous le nom d' iChannel0 , iChannel1 , etc. Et c'est la convention utilisée principalement dans cette documentation.
Un tel uniforme peut être défini avec:
shaderWebBackground . shade ( {
onInit : ( ctx ) => {
ctx . iWebCam = initializeTexture ( ctx . gl ) ;
} ,
shaders : {
image : {
uniforms : {
iWebCam : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . iWebCam ) ;
}
}
}
} ) ;La texture passée comme deuxième argument à CTX.Texture peut être une instance de WebGlTexture ou une référence au tampon d'un autre shader dans le pipeline. Vérifiez la section et l'API de la configuration du complexe - Contexte: tampons.
Voir la section Ajout de textures pour plus de détails sur la façon de charger une texture à partir d'une image.
Tous les shaders, à l'exception du dernier du pipeline, auront des textures associées auxquelles il y aura des textures. Par défaut, ces textures sont initialisées sous forme de point flottante RGBA HALF_FLOAT (16bit) avec une interpolation linéaire et sont fixées au bord. L'initialisation de la texture peut être personnalisée. Voir API - Shader: Documentation de la texture pour plus de détails.
Voici un exemple complet d'un objet de configuration avec des commentaires. Il utilise les conventions Shadertoy pour les tampons et les uniformes de dénomination, mais gardez à l'esprit que la dénomination est arbitraire et pourrait être ajustée aux besoins de votre projet.
// mouse coordinates taken from from the mousemove event expressed in "CSS pixels"
var mouseX ;
var mouseY ;
document . addEventListener ( "mousemove" , ( event ) => {
mouseX = event . clientX ;
mouseY = event . clientY ;
} ) ;
shaderWebBackground . shade ( {
// supplied canvas to use for shading
canvas : document . getElementById ( "my-canvas" ) ,
// called only once before the first run
onInit : ( ctx ) => {
// we can center the mouse even before any "mousemove" event occurs
// note, we are
mouseX = ctx . cssWidth / 2 ;
mouseY = ctx . cssHeight / 2 ;
// for convenience you can store your attributes on context
ctx . iFrame = 0 ;
} ,
onResize : ( width , height , ctx ) => {
ctx . iMinDimension = Math . min ( width , height ) ;
} ,
onBeforeFrame : ( ctx ) => {
ctx . shaderMouseX = ctx . toShaderX ( mouseX ) ;
ctx . shaderMouseY = ctx . toShaderY ( mouseY ) ;
} ,
shaders : {
// the first buffer to be rendered in the pipeline
BufferA : {
// uniform setters, attribute names should match with those defined in the shader
uniforms : {
// uniform value calculated in place
iTime : ( gl , loc ) => gl . uniform1f ( loc , performance . now ( ) / 1000 ) ,
// uniform values taken from context
iFrame : ( gl , loc ) => gl . uniform1i ( loc , ctx . iFrame ) ,
iMinDimension : ( gl , loc , ctx ) => gl . uniform1f ( loc , ctx . iMinDimension ) ,
iResolution : ( gl , loc , ctx ) => gl . uniform2f ( loc , ctx . width , ctx . height ) ,
iMouse : ( gl , loc , ctx ) => gl . uniform2f ( loc , ctx . shaderMouseX , ctx . shaderMouseY ) ,
// inputing the previous output of itself - feedback loop
iChannel0 : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . buffers . BufferA )
// ... more uniforms
}
} ,
// ... more shaders
BufferD : {
// optional custom initializer of buffer's texture
texture : ( gl , ctx ) => {
// initializing floating-point texture in custom way for WebGL 1 and 2
ctx . initHalfFloatRGBATexture ( ctx . width , ctx . height ) ;
// standard WebGL texture parameters
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_MIN_FILTER , gl . NEAREST ) ;
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_MAG_FILTER , gl . NEAREST ) ;
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_WRAP_S , gl . REPEAT ) ;
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_WRAP_T , gl . REPEAT ) ;
} ,
uniforms : {
iChanel0 : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . buffers . BufferA )
// ... more uniforms
}
} ,
// the last shader will render to screen
Image : {
uniforms : {
iChanel0 : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . buffers . BufferD )
// ... more uniforms
}
}
} ,
onAfterFrame : ( ctx ) => {
ctx . iFrame ++ ;
} ,
// custom error handler
onError : ( error , canvas ) => {
canvas . remove ( ) ;
console . error ( error ) ;
document . documentElement . classList . add ( "my-fallback" ) ;
}
} ) ; L'API est destinée à être explicative. Vérifiez les spécifications de l'API pour plus de détails. Il y a plusieurs shaders définis dans l'exemple ci-dessus. Ils seront traités dans une séquence appelée Multipass dans Shadertoy Nomenclature. Les derniers shaders définis rendront à l'écran. La sortie des shaders précédents, y compris la boucle de rétroaction du cadre précédent rendu par le même shader, peut être facilement transmis aux uniformes.
Plusieurs validations sont effectuées sur la configuration fournie pour éviter les problèmes courants qui sont généralement difficiles à déboguer autrement. Le dossier SRC / Test / HTML / ERRORS / Contient tous les cas de test d'erreur qui peuvent également être vérifiés sur la démo en direct de la gestion des erreurs.
Toutes les erreurs et avertissements seront visibles sur la console.
Voir:
// mouse coordinates taken from from the mousemove event
var mouseX ;
var mouseY ;
document . addEventListener ( "mousemove" , ( event ) => {
mouseX = event . clientX ;
mouseY = event . clientY ;
} ) ;
// mouse coordinates relative to the shader, you can also store them on the context
var shaderMouseX ;
var shaderMouseY ;
shaderWebBackground . shade ( {
onInit : ( ctx ) => {
// screen center
mouseX = ctx . cssWidth / 2 ;
mouseY = ctx . cssHeight / 2 ;
} ,
onBeforeFrame : ( ctx ) => {
shaderMouseX = ctx . toShaderX ( mouseX ) ;
shaderMouseY = ctx . toShaderY ( mouseY ) ;
} ,
shaders : {
image : {
uniforms : {
iMouse : ( gl , loc ) => gl . uniform2f ( loc , shaderMouseX , shaderMouseY )
}
}
}
} ) ; Remarque: les coordonnées initiales de la souris sont fournies dans la fonction onInit car le premier événement mousemove peut se produire longtemps après le démarrage du shader. Les coordonnées du shader commencent dans le coin inférieur à gauche de la toile et sont alignées sur le milieu du pixel - (0.5, 0.5) .
Référence de l'API:
Démos:
python -m http.server 8000 Si cela ne fonctionne pas sur votre dernier Ubuntu que d'exécuter sudo apt install python-is-python3 en premier.
Voir texture: démo de cartographie en marbre bleu à plat
Les textures peuvent être définies de la même manière que les tampons sont définis en uniformes, mais nous devons d'abord les charger. Par exemple en définissant une promesse personnalisée qui peut être réutilisée:
const loadImage = ( src ) => new Promise ( ( resolve , reject ) => {
let img = new Image ( ) ;
img . onload = ( ) => resolve ( img ) ;
img . onerror = ( ) => {
reject ( new Error ( "Failed to load image from: " + src ) ) ;
}
img . src = src ;
} ) ; La fonction onInit est un endroit assez pratique pour appeler loadPicture :
shaderWebBackground . shade ( {
onInit : ( ctx ) => {
loadImage ( "texture.jpg" )
. then ( image => {
const gl = ctx . gl ;
const texture = gl . createTexture ( ) ;
gl . bindTexture ( gl . TEXTURE_2D , texture ) ;
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_WRAP_S , gl . CLAMP_TO_EDGE ) ;
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_WRAP_T , gl . CLAMP_TO_EDGE ) ;
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_MIN_FILTER , gl . LINEAR ) ;
gl . texParameteri ( gl . TEXTURE_2D , gl . TEXTURE_MAG_FILTER , gl . LINEAR ) ;
gl . texImage2D ( gl . TEXTURE_2D , 0 , gl . RGBA , gl . RGBA , gl . UNSIGNED_BYTE , image ) ;
gl . bindTexture ( gl . TEXTURE_2D , null ) ;
ctx . iTexture = texture ;
} ) ;
} ,
shaders : {
image : {
uniforms : {
iTexture : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . iTexture )
}
}
}
} ) ; Cette bibliothèque peut utiliser le code shadertoy avec un minimum d'effort - un simple emballage de shader:
< script type =" x-shader/x-fragment " id =" Image " >
precision highp float ;
uniform vec2 iResolution ;
uniform float iTime ;
// ... other needed uniforms
// -- Paste your Shadertoy code here:
// ...
// -- End of Shadertoy code
void main ( ) {
mainImage ( gl_FragColor , gl_FragCoord . xy ) ;
}
</ script > L'attribut id du <script> est défini pour refléter l'onglet Shadertoy appelé Image . La plupart des shaders utiliseront au moins ces 2 uniformes, et il est facile de fournir leurs valeurs dans la configuration:
shaderWebBackground . shade ( {
shaders : {
Image : {
uniforms : {
iResolution : ( gl , loc , ctx ) => gl . uniform2f ( loc , ctx . width , ctx . height ) ,
iTime : ( gl , loc ) => gl . uniform1f ( loc , performance . now ( ) / 1000 ) ,
}
}
}
} ) ;Demos Shadertoy:
Il n'y a pas de solution automatisée pour cela. Vous devrez copier la partie Common directement dans vos shaders, juste au-dessus de l'autre code Shadertoy.
texture ? Dans Shadertoy, les textures sont accessibles avec la fonction texture tandis que dans WebGL 1, il est texture2D . Voici une solution de contournement simple à ajouter avant le code d'origine:
#define texture texture2D Dans Shadertoy, chaque liaison "canal", une texture peut avoir des paramètres d'échantillonneur séparés comme l'interpolation ou l'enveloppe. Cette fonctionnalité ne peut pas être facilement portée sur WebGL 1, mais la plupart des shaders relayant ces fonctionnalités peuvent être ajustés avec des solutions de contournement basées sur le code. Par exemple, si la texture est censée être répétée, alors quelque chose comme celui-ci pourrait être un remplacement fonctionnel de la fonction texture dans un shader donné:
vec4 repeatedTexture( in sampler2D channel, in vec2 uv) {
return texture2D (channel, mod (uv, 1 .));
}Voir aussi API - Shader: Texture.
Vous pouvez nommer vos shaders en fonction des noms de tampon Shadertoy:
BufferABufferBBufferCBufferDImagePuis les câbler ensemble:
<!DOCTYPE html >
< html lang =" en " >
< head >
< title > Multipass Shadertoy shader </ title >
< script type =" x-shader/x-fragment " id =" BufferA " >
precision highp float ;
uniform sampler2D iChannel0 ;
// ... the code of BufferA tab with the uniforms and wrapping as above
</ script >
< script type =" x-shader/x-fragment " id =" Image " >
precision highp float ;
uniform sampler2D iChannel0 ;
// ... the code of Image tab with the uniforms and wrapping as above
</ script >
< script >
// ... your prefer method of loading shader-web-background as described above
</ script >
< script >
shaderWebBackground . shade ( {
shaders : {
BufferA : {
uniforms : {
iChannel0 : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . buffers . BufferA )
}
} ,
Image : {
uniforms : {
iChannel0 : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . buffers . BufferA )
}
}
}
} ) ;
</ script >
</ head >
< body >
</ body >
</ html > Il est possible de modifier le vertex shader par défaut pour chaque shader de fragment en fournissant le script suivant dans le <head> :
< script type = "x-shader/x-vertex" id = "shaderIdVertex" >
attribute vec2 V;
varying vec2 uv;
void main() {
gl_Position = vec4 ( V , 0 , 1 ) ;
}
</ script >
< script type = "x-shader/x-fragment" id = "shaderId" >
// ...
varying vec2 uv ;
// ...
< / script > Remarque: Le type de script est défini sur x-shader/x-vertex et l'attribut id est apparenté avec le suffixe Vertex . L'attribut de sommet doit être nommé V .
Remarque: varying vec2 uv peuvent être spécifiés pour être partagés entre les shaders Vertex et Fragment (non ajoutés par défaut).
git clone https://github.com/xemantic/shader-web-background.git
cd shader-web-background
./gradlew compileJsIl déclenchera le compilateur Google Closure qui vérifiera les sources à l'aide des informations de type et les transpilera dans des fichiers JavaScript minifiés:
Ce projet a été développé à l'aide d'idées Intellij avec le plugin Google-Java-Format activé. L'élément le plus notable de ce style est 2 espaces au lieu de 4 pour les onglets de rendu.
Soit:
<section id="projects-using-shader-web-background">Ou envoyez-moi un lien avec la description.