מסננים מותאמים אישית, או 'שידרוגים של CSS' כפי שהם נקראים בעבר, מאפשרים לכם להשתמש ביכולות של שדרוגים של WebGL עם תוכן ה-DOM. מאחר שבהטמעה הנוכחית, ה-shaders שבהם נעשה שימוש הם כמעט זהים לאלה של WebGL, צריך לעשות צעד אחורה ולהבין קצת את המונחים של תלת-ממד ואת צינור עיבוד הנתונים של הגרפיקה.
צירפתי גרסה מוקלטת של מצגת שהעברתי לאחרונה ב-LondonJS. בסרטון הזה אני מסביר על מונחי התלת-ממד שצריך להכיר, על סוגי המשתנים השונים שתראו ועל הדרכים שבהן אפשר להתחיל לעבוד עם מסננים מותאמים אישית כבר היום. כדאי גם להוריד את השקפים כדי שתוכלו לנסות את הדמואים בעצמכם.
מבוא לשכבות שידור (shaders)
כתבתי בעבר מבוא לשימרים, שמספק פירוט טוב של מהם שימרים ואיך אפשר להשתמש בהם מנקודת מבט של WebGL. אם זו הפעם הראשונה שאתם עובדים עם שיבושים, מומלץ לקרוא את המאמר הזה לפני שממשיכים הלאה, כי הרבה מהמושגים והשפה של המסננים המותאמים אישית מבוססים על המונחים הקיימים של שיבושי WebGL.
אז בואו נפעיל את המסננים בהתאמה אישית ונמשיך הלאה.
הפעלת מסננים בהתאמה אישית
מסננים מותאמים אישית זמינים ב-Chrome וב-Canary, וגם ב-Chrome ל-Android. פשוט עוברים אל about:flags
ומחפשים את 'CSS Shaders', מפעילים אותם ומפעילים מחדש את הדפדפן. עכשיו אפשר להתחיל.
התחביר
מסננים מותאמים אישית מוסיפים לקבוצת המסננים שכבר אפשר להחיל על רכיבי ה-DOM, כמו blur
או sepia
. אריק בודלמן (Eric Bidelman) כתב כלי נהדר לניסוי לצורך כך, שכדאי לבדוק.
כדי להחיל מסנן מותאם אישית על רכיב DOM, משתמשים בתחביר הבא:
.customShader {
-webkit-filter:
custom(
url(vertexshader.vert)
mix(url(fragment.frag) normal source-atop),
/* Row, columns - the vertices are made automatically */
4 5,
/* We set uniforms; we can't set attributes */
time 0)
}
כאן אפשר לראות שאנחנו מגדירים את ה-vertex shader וה-fragment shader, את מספר השורות והעמודות שרוצים לפצל את רכיב ה-DOM שלהם, ואז את כל המשתנים האחידים שרוצים להעביר.
חשוב לציין שאנחנו משתמשים בפונקציה mix()
סביב שפתח הפירור עם מצב ערבוב (normal
) ומצב מורכב (source-atop
). נבחן את שפתח הפירור עצמו כדי להבין למה אנחנו בכלל צריכים פונקציית mix()
.
דחיפת פיקסלים
אם אתם מכירים את ה-shaders של WebGL, תבחינו שהדברים שונים במקצת במסננים מותאמים אישית. ראשית, אנחנו לא יוצרים את הטקסטורות שבהן משתמש ה-fragment shader כדי למלא את הפיקסלים. במקום זאת, תוכן ה-DOM שאליו מיושם המסנן ממופה למרקם באופן אוטומטי. המשמעות של זה היא שני דברים:
- מטעמי אבטחה, אנחנו לא יכולים לשלוח שאילתות לגבי ערכי צבע של פיקסלים ספציפיים של המרקם של DOM
- אנחנו לא מגדירים (לפחות בהטמעות הנוכחיות) את צבע הפיקסל הסופי בעצמנו, כלומר
gl_FragColor
אסור. במקום זאת, ההנחה היא שתרצו להציג את תוכן ה-DOM, ותוכלו לבצע מניפולציה על הפיקסלים שלו באופן עקיף באמצעותcss_ColorMatrix
ו-css_MixColor
.
המשמעות היא שהקוד Hello World של שגיאות הפירור נראה כך:
void main() {
css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
css_MixColor = vec4(0.0, 0.0, 0.0, 0.0);
// umm, where did gl_FragColor go?
}
כל פיקסל של תוכן ה-DOM מוכפל ב-css_ColorMatrix
, שבמקרה שלמעלה לא עושה כלום כי הוא מטריצת הזהות ולא משנה אף אחד מערכי ה-RGBA. אם נרצה, למשל, להשאיר רק את הערכים האדומים, נשתמש ב-css_ColorMatrix
כך:
// keep only red and alpha
css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0);
אפשר לראות שכאשר מכפילים את ערכי הפיקסלים ב-4D (RGBA) במטריצה, מקבלים ערך פיקסל שעבר מניפולציה בצד השני, ובמקרה הזה ערך שבו הרכיבים הירוקים והכחולים מוגדרים לאפס.
הערך css_MixColor
משמש בעיקר כצבע בסיס שרוצים לשלב עם תוכן ה-DOM. המיקס מתבצע באמצעות שיטות המיזוג שאתם מכירים מחבילות הגרפיקה: שכבת-על, מסך, דילוג צבע, תאורה קשה וכו'.
יש הרבה דרכים שבהן שני המשתנים האלה יכולים לשנות את הפיקסלים. מומלץ לעיין במפרט של אפקטים של מסננים כדי להבין טוב יותר את האינטראקציה בין המצבים 'מיזוג' ו'שילוב'.
יצירת קודקודים
ב-WebGL אנחנו אחראים באופן מלא ליצירת הנקודות התלת-ממדיות של המרקם, אבל במסננים מותאמים אישית כל מה שצריך לעשות הוא לציין את מספר השורות והעמודות הרצוי, והדפדפן יפרק באופן אוטומטי את תוכן ה-DOM למקבץ של משולשים:
לאחר מכן, כל אחד מהקודקודים האלה מועבר ל-vertex shader שלנו לצורך מניפולציה, וכך אנחנו יכולים להתחיל להזיז אותם במרחב תלת-ממדי לפי הצורך. בקרוב תהיה לך אפשרות ליצור אפקטים נהדרים!
אנימציה באמצעות Shaders
הוספת אנימציות לשיבושים (shaders) היא מה שמאפשר להם להיות מהנים ומעניינים. כדי לעשות זאת, פשוט משתמשים במעבר (או באנימציה) ב-CSS כדי לעדכן ערכים אחידים:
.shader {
/* transition on the filter property */
-webkit-transition: -webkit-filter 2500ms ease-out;
-webkit-filter: custom(
url(vshader.vert)
mix(url(fshader.frag) normal source-atop),
1 1,
time 0);
}
.shader:hover {
-webkit-filter: custom(
url(vshader.vert)
mix(url(fshader.frag) normal source-atop),
1 1,
time 1);
}
מה שחשוב לשים לב אליו בקוד שלמעלה הוא שהזמן יתארך מ-0
ל-1
במהלך המעבר. בתוך ה-shader אפשר להצהיר על המשתנה המאונח time
ולהשתמש בערך הנוכחי שלו:
uniform float time;
uniform mat4 u_projectionMatrix;
attribute vec4 a_position;
void main() {
// copy a_position to position - attributes are read only!
vec4 position = a_position;
// use our time uniform from the CSS declaration
position.x += time;
gl_Position = u_projectionMatrix * position;
}
מתחילים לשחק!
פילטרים מותאמים אישית הם כלי נהדר ליצירת אפקטים מדהימים, שקשה (ובמקרים מסוימים בלתי אפשרי) ליצור בלעדיהם. אנחנו עדיין בתחילת הדרך, והדברים משתנים לא מעט, אבל הוספת התכונות האלה תוסיף קצת עניין לפרויקטים שלכם, אז למה לא לנסות?