مقدمه ای بر فیلترهای سفارشی (با نام مستعار CSS Shaders)

فیلترهای سفارشی یا همان طور که قبلاً به آنها سایه زن CSS گفته می شد، به شما امکان می دهد از قدرت سایه زن های WebGL با محتوای DOM خود استفاده کنید. از آنجایی که در پیاده‌سازی فعلی، سایه‌زن‌های استفاده‌شده تقریباً مشابه آن‌هایی هستند که در WebGL هستند، باید یک قدم به عقب برگردید و برخی اصطلاحات سه بعدی و کمی خط لوله گرافیکی را درک کنید.

من یک نسخه ضبط شده از ارائه ای را که اخیراً به LondonJS تحویل داده ام اضافه کرده ام. در ویدیوی من مروری بر اصطلاحات سه بعدی دارم که باید بدانید، انواع متغیرهای مختلفی که با آن ها روبرو خواهید شد، و چگونه می توانید امروز با فیلترهای سفارشی شروع به بازی کنید. همچنین باید اسلایدها را بگیرید تا بتوانید خودتان با دموها بازی کنید.

معرفی Shaders

قبلاً مقدمه‌ای برای سایه‌زن‌ها نوشته‌ام که به شما توضیح خوبی درباره اینکه سایه‌زن‌ها چیست و چگونه می‌توانید از آنها از دیدگاه WebGL استفاده کنید، می‌دهد. اگر هرگز با سایه‌زن‌ها سروکار نداشته‌اید، قبل از اینکه خیلی بیشتر بروید، خواندن آن ضروری است، زیرا بسیاری از مفاهیم فیلترهای سفارشی و زبان وابسته به اصطلاحات سایه‌زن WebGL موجود است.

بنابراین با این گفته، اجازه دهید فیلترهای سفارشی را فعال کرده و شخم بزنیم!

فعال کردن فیلترهای سفارشی

فیلترهای سفارشی در Chrome و Canary و همچنین Chrome for Android در دسترس هستند. به سادگی به about:flags بروید و "CSS Shaders" را جستجو کنید، آنها را فعال کنید و مرورگر را مجددا راه اندازی کنید. حالا شما خوب هستید که بروید!

نحو

فیلترهای سفارشی مجموعه فیلترهایی را که می‌توانید از قبل روی عناصر DOM خود اعمال کنید، مانند blur یا sepia ، گسترش می‌یابد. اریک بیدلمن یک ابزار زمین بازی عالی برای آنها نوشت که باید آن را بررسی کنید.

برای اعمال یک فیلتر سفارشی به یک عنصر 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)
}

از اینجا خواهید دید که ما سایه بان های رأس و قطعه، تعداد سطرها و ستون هایی که می خواهیم عنصر DOM ما به آن ها تقسیم شود و سپس هر یونیفرمی که می خواهیم از آن عبور کنیم را اعلام می کنیم.

نکته پایانی که در اینجا باید به آن اشاره کرد این است که ما از تابع mix() در اطراف قطعه سایه زن با یک حالت ترکیبی ( normal ) و یک حالت ترکیبی ( source-atop ) استفاده می کنیم. بیایید نگاهی به خود شیدر فرگمنت بیندازیم تا ببینیم چرا حتی به یک تابع mix() نیاز داریم.

Pixel Pushing

اگر با سایه زن های WebGL آشنا هستید، متوجه خواهید شد که در فیلترهای سفارشی همه چیز کمی متفاوت است. برای نمونه، ما بافت(هایی) را که شیدر قطعه ما برای پرکردن پیکسل ها استفاده می کند، ایجاد نمی کنیم. در عوض محتوای DOM که دارای فیلتر اعمال شده است به طور خودکار به یک بافت نگاشت می شود و این به معنای دو چیز است:

  1. به دلایل امنیتی، نمی‌توانیم مقادیر رنگ پیکسل‌های تک تک بافت DOM را پرس و جو کنیم
  2. ما (حداقل در اجراهای فعلی) رنگ پیکسل نهایی را خودمان تنظیم نمی کنیم، یعنی 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 خود ترکیب کنید. اختلاط از طریق حالت‌های ترکیبی انجام می‌شود که از بسته‌های هنری با آن‌ها آشنا خواهید شد: روکش، صفحه، جاخالی رنگ، نور سخت و غیره.

راه های زیادی وجود دارد که این دو متغیر می توانند پیکسل ها را دستکاری کنند. شما باید مشخصات Filter Effects را بررسی کنید تا نحوه تعامل حالت های ترکیبی و ترکیبی را بهتر بشناسید.

ایجاد رأس

در WebGL ما مسئولیت کامل ایجاد نقاط سه بعدی مش خود را بر عهده می‌گیریم، اما در فیلترهای سفارشی تنها کاری که باید انجام دهید این است که تعداد سطرها و ستون‌هایی را که می‌خواهید مشخص کنید و مرورگر به طور خودکار محتوای DOM شما را به دسته‌ای از مثلث‌ها تقسیم می‌کند. :

ایجاد رأس
یک تصویر به سطرها و ستون ها تقسیم می شود

سپس هر یک از آن رئوس برای دستکاری به سایه‌زن رأس ما منتقل می‌شود و این بدان معناست که می‌توانیم آن‌ها را در فضای سه‌بعدی در صورت نیاز حرکت دهیم. زمان زیادی نیست که بتوانید جلوه های بسیار شگفت انگیزی بسازید!

افکت آکاردئونی
تصویری که توسط افکت آکاردئون تاب می‌خورد

متحرک سازی با 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 کاهش می یابد. در داخل سایه زن می توانیم 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;
}

بازی کن

بازی کردن با فیلترهای سفارشی بسیار سرگرم کننده است و جلوه های شگفت انگیزی که می توانید ایجاد کنید بدون آنها دشوار (و در برخی موارد غیرممکن) است. هنوز روزهای اولیه است، و همه چیز کمی در حال تغییر است، اما اضافه کردن آنها کمی به پروژه های شما نمایشی اضافه می کند، پس چرا آنها را راه ندهید؟

منابع اضافی