Exibe os shaders de fragmento GLSL como um histórico de site. Suporta shaderoy Shaders, Multipass-Buffers de tela fora da tela de pingue-pongue, loops de feedback, texturas de ponto flutuante. Com o WebGL 1 ou 2, tentará executar onde for tecnicamente possível.
Site/Demo : ? https://xemantic.github.io/shader-web-background?
❔ ??? Para fazer perguntas, vá para o Xemantic Discord Server ☕ ???
Eu projetei esta biblioteca para usar shaders de fragmentos complexos como parte do meu processo de design e desenvolvimento da web. Esta é a ferramenta que finalmente me permite abraçar o navegador da web como um ambiente de codificação criativa. Se você está familiarizado com o GLSL, também pode ajudá -lo a publicar seu trabalho na web. Se você vier de um histórico de desenvolvimento da Web, convém aprender um pouco mais sobre os shaders primeiro, por exemplo, do Livro dos Shaders. Espero que os exemplos apresentados nesta documentação sejam auto-explicativos. Se você achar útil, então
❤️ Patrocinador Xemantic no github ou https://www.buymeacoffee.com/kazik
Kazik (Morisil) Pogoda
https://xemantic.com/
Índice
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 >Se você preferir aprender pelo exemplo, aqui está a lista de demos exibidos com o código -fonte destacado:
https://xemantic.github.io/shader-web-background/#demo
Existem várias maneiras de ajustar esta biblioteca às suas necessidades:
Se você deseja que seus shaders comecem a renderizar antes que outros recursos sejam carregados, opte por esse método. Basta pegar o conteúdo de:
https://xemantic.github.io/shader-web-background/dist/shader-web-background.min.js
e coloque -o como <script> no <head> do seu arquivo html.
Consulte a demonstração mínima para referência (versão ao vivo).
Adicione este código ao <head> do seu HTML:
< script src =" https://xemantic.github.io/shader-web-background/dist/shader-web-background.min.js " > </ script > No futuro, publicarei shader-web-background no NPM. Por enquanto, você pode simplesmente baixar a mais recente distribuição minificada, juntamente com mapa e fontes de origem.
Você precisará de pelo menos um shader de fragmento definido assim:
< script type =" x-shader/x-fragment " id =" image " >
precision highp float ;
void main ( ) {
// ...
}
</ script > Coloque -o no <head> do seu HTML. O type deve ser x-shader/x-fragment e o atributo id é arbitrário.
id exclusivo a cada um de seus shaders, se você estiver definindo mais deles.
< script >
shaderWebBackground.shade( {
shaders : {
image : { }
}
} );
</ script >image do nome do shader deve corresponder à definida como atributo de id de origem do shader.
Esta etapa não é necessária, no entanto, adicionar isso melhorará a experiência da pequena quantidade de usuários que ainda não conseguem executar shaders em seus dispositivos.
Defina o estilo CSS de fallback, por exemplo, uma captura de tela estática do seu quadro de shader:
< style >
.shader-web-background-fallback {
background: url("https://placekitten.com/666/666");
background-position: center;
background-size: cover;
background-attachment: fixed;
}
</ style > A classe CSS shader-web-background-fallback é aplicada à raiz do documento HTML e à tela.
shader-web-background-fallback , no entanto, pode não funcionar em alguns navegadores. O manipulador de erros personalizado pode ser necessário para compatibilidade cruzada.
Consulte a seção de erros de manuseio para obter detalhes.
Veja a API completa do shader-web-background
O objeto de configuração passado para a chamada shaderwebbackground.shade (config) no exemplo acima resultará em um pipeline de renderização mínimo que consiste em um image de fragmento nomeado. Um elemento estático <canvas id="shader-web-background"> que cobre toda a viewport será adicionado à página com z-index: -9999 , a ser exibido atrás de outros elementos da página.
NOTA: O elemento padrão <canvas> será anexado ao documento <body> somente quando toda a árvore Dom for construída. Além disso, a renderização real dos quadros de shader não acontecerá até que a página esteja totalmente carregada, mesmo que os shaders sejam compilados imediatamente.
Os uniformes fornecem aos shaders a contribuição do mundo fora da GPU. Descrever esse mecanismo está fora do escopo desta documentação. Decidi não criar abstração nessa parte do WebGL, porque já é bastante conciso. Consulte WebglrenderingContext.uniforme documentação.
Vamos supor que você deseja fornecer ao seu shader um valor de tempo medido em segundos desde o momento em que a página foi carregada. Primeiro defina um uniforme no shader image :
uniform float iTime; O nome iTime é arbitrário, mas deve corresponder ao que você especificar na configuração:
shaderWebBackground . shade ( {
shaders : {
image : {
uniforms : {
iTime : ( gl , loc ) => gl . uniform1f ( loc , performance . now ( ) / 1000 )
}
}
}
} ) ; A função (gl, loc) => gl.uniform1f(loc, performance.now() / 1000) será invocada antes de renderizar cada estrutura do shader. Se você não está familiarizado com as funções de seta JavaScript, é um equivalente a:
function ( gl , loc ) {
gl . uniform1f ( loc , performance . now ( ) / 1000 )
} Verifique a documentação da função JavaScript padrão.now () que retorna o número de milissegundos desde que a página foi carregada. A divisão por 1000 resultará em valor de ponto flutuante medido em segundos.
Resumo: Você pode usar esse mecanismo para adaptar qualquer API como uma entrada de seus shaders. Verifique as demos do projeto para obter exemplos de como integrar a entrada como:
A declaração de uniforme "textura" usa o tipo sampler2D :
uniform sampler2D iWebCam; O nome uniforme é arbitrário. Por exemplo, Shadetoy está vinculando texturas em nome iChannel0 , iChannel1 , etc. e esta é a convenção usada principalmente nesta documentação.
Esse uniforme pode ser definido com:
shaderWebBackground . shade ( {
onInit : ( ctx ) => {
ctx . iWebCam = initializeTexture ( ctx . gl ) ;
} ,
shaders : {
image : {
uniforms : {
iWebCam : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . iWebCam ) ;
}
}
}
} ) ;A textura passou como um segundo argumento para o CTX.Texture pode ser uma instância de Webgltexture ou uma referência ao buffer de outro shader no pipeline. Verifique seção de exemplo de configuração complexa e API - Contexto: Buffers.
Consulte a seção Adicionando texturas para obter detalhes sobre como carregar uma textura de uma imagem.
Todos os shaders, exceto o último do pipeline, terão texturas associadas a renderizar. Por padrão, essas texturas são inicializadas como ponto flutuante RGBA HALF_FLOAT (16bits) com interpolação linear e são fixadas na borda. A inicialização da textura pode ser personalizada. Consulte API - SHADER: Documentação de textura para obter detalhes.
Aqui está um exemplo abrangente de um objeto de configuração com comentários. Ele está usando convenções Shadetoy para nomear buffers e uniformes, mas lembre -se de que a nomeação é arbitrária e pode ser ajustada às necessidades do seu projeto.
// 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" ) ;
}
} ) ; A API deve ser auto -explicativa. Verifique a especificação da API para obter detalhes. Existem vários shaders definidos no exemplo acima. Eles serão processados em sequência chamada Multipass na nomenclatura de Shadetoy. O último dos shaders definidos será renderizado para a tela. A saída de shaders anteriores, incluindo o loop de feedback do quadro anterior renderizado pelo mesmo shader, pode ser facilmente transmitido para uniformes.
Várias validações estão sendo realizadas na configuração fornecida para evitar problemas comuns que geralmente são difíceis de depurar. A pasta src/test/html/erros/contém todos os casos de teste de erro que também podem ser verificados na demonstração ao vivo do manuseio de erros.
Todos os erros e avisos serão visíveis no console.
Ver:
// 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 )
}
}
}
} ) ; NOTA: As coordenadas iniciais do mouse são fornecidas na função onInit , porque o primeiro evento mousemove pode acontecer muito depois que o shader é iniciado. As coordenadas do shader começam no canto inferior esquerdo da tela e estão alinhadas com o meio do pixel - (0.5, 0.5) .
Referência da API:
Demos:
python -m http.server 8000 se não funcionar no seu mais recente Ubuntu, o Run sudo apt install python-is-python3 primeiro.
Veja Textura: Demoção de mármore azul para mapeamento de terra plana
As texturas podem ser definidas da mesma maneira que os buffers são definidos como uniformes, mas primeiro precisamos carregá -los. Por exemplo, definindo a promessa personalizada que pode ser reutilizada:
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 ;
} ) ; A função onInit é um local bastante conveniente para chamar 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 )
}
}
}
} ) ; Esta biblioteca pode utilizar o código Shadetoy com um esforço mínimo - um shater simples:
< 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 > O atributo id do <script> está definido para refletir a guia Shadetoy chamada Image . A maioria dos shaders usará pelo menos esses 2 uniformes e é fácil fornecer seus valores na configuração:
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 ) ,
}
}
}
} ) ;Demonses Shadetoy:
Não há solução automatizada para isso. Você precisará copiar a parte Common diretamente para seus shaders, logo acima do outro código Shadetoy.
texture ? Nas texturas de shadetoy, são acessadas com a função texture enquanto no webgl 1 é texture2D . Aqui está uma solução alternativa simples a ser adicionada antes do código original:
#define texture texture2D Em Shadetoy, cada "canal" que vincula uma textura pode ter parâmetros de amostragem separados, como interpolação ou embalagem. Essa funcionalidade não pode ser facilmente portada para o WebGL 1, mas a maioria dos shaders que transmitia esses recursos pode ser ajustada com soluções alternativas baseadas em código. Por exemplo, se a textura deve ser repetida, algo assim pode ser uma substituição funcional da função texture em um determinado shader:
vec4 repeatedTexture( in sampler2D channel, in vec2 uv) {
return texture2D (channel, mod (uv, 1 .));
}Veja também API - Shader: Texture.
Você pode nomear seus shaders de acordo com os nomes de buffer shaderoy:
BufferABufferBBufferCBufferDImageE depois os conecte:
<!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 > É possível alterar o shader de vértice padrão para cada shader de fragmento, fornecendo o seguinte script no <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 > NOTA: O type de script é definido como x-shader/x-vertex e o atributo id é preso com o sufixo Vertex . O atributo vértice deve ser nomeado V .
NOTA: varying vec2 uv pode ser especificado para ser compartilhado entre os shaders de vértice e fragmentos (não adicionados por padrão).
git clone https://github.com/xemantic/shader-web-background.git
cd shader-web-background
./gradlew compileJsEle desencadeará o compilador de fechamento do Google, que verificará as fontes usando informações de tipo e transpilam -as em arquivos JavaScript ministrados:
Este projeto foi desenvolvido usando o Intellij Idea com o plug-in do Google-Java-format ativado. O elemento mais notável desse estilo são 2 espaços em vez de 4 para renderizar guias.
Qualquer:
<section id="projects-using-shader-web-background">Ou envie -me um link com a descrição.