BOB
چهارشنبه 30 اسفند 1391, 13:58 عصر
نرمافزار Pixel Bender (http://en.wikipedia.org/wiki/Adobe_Pixel_Bender) یکی از زیرمجموعههای بسته نرمافزاری CS است که توسط شرکت Adobe ارائه شده و هدف از ارائه این آن، فراهم کردن امکان طراحی Pixel Shader های سفارشی، توسط برنامهنویسان میباشد.
در حال حاضر جلوهها و فیلترهایی که در این محیط طراحی میشوند، در برنامههای Flash و After Effects قابل استفاده هستند.
برای فهم بهتر این مسئله که برنامه Pixel Bender دقیقا چه کاری انجام میدهد، باید با مفهوم Shader (http://en.wikipedia.org/wiki/Shader_%28computer_science%29) و Pixel Shader (http://en.wikipedia.org/wiki/Pixel_shader) آشنا شوید.
یک Shader، عبارت است از مجموعهای از دستورالعملهای سطح پایین که به منظور محاسبه سریع و بلادرنگ Render تصاویر، در واحد پردازش گرافیکی سیستم (GPU) طراحی شده است. همان طور که میدانید GPU، همان پردازنده سختافزاری فوق سریعی است که در کارت گرافیک سیستم تعبیه شده است.
به عبارتی، Shader ها برای اجرای مستقیم و بدون واسطه بر روی پردازنده کارت گرافیک سیستم طراحی میشوند. در واقع این همان کاری است که در کتابخانههای OpenGL و Direct3D هم انجام میشود. (مبحث طراحی Shader، جزء داغترین مباحث صنعت طراحی بازیهای کامپیوتری است)
Shader ها به سه دسته Vertex Shader، Geometry Shader و Pixel Shader تقسیم میشوند که دسته آخر، برای محاسبه Render گرافیکی به صورت پیکسل به پیکسل طراحی شدهاند.
هر Pixel Shader شامل مجموعهای از دستورالعملهای سطح Kernel میباشد که به ازای هر پیکسل موجود در تصویر ورودی، یک بار اجرا شده و مشخصات پیکسل تصویر خروجی (تصویر Render شده) را محاسبه میکند. از آنجا که قرار است دستورات پردازشی این توابع، به دفعات و بر روی GPU اجرا شوند، معمولا از زبانهای سطح پایینی مانند اسمبلی (در گذشته) و زبان C (در حال حاضر) استفاده میشود.
جالب است که بدانید عبارت Shader، اولین بار در سال 1989 و در رابطه با نرمافزار RenderMan از استودیوی پیکسار (PIXAR) که به منظور render تصاویر یک دوربین طراحی شده بود، مورد استفاده قرار گرفت. همچنین اولین استفاده حقیقی از Shader ها نیز، به انیمیشن "داستان اسباببازی" (Toy Story) از این شرکت بر میگردد.
بگذریم،
تا به اینجا، مشخص شد که برنامه Pixel Bender به عنوان یک IDE برای طراحی توابع Shader در زبان C ارائه شده است. اما اجازه دهید بعد از ذکر این مقدمه، به طراحی اولین Shader بپردازیم:
طراحی Shader:
برنامه Pixel Bender را اجرا نموده و گزینه Create New Kernel را انتخاب کنید. با این کار قالب کلی یک تابع Shader به صورت زیر نمایش داده میشود:
<languageVersion : 1.0;>
kernel NewFilterName
< namespace : "Your Namespace";
vendor : "Your Vendor";
version : 1;
description : "your description";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
}
}
با ایجاد تمایز در رنگ کدهای این تابع، شرح هر بخش به صورت زیر است:
قسمت اول: انتصاب نام به تابع جدید و شروع کدنویسی.
قسمت دوم: تعریف Metadata برای معرفی تابع.
قسمت سوم: کد نویسی بدنه تابع.
در دو سطر اول، دو متغیر از نوع داده image4 و pixel4 برای دریافت تصویر ورودی (src) و محاسبه مقدار پیکسل خروجی (dst) مورد استفاده قرار گرفتهاند. عدد 4 در انتهای نوع داده آنها به معنی 4 کاناله (Red, Green, Blue, Alpha) بودن پیکسلهای آنها میباشد.
ضمنا در صورت نیاز به دریافت پارامتر ورودی از کاربر، نام و نوع آنها در همین قسمت تعریف میشود.
تابع evaluatePixel نیز، همان تابع اصلی محاسبه render پیکسلهای خروجی بوده (تمام عملیات پردازشی در بدنه این تابع تعریف میگردد) و به صورت خودکار اجرا میگردد.
دستور outCoord، مختصات پیکسل ورودی را محاسبه کرده و دستور sampleNearest نیز عملیات نمونه گیری از هر پیکسل را انجام میدهد. در نتیجه در دستور فوق، با انتصاب مقدار نمونهگیری شده به متغیر dst، مقدار هر پیکسل دقیقا به همان صورتی که هست به تصویر خروجی منتقل شده و هیچ تغییری در پیکسلهای تصویر ایجاد نمیشود.
با بررسی این دستور، مشخص میشود که برای اعمال هر نوع افکت در تصویر خروجی، باید مقدار نمونهگیری شده را پس از انجام تغییرات مورد نظر در مشخصههای red، green، blue و alpha، به متغیر خروجی که همان dst است انتصاب دهیم.
اساس طراحی یک Pixel Shader در برنامه Adobe Pixel Bender همین است:
دریافت مقدار اولیه پیکسل.
اعمال تغییرات در آن.
انتصاب به پیکسل خروجی.
طراحی بدنه تابع و در نهایت، استفاده از آن:
ابتدا سه بخش Pixel Shader را برای مشخص شدن نام تابع و Metadata آن به همراه بدنه خالی تابع تعریف میکنیم:
<languageVersion : 1.0;>
kernel FiltRGB
< namespace : "com.mshams";
vendor : "www.mshams.ir";
version : 1;
description : "first test.";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
}
}
برای اعمال هر گونه تغییر در مقدار پیکسلهای تصویر خروجی، پس از دریافت مشخصات پیکسل با تابع نمونه گیری (sample، sampleNearest یا sampleLinear)، تغییرات را اعمال نموده و آن را به متغیر خروجی انتصاب میدهیم.
اما مسلما یکی از مهمترین اصول طراحی یک فیلتر، امکان تغییر پارامترهای محاسباتی توسط کاربر است. برای تعریف یک پارامتر ورودی در برنامه Pixel Bender از قالب زیر استفاده میشود:
parameter datatype paramName
<
minValue: n; maxValue: n; defaultValue: n;
>;
به عنوان مثال بنده در این مثال، چهار پارامتر ورودی از نوع float، برای تعیین مقادیر مشخصههای قرمز، سبز، آبی و درصد Grayscale تعریف میکنم. پس از تعریف پارامترهای ورودی و اجرای تابع با استفاده از گزینه Build and Run در برنامه Pixel Bender، چهار Scrollbar در سمت راست برنامه ظاهر شده و امکان تغییر مقدار پارامترها فراهم میگردد. با تغییر هر scroll، مقدار جدید به صورت بلادرنگ، در تابع اعمال شده و تصویر خروجی render میشود.
parameter float red
<
minValue:1.0; maxValue:255.0; defaultValue:1.0;
>;
parameter float green
<
minValue:1.0; maxValue:255.0; defaultValue:1.0;
>;
parameter float blue
<
minValue:1.0; maxValue:255.0; defaultValue:1.0;
>;
parameter float grayPercent
<
minValue:0.0; maxValue:100.0; defaultValue:0.0;
>;
اکنون در بدنه تابع، متغیر جدیدی از نوع pixel4 تعریف نموده و مقدار پیکسل نمونهگیری شده را به آن انتصاب میدهیم. سپس تمام تغییرات مورد نظر را بر روی این متغیر اعمال نموده و در نهایت آن را به متغیر خروجی dst انتصاب میدهیم.
هدف از طراحی فیلتر مورد نظر ما، دریافت مقادیر مشخصههای Red، Green، Blue و اعمال مستقیم آنها به پیکسل خروجی است. همچنین با تغییر متغیر grayPercent، درصد Grayscale تصویر تعیین خواهد شد.
pixel4 p = sampleNearest(src, outCoord());
float sum = (p.r * red + p.g * green + p.b * blue) / 3.0;
float gp = sum * grayPercent / 100.0;
p.r = p.r * red * (100.0 - grayPercent) /100.0 + gp;
p.g = p.g * green * (100.0 - grayPercent) /100.0 + gp;
p.b = p.b * blue * (100.0 - grayPercent) /100.0 + gp;
dst = p;
به همین سادگی، یک افکت Pixel Shader ساخته میشود. با انتخاب گزینه Build and Run، افکت کامپایل شده و به صورت بلادرنگ قابل استفاده میشود.
پیشنهاد میکنم برای مطالعه بیشتر در مورد دستورات قابل استفاده در این IDE، به راهنمای Pixel Bender Developer Guide در مجموعه Adobe Creative Suite مراجعه کنید. با استفاده از دستورات پیچیدهتر و محاسبات بیشتر، افکتهای بسیار پیچیدهای را میتوان به صورت بلادرنگ در برنامه Adobe Pixel Bender طراحی نمود.
برای استفاده از این افکت در برنامه Flash، باید از گزینه Export for FlashPlayer در منوی فایل استفاده نموده و فایلی با پسوند *.PBJ ایجاد کنید.
حال طریقه بارگذاری این فیلتر با استفاده از ActionScript 3 و شیوه مقدار دهی به پارامترهای ورودی آن مورد بررسی قرار میگیرد.
اکنون به بررسی شیوه بارگذاری فیلتر با استفاده از ActionScript 3 و مقدار دهی به پارامترهای ورودی آن میپردازیم.
مراحل کلی انجام این عملیات در AS3 به صورت زیر است:
تعریف یک شئ URLLoader با نوع داده باینری و بارگذاری فایل pbj.
تعریف یک شئ از کلاس Shader و دریافت داده از خروجی مرحله قبل.
مقدار دهی به پارامترهای فیلتر به صورت دلخواه.
تعریف یک شئ از کلاس ShaderFilter و دریافت متغیر Shader.
انتصاب متغیر ShaderFilter به خصیصه filters از DisplayObject مورد نظر.
در این مثال، ابتدا یک تصویر دلخواه به فلش وارد نموده و سپس یک MovieClip برای آن درست نموده و با نام img در stage قرار میدهیم.
سپس چهار کامپوننت Slider به صفحه اضافه نموده و نام آنها را به صورت slider_gp، slider_r، slider_g و slider_b تعیین میکنیم. محدوده Slider های مقادیر rgb از 1 تا 255 بوده و Slider مربوط به درصد Grayscale نیز از 1 تا 100 میباشد. (مشابه همان محدودهای که در Pixel Bender برای پارامترهای تعریف شد)
کدنویسی:
import flash.display.*;
import flash.events.*;
import flash.filters.*;
var loader:URLLoader;
var shader:Shader;
var filter:ShaderFilter;
slider_gp.visible = false;
slider_r.visible = false;
slider_g.visible = false;
slider_b.visible = false;
slider_gp.addEventListener(Event.CHANGE, onSlider);
slider_r.addEventListener(Event.CHANGE, onSlider);
slider_g.addEventListener(Event.CHANGE, onSlider);
slider_b.addEventListener(Event.CHANGE, onSlider);
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("msh_FiltRGB.pbj"));
function onComplete(e:Event) {
shader = new Shader(loader.data);
slider_gp.visible = true;
slider_r.visible = true;
slider_g.visible = true;
slider_b.visible = true;
}
function onSlider(e:Event) {
shader.data.grayPercent.value = [slider_gp.value];
shader.data.red.value = [slider_r.value];
shader.data.green.value = [slider_g.value];
shader.data.blue.value = [slider_b.value];
filter = new ShaderFilter(shader);
img.filters = [filter];
}
با تغییر مقدار Slider ها، مقدار پارمترهای فیلتر تغییر کرده و به صورت بلادرنگ به شئ نمایشی مورد نظر اعمال میشود.
در حال حاضر جلوهها و فیلترهایی که در این محیط طراحی میشوند، در برنامههای Flash و After Effects قابل استفاده هستند.
برای فهم بهتر این مسئله که برنامه Pixel Bender دقیقا چه کاری انجام میدهد، باید با مفهوم Shader (http://en.wikipedia.org/wiki/Shader_%28computer_science%29) و Pixel Shader (http://en.wikipedia.org/wiki/Pixel_shader) آشنا شوید.
یک Shader، عبارت است از مجموعهای از دستورالعملهای سطح پایین که به منظور محاسبه سریع و بلادرنگ Render تصاویر، در واحد پردازش گرافیکی سیستم (GPU) طراحی شده است. همان طور که میدانید GPU، همان پردازنده سختافزاری فوق سریعی است که در کارت گرافیک سیستم تعبیه شده است.
به عبارتی، Shader ها برای اجرای مستقیم و بدون واسطه بر روی پردازنده کارت گرافیک سیستم طراحی میشوند. در واقع این همان کاری است که در کتابخانههای OpenGL و Direct3D هم انجام میشود. (مبحث طراحی Shader، جزء داغترین مباحث صنعت طراحی بازیهای کامپیوتری است)
Shader ها به سه دسته Vertex Shader، Geometry Shader و Pixel Shader تقسیم میشوند که دسته آخر، برای محاسبه Render گرافیکی به صورت پیکسل به پیکسل طراحی شدهاند.
هر Pixel Shader شامل مجموعهای از دستورالعملهای سطح Kernel میباشد که به ازای هر پیکسل موجود در تصویر ورودی، یک بار اجرا شده و مشخصات پیکسل تصویر خروجی (تصویر Render شده) را محاسبه میکند. از آنجا که قرار است دستورات پردازشی این توابع، به دفعات و بر روی GPU اجرا شوند، معمولا از زبانهای سطح پایینی مانند اسمبلی (در گذشته) و زبان C (در حال حاضر) استفاده میشود.
جالب است که بدانید عبارت Shader، اولین بار در سال 1989 و در رابطه با نرمافزار RenderMan از استودیوی پیکسار (PIXAR) که به منظور render تصاویر یک دوربین طراحی شده بود، مورد استفاده قرار گرفت. همچنین اولین استفاده حقیقی از Shader ها نیز، به انیمیشن "داستان اسباببازی" (Toy Story) از این شرکت بر میگردد.
بگذریم،
تا به اینجا، مشخص شد که برنامه Pixel Bender به عنوان یک IDE برای طراحی توابع Shader در زبان C ارائه شده است. اما اجازه دهید بعد از ذکر این مقدمه، به طراحی اولین Shader بپردازیم:
طراحی Shader:
برنامه Pixel Bender را اجرا نموده و گزینه Create New Kernel را انتخاب کنید. با این کار قالب کلی یک تابع Shader به صورت زیر نمایش داده میشود:
<languageVersion : 1.0;>
kernel NewFilterName
< namespace : "Your Namespace";
vendor : "Your Vendor";
version : 1;
description : "your description";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
}
}
با ایجاد تمایز در رنگ کدهای این تابع، شرح هر بخش به صورت زیر است:
قسمت اول: انتصاب نام به تابع جدید و شروع کدنویسی.
قسمت دوم: تعریف Metadata برای معرفی تابع.
قسمت سوم: کد نویسی بدنه تابع.
در دو سطر اول، دو متغیر از نوع داده image4 و pixel4 برای دریافت تصویر ورودی (src) و محاسبه مقدار پیکسل خروجی (dst) مورد استفاده قرار گرفتهاند. عدد 4 در انتهای نوع داده آنها به معنی 4 کاناله (Red, Green, Blue, Alpha) بودن پیکسلهای آنها میباشد.
ضمنا در صورت نیاز به دریافت پارامتر ورودی از کاربر، نام و نوع آنها در همین قسمت تعریف میشود.
تابع evaluatePixel نیز، همان تابع اصلی محاسبه render پیکسلهای خروجی بوده (تمام عملیات پردازشی در بدنه این تابع تعریف میگردد) و به صورت خودکار اجرا میگردد.
دستور outCoord، مختصات پیکسل ورودی را محاسبه کرده و دستور sampleNearest نیز عملیات نمونه گیری از هر پیکسل را انجام میدهد. در نتیجه در دستور فوق، با انتصاب مقدار نمونهگیری شده به متغیر dst، مقدار هر پیکسل دقیقا به همان صورتی که هست به تصویر خروجی منتقل شده و هیچ تغییری در پیکسلهای تصویر ایجاد نمیشود.
با بررسی این دستور، مشخص میشود که برای اعمال هر نوع افکت در تصویر خروجی، باید مقدار نمونهگیری شده را پس از انجام تغییرات مورد نظر در مشخصههای red، green، blue و alpha، به متغیر خروجی که همان dst است انتصاب دهیم.
اساس طراحی یک Pixel Shader در برنامه Adobe Pixel Bender همین است:
دریافت مقدار اولیه پیکسل.
اعمال تغییرات در آن.
انتصاب به پیکسل خروجی.
طراحی بدنه تابع و در نهایت، استفاده از آن:
ابتدا سه بخش Pixel Shader را برای مشخص شدن نام تابع و Metadata آن به همراه بدنه خالی تابع تعریف میکنیم:
<languageVersion : 1.0;>
kernel FiltRGB
< namespace : "com.mshams";
vendor : "www.mshams.ir";
version : 1;
description : "first test.";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
}
}
برای اعمال هر گونه تغییر در مقدار پیکسلهای تصویر خروجی، پس از دریافت مشخصات پیکسل با تابع نمونه گیری (sample، sampleNearest یا sampleLinear)، تغییرات را اعمال نموده و آن را به متغیر خروجی انتصاب میدهیم.
اما مسلما یکی از مهمترین اصول طراحی یک فیلتر، امکان تغییر پارامترهای محاسباتی توسط کاربر است. برای تعریف یک پارامتر ورودی در برنامه Pixel Bender از قالب زیر استفاده میشود:
parameter datatype paramName
<
minValue: n; maxValue: n; defaultValue: n;
>;
به عنوان مثال بنده در این مثال، چهار پارامتر ورودی از نوع float، برای تعیین مقادیر مشخصههای قرمز، سبز، آبی و درصد Grayscale تعریف میکنم. پس از تعریف پارامترهای ورودی و اجرای تابع با استفاده از گزینه Build and Run در برنامه Pixel Bender، چهار Scrollbar در سمت راست برنامه ظاهر شده و امکان تغییر مقدار پارامترها فراهم میگردد. با تغییر هر scroll، مقدار جدید به صورت بلادرنگ، در تابع اعمال شده و تصویر خروجی render میشود.
parameter float red
<
minValue:1.0; maxValue:255.0; defaultValue:1.0;
>;
parameter float green
<
minValue:1.0; maxValue:255.0; defaultValue:1.0;
>;
parameter float blue
<
minValue:1.0; maxValue:255.0; defaultValue:1.0;
>;
parameter float grayPercent
<
minValue:0.0; maxValue:100.0; defaultValue:0.0;
>;
اکنون در بدنه تابع، متغیر جدیدی از نوع pixel4 تعریف نموده و مقدار پیکسل نمونهگیری شده را به آن انتصاب میدهیم. سپس تمام تغییرات مورد نظر را بر روی این متغیر اعمال نموده و در نهایت آن را به متغیر خروجی dst انتصاب میدهیم.
هدف از طراحی فیلتر مورد نظر ما، دریافت مقادیر مشخصههای Red، Green، Blue و اعمال مستقیم آنها به پیکسل خروجی است. همچنین با تغییر متغیر grayPercent، درصد Grayscale تصویر تعیین خواهد شد.
pixel4 p = sampleNearest(src, outCoord());
float sum = (p.r * red + p.g * green + p.b * blue) / 3.0;
float gp = sum * grayPercent / 100.0;
p.r = p.r * red * (100.0 - grayPercent) /100.0 + gp;
p.g = p.g * green * (100.0 - grayPercent) /100.0 + gp;
p.b = p.b * blue * (100.0 - grayPercent) /100.0 + gp;
dst = p;
به همین سادگی، یک افکت Pixel Shader ساخته میشود. با انتخاب گزینه Build and Run، افکت کامپایل شده و به صورت بلادرنگ قابل استفاده میشود.
پیشنهاد میکنم برای مطالعه بیشتر در مورد دستورات قابل استفاده در این IDE، به راهنمای Pixel Bender Developer Guide در مجموعه Adobe Creative Suite مراجعه کنید. با استفاده از دستورات پیچیدهتر و محاسبات بیشتر، افکتهای بسیار پیچیدهای را میتوان به صورت بلادرنگ در برنامه Adobe Pixel Bender طراحی نمود.
برای استفاده از این افکت در برنامه Flash، باید از گزینه Export for FlashPlayer در منوی فایل استفاده نموده و فایلی با پسوند *.PBJ ایجاد کنید.
حال طریقه بارگذاری این فیلتر با استفاده از ActionScript 3 و شیوه مقدار دهی به پارامترهای ورودی آن مورد بررسی قرار میگیرد.
اکنون به بررسی شیوه بارگذاری فیلتر با استفاده از ActionScript 3 و مقدار دهی به پارامترهای ورودی آن میپردازیم.
مراحل کلی انجام این عملیات در AS3 به صورت زیر است:
تعریف یک شئ URLLoader با نوع داده باینری و بارگذاری فایل pbj.
تعریف یک شئ از کلاس Shader و دریافت داده از خروجی مرحله قبل.
مقدار دهی به پارامترهای فیلتر به صورت دلخواه.
تعریف یک شئ از کلاس ShaderFilter و دریافت متغیر Shader.
انتصاب متغیر ShaderFilter به خصیصه filters از DisplayObject مورد نظر.
در این مثال، ابتدا یک تصویر دلخواه به فلش وارد نموده و سپس یک MovieClip برای آن درست نموده و با نام img در stage قرار میدهیم.
سپس چهار کامپوننت Slider به صفحه اضافه نموده و نام آنها را به صورت slider_gp، slider_r، slider_g و slider_b تعیین میکنیم. محدوده Slider های مقادیر rgb از 1 تا 255 بوده و Slider مربوط به درصد Grayscale نیز از 1 تا 100 میباشد. (مشابه همان محدودهای که در Pixel Bender برای پارامترهای تعریف شد)
کدنویسی:
import flash.display.*;
import flash.events.*;
import flash.filters.*;
var loader:URLLoader;
var shader:Shader;
var filter:ShaderFilter;
slider_gp.visible = false;
slider_r.visible = false;
slider_g.visible = false;
slider_b.visible = false;
slider_gp.addEventListener(Event.CHANGE, onSlider);
slider_r.addEventListener(Event.CHANGE, onSlider);
slider_g.addEventListener(Event.CHANGE, onSlider);
slider_b.addEventListener(Event.CHANGE, onSlider);
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("msh_FiltRGB.pbj"));
function onComplete(e:Event) {
shader = new Shader(loader.data);
slider_gp.visible = true;
slider_r.visible = true;
slider_g.visible = true;
slider_b.visible = true;
}
function onSlider(e:Event) {
shader.data.grayPercent.value = [slider_gp.value];
shader.data.red.value = [slider_r.value];
shader.data.green.value = [slider_g.value];
shader.data.blue.value = [slider_b.value];
filter = new ShaderFilter(shader);
img.filters = [filter];
}
با تغییر مقدار Slider ها، مقدار پارمترهای فیلتر تغییر کرده و به صورت بلادرنگ به شئ نمایشی مورد نظر اعمال میشود.