Menampilkan shader fragmen GLSL sebagai latar belakang situs web. Mendukung Shadertoy Shaders, Multipass-Ping-pong offscreen buffer, loop umpan balik, tekstur titik mengambang. Baik dengan WebGL 1 atau 2, akan mencoba berjalan di mana pun secara teknis dimungkinkan.
Situs Web/Demo : ? https://xemantic.github.io/shader-web-background?
❔ ??? Untuk mengajukan pertanyaan, kunjungi server Xemantic Discord ☕ ???
Saya merancang perpustakaan ini untuk menggunakan shader fragmen yang kompleks sebagai bagian dari desain web dan proses pengembangan saya. Ini adalah alat yang akhirnya memungkinkan saya merangkul browser web sebagai lingkungan pengkodean kreatif. Jika Anda terbiasa dengan GLSL, maka itu mungkin membantu Anda mempublikasikan karya Anda di web juga. Jika Anda berasal dari latar belakang pengembangan web, maka Anda mungkin ingin belajar lebih banyak tentang shader terlebih dahulu, misalnya dari Book of Shaders. Saya berharap contoh-contoh yang disajikan dalam dokumentasi ini cukup jelas. Jika Anda merasa berguna, maka
❤️ Sponsor Xemantic di GitHub atau https://www.buymeacoffee.com/kazik
Kazik (Morisil) Pogoda
https://xemantic.com/
Daftar isi
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 >Jika Anda lebih suka belajar dengan contoh, berikut adalah daftar demo yang ditampilkan dengan kode sumber yang disorot:
https://xemantic.github.io/shader-web-background/#demo
Ada beberapa cara untuk menyesuaikan perpustakaan ini dengan kebutuhan Anda:
Jika Anda ingin shader Anda mulai render sebelum sumber daya lain dimuat, maka pilih metode ini. Ambil saja isi:
https://xemantic.github.io/shader-web-background/dist/shader-web-background.min.js
dan masukkan sebagai <script> di <head> file html Anda.
Lihat demo minimal untuk referensi (versi langsung).
Tambahkan kode ini ke <head> HTML Anda:
< script src =" https://xemantic.github.io/shader-web-background/dist/shader-web-background.min.js " > </ script > Di masa depan saya akan menerbitkan shader-web-background ke NPM. Untuk saat ini Anda dapat mengunduh distribusi terpini terbaru bersama dengan peta sumber dan sumber.
Anda akan membutuhkan setidaknya satu fragmen shader yang ditentukan seperti ini:
< script type =" x-shader/x-fragment " id =" image " >
precision highp float ;
void main ( ) {
// ...
}
</ script > Masukkan ke dalam <head> HTML Anda. type harus x-shader/x-fragment dan atribut id sewenang-wenang.
id unik untuk setiap shader Anda jika Anda mendefinisikan lebih banyak dari mereka.
< script >
shaderWebBackground.shade( {
shaders : {
image : { }
}
} );
</ script >image nama shader harus cocok dengan yang ditentukan sebagai atribut Sumber id Shader.
Langkah ini tidak perlu, namun menambahkannya akan meningkatkan pengalaman bagi sejumlah kecil pengguna yang masih tidak dapat menjalankan shader di perangkat mereka.
Tentukan gaya Fallback CSS, misalnya tangkapan layar statis dari bingkai shader Anda:
< style >
.shader-web-background-fallback {
background: url("https://placekitten.com/666/666");
background-position: center;
background-size: cover;
background-attachment: fixed;
}
</ style > Kelas CSS shader-web-background-fallback diterapkan pada root dokumen HTML dan kanvas.
shader-web-background-fallback , namun mungkin tidak berfungsi pada beberapa browser. Penangan kesalahan khusus mungkin diperlukan untuk kompatibilitas silang.
Lihat bagian Penanganan Kesalahan untuk detailnya.
Lihat API Shader-Web-Background lengkap
Objek konfigurasi yang diteruskan ke shaderwebbackground.shade (config) panggilan dalam contoh di atas akan menghasilkan pipa rendering minimal yang terdiri dari satu fragmen shader bernama image . Statis baru <canvas id="shader-web-background"> Elemen yang mencakup seluruh viewport akan ditambahkan ke halaman dengan z-index: -9999 , yang akan ditampilkan di belakang elemen halaman lainnya.
CATATAN: Elemen <canvas> default akan dilampirkan ke Dokumen <body> hanya ketika seluruh pohon DOM dibangun. Juga render aktual bingkai shader tidak akan terjadi sampai halaman sepenuhnya dimuat, meskipun shader segera dikompilasi.
Seragam memberikan input dari dunia dari dunia di luar GPU. Menjelaskan mekanisme ini di luar ruang lingkup dokumentasi ini. Saya memutuskan untuk tidak membangun abstraksi atas bagian WebGL ini, karena sudah cukup ringkas. Lihat WebGlrenderingContext.uniform Documentation.
Mari kita asumsikan bahwa Anda ingin memberikan nilai waktu shader Anda diukur dalam hitungan detik sejak saat halaman dimuat. Pertama -tama tentukan seragam di shader image :
uniform float iTime; Nama iTime sewenang -wenang, tetapi harus cocok dengan apa yang Anda tentukan dalam konfigurasi:
shaderWebBackground . shade ( {
shaders : {
image : {
uniforms : {
iTime : ( gl , loc ) => gl . uniform1f ( loc , performance . now ( ) / 1000 )
}
}
}
} ) ; Fungsi (gl, loc) => gl.uniform1f(loc, performance.now() / 1000) akan dipanggil sebelum membuat setiap bingkai shader. Jika Anda tidak terbiasa dengan fungsi panah JavaScript, itu setara dengan:
function ( gl , loc ) {
gl . uniform1f ( loc , performance . now ( ) / 1000 )
} Periksa dokumentasi fungsi JavaScript Standard.Now (sekarang) yang mengembalikan jumlah milidetik sejak halaman dimuat. Membagi dengan 1000 akan menghasilkan nilai titik mengambang yang diukur dalam detik.
Ringkasan: Anda dapat menggunakan mekanisme ini untuk mengadaptasi API apa pun sebagai input shader Anda. Periksa demo proyek untuk contoh bagaimana mengintegrasikan input seperti:
Deklarasi seragam "tekstur" menggunakan tipe sampler2D :
uniform sampler2D iWebCam; Nama seragamnya sewenang -wenang. Misalnya Shadertoy mengikat tekstur di bawah nama iChannel0 , iChannel1 , dll. Dan ini adalah konvensi yang sebagian besar digunakan dalam dokumentasi ini.
Seragam seperti itu dapat diatur dengan:
shaderWebBackground . shade ( {
onInit : ( ctx ) => {
ctx . iWebCam = initializeTexture ( ctx . gl ) ;
} ,
shaders : {
image : {
uniforms : {
iWebCam : ( gl , loc , ctx ) => ctx . texture ( loc , ctx . iWebCam ) ;
}
}
}
} ) ;Tekstur yang dilewati sebagai argumen kedua ke CTX.Texture dapat berupa contoh webgltexture atau referensi ke buffer shader lain dalam pipa. Periksa bagian Contoh Konfigurasi Kompleks dan API - Konteks: Buffer.
Lihat bagian Tambahkan Tekstur untuk detail tentang cara memuat tekstur dari gambar.
Semua shader, kecuali yang terakhir dalam pipa, akan memiliki tekstur terkait untuk diterjemahkan. Secara default, tekstur ini diinisialisasi sebagai RGBA HALF_FLOAT (16bit) titik mengambang dengan interpolasi linier dan dijepit ke tepi. Inisialisasi tekstur dapat disesuaikan. Lihat API - Shader: Dokumentasi Tekstur untuk detailnya.
Berikut adalah contoh komprehensif dari objek konfigurasi dengan komentar. Ini menggunakan konvensi Shadertoy untuk penamaan buffer dan seragam tetapi perlu diingat bahwa penamaan itu sewenang -wenang dan mungkin disesuaikan dengan kebutuhan proyek Anda.
// 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" ) ;
}
} ) ; API dimaksudkan untuk menjadi penjelasan diri. Periksa spesifikasi API untuk detailnya. Ada beberapa shader yang didefinisikan dalam contoh di atas. Mereka akan diproses secara berurutan yang disebut Multipass dalam nomenklatur shadertoy. Shader terakhir yang ditentukan akan membuat layar. Output shader sebelumnya, termasuk loop umpan balik dari bingkai sebelumnya yang diberikan oleh shader yang sama, dapat dengan mudah diteruskan ke seragam.
Beberapa validasi dilakukan pada konfigurasi yang disediakan untuk menghindari masalah umum yang biasanya sulit untuk debug sebaliknya. SRC/tes/html/kesalahan/folder berisi semua kasus uji kesalahan yang juga dapat diperiksa pada demo langsung penanganan kesalahan.
Semua kesalahan dan peringatan akan terlihat di konsol.
Melihat:
// 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 )
}
}
}
} ) ; CATATAN: Koordinat mouse awal disediakan dalam fungsi onInit karena peristiwa mousemove pertama dapat terjadi lama setelah shader dimulai. Koordinat shader mulai dari sudut kiri bawah kanvas dan disejajarkan dengan tengah piksel - (0.5, 0.5) .
Referensi API:
Demo:
python -m http.server 8000 jika tidak berfungsi pada ubuntu terbaru Anda daripada menjalankan sudo apt install python-is-python3 pertama.
Lihat Tekstur: Demo Pemetaan Bumi Marmer ke Datar
Tekstur dapat diatur dengan cara yang sama buffer diatur sebagai seragam, tetapi pertama -tama kita perlu memuatnya. Misalnya dengan mendefinisikan janji khusus yang dapat digunakan kembali:
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 ;
} ) ; Fungsi onInit adalah tempat yang cukup nyaman untuk memanggil 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 )
}
}
}
} ) ; Perpustakaan ini dapat menggunakan kode shadertoy dengan upaya minimal - pembungkus shader sederhana:
< 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 > Atribut id dari <script> diatur untuk mencerminkan tab shadertoy yang disebut Image . Sebagian besar shader akan menggunakan setidaknya 2 seragam ini, dan mudah untuk memberikan nilainya dalam konfigurasi:
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 ) ,
}
}
}
} ) ;Demo Shadertoy:
Tidak ada solusi otomatis untuk itu. Anda harus menyalin bagian Common langsung ke shader Anda, tepat di atas kode shadertoy lainnya.
texture ? Dalam Shadertoy, tekstur diakses dengan fungsi texture saat berada di WebGL 1 itu adalah texture2D . Berikut adalah solusi sederhana yang akan ditambahkan sebelum kode asli:
#define texture texture2D Di Shadertoy masing -masing "saluran" yang mengikat tekstur dapat memiliki parameter sampler terpisah seperti interpolasi atau pembungkus. Fungsionalitas ini tidak dapat dengan mudah diangkut ke WebGL 1, tetapi sebagian besar shader menyampaikan fitur-fitur ini dapat disesuaikan dengan solusi berbasis kode. Misalnya jika tekstur seharusnya diulang, maka sesuatu seperti ini mungkin merupakan penggantian fungsional fungsi texture dalam shader yang diberikan:
vec4 repeatedTexture( in sampler2D channel, in vec2 uv) {
return texture2D (channel, mod (uv, 1 .));
}Lihat juga API - Shader: Tekstur.
Anda dapat memberi nama Shaders Anda sesuai dengan nama buffer Shadertoy:
BufferABufferBBufferCBufferDImageDan kemudian menyatukan mereka:
<!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 > Dimungkinkan untuk mengubah vertex shader default untuk setiap fragmen shader dengan memberikan skrip berikut di <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 > Catatan: type skrip diatur ke x-shader/x-vertex dan atribut id dipersiapkan dengan akhiran Vertex . Atribut Vertex harus dinamai V .
Catatan: varying vec2 uv dapat ditentukan untuk dibagikan antara vertex dan shader fragmen (tidak ditambahkan secara default).
git clone https://github.com/xemantic/shader-web-background.git
cd shader-web-background
./gradlew compileJsIni akan memicu google compiler penutupan yang akan memeriksa sumber menggunakan informasi tipe dan mentranspile mereka ke file JavaScript yang diminifikasi:
Proyek ini telah dikembangkan menggunakan ide IntelliJ dengan plugin Google-Java-format yang diaktifkan. Elemen yang paling mencolok dari gaya ini adalah 2 spasi, bukan 4 untuk rendering tab.
Salah satu:
<section id="projects-using-shader-web-background">Atau kirimkan saya tautan dengan deskripsi.