بازی و سرگرمی

گیمولوژی | رسانه تحلیلی بازی سازی | شیدرنویسی در یونیتی (بخش اول)

در مقاله ی قبلی توضیحاتی درباره ی شیدر و عملکرد GPU و CPU و انواع شیدر را به صورت اجمالی بررسی کردیم.در این مقاله سعی داریم کمی بیشتر وارد جزئیات شویم تا درک بیشتری از لزوم استفاده ی شیدر داشته باشیم.

انواع شیدر:

2D Shaders

شیدرهای دوبعدی روی تصاویر عمل می کنند و در گرافیک کامپیوتری «تکسچر» (Texture) نیز نامیده می شوند. آنها ویژگی های پیکسل را تغییر می دهند. در حال حاضر تنها شیدر دوبعدی، شیدرهای پیکسلی هستند. 

Pixel Shaders

شما حتماً تجربه نقاشی با کامپیوتر را دارید و می دانید که در این فرایند شما یک دایره، یک مستطیل، یک خط، مثلث و یا هر شکلی را  که تجسم کنید می توانید ترسیم کنید. این فرایند بسیار شبیه به نوشتن یک نامه یا یک کتاب دستی است که شامل مجموعه دستورالعمل هایی است که یک کار را بعد از دیگری انجام می دهند.

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

چرا شیدر ها سریع هستند؟

برای پاسخ دادن به این سوال، بهتر است شگفتی های پردازش موازی را با ذکر یک مثال بررسی کنیم: تصور کنید CPU کامپیوتر شما به عنوان یک لوله صنعتی بزرگ و هر کار به عنوان چیزی است که از آن عبور می کند درست مثل یک Pipe Line.

برخی از وظایف بزرگ تر از دیگر وظایف هستند، به این معنی که آن ها نیاز به زمان و انرژی بیشتر برای مقابله با آن را دارند. بهتر است بگوییم که آن کار ها نیاز به قدرت پردازش بیشتری دارند.

به دلیل معماری کامپیوترها، هر یک از کارها باید یک بار در یک زمان انجام شود. کامپیوترهای مدرن معمولا دارای گروهی از چهار پردازنده هستند که مانند این لوله ها کار می کنند. تکمیل وظایف یکی پس از دیگری، برای روان نگه داشتن هر چیزی که در حال اجرا است کاربرد دارد. هر لوله به عنوان یک Thread شناخته شده است. بازی های ویدئویی و سایر برنامه های گرافیکی نیاز به قدرت پردازش بیش از برنامه های دیگر دارند و به دلیل محتوای گرافیکی، آنها باید تعداد زیادی از عملیات PixelbyPixel را انجام دهند. هر پیکسل روی صفحه نمایش باید محاسبه شود و در بازی های سه بعدی، شکل های هندسی و پرسپکتیو نیاز به محاسبه دارند.

بیایید به استعاره لوله ها و وظایف مان بازگردیم. هر پیکسل روی صفحه نمایش نشان دهنده ی یک کار ساده است. به طور جداگانه هر یک از کارهای پیکسل مسئله ای برای CPU نیست، اما (اینجا مشکل است) کار کوچک باید به هر پیکسل روی صفحه اعمال شود!

این بدان معنی است که در صفحه نمایش 800×600 قدیمی، 480،000 پیکسل باید در هر فریم پردازش شود که به معنی 14،400،000 محاسبات در ثانیه است. بله این مسئله به اندازه ای بزرگ است که میکروپروسسور را بیش از حد بارگذاری می کند. در یک صفحه نمایش شبکیه 2880×1800 مدرن که در محدوده 60 فریم در ثانیه اجرا می شود، محسابات تا 311،040،000 در ثانیه افزایش پیدا می کند. مهندسان گرافیک چگونه این مشکل را حل کردند؟

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

ریزپردازنده های کوچک را به عنوان یک جدول از لوله ها و داده های هر پیکسل را به عنوان توپ پینگ پنگ در نظر بگیرید.

14،400،000 توپ پینگ پنگ در یک ثانیه تقریبا می تواند هر لوله ای را مسدود کند. اما یک میز از 800×600 لوله کوچک دریافت 30 موج 480،000 پیکسل در ثانیه را می تواند به صورت روان رسیدگی کند. این در رزولوشن های بالاتر هم کار می کند. جریان سخت افزار موازی شما، می تواند بزرگ تر باشد.

یکی دیگر از ویژگی های فوق العاده قدرتمند GPU، توابع ریاضی خاصی است که از طریق سخت افزار شتاب پیدا می کند. عملیات ریاضی بسیار پیچیده به واسطه ی میکروچیپ ها به جای استفاده از نرم افزار به طور مستقیم حل می شود. این بدان معنی است که عملکردهای بی نظیر مثلثاتی و ماتریس سریع تر از برق می توانند محاسبه شوند.

 Pixel Shaders، که همچنین به عنوان Fragment Shaders شناخته می شوند، محاسبه رنگ و ویژگی های دیگر هر پیکسل را بر عهده دارند. ساده ترین نوع این شیدر، خروجی یک پیکسل صفحه نمایش، به عنوان یک رنگ است؛ شیدرهای پیچیده تر با چند ورودی/خروجی نیز امکان پذیر است. شیدرهای پیکسلی همیشه از یک رنگ یکسان، برای اعمال مقدار نوردهی، برای انجام Bump Mapping، سایه، برجستگی های ظریف، شفافیت و سایر پدیده ها استفاده می کنند. آن ها می توانند عمق فرگمنت (برای Zbuffering) یا خروجی بیش از یک رنگ را اگر چندین هدف رندر فعال باشند تغییر دهند. شیدر برای هر راس را Vertex Shader و برای هر پیکسل بافت را Fragment Shader می نامند.

به طور کلی حتی رسم یک مثلث دارای بافت (Textured Triangle) نیاز به یک شیدر دارد. در گرافیک سه بعدی، پیکسل شیدر به تنهایی نمی تواند جزئیات بسیار پیچیده ای اعمال کند، زیرا تنها در یک قطعه واحد (Single Fragment)، بدون اینکه از هندسه صحنه استفاده کند عمل می کند. با این حال، شیدرهای پیکسلی دانش هماهنگی صفحه نمایش را دارند و می توانند یک تکسچر را دریافت و به شیدر منتقل کنند. این تکنیک می تواند طیف گسترده ای از اثرات پس پردازش (Postprocessing) دو بعدی، مانند تاری یا تشخیص/تقویت لبه برای سایه های کارتونی را فعال کند. Pixel Shaders ممکن است در مراحل متوسط به هر یک از تصاویر دوبعدی یا بافت ها در خط لوله اعمال شود، در حالی که Vertex Shaders همیشه به یک صحنه ی سه بعدی نیاز دارد.

3D Shaders

شیدرهای سه بعدی بر روی مدل های سه بعدی یا هندسه های دیگر عمل می کنند اما ممکن است به رنگ ها و بافت های مورد استفاده برای طراحی مدل یا مش دسترسی پیدا کنند.

Vertex Shader

ورتکس شیدر به طور همزمان روی تک تک ورتکس ها کاری انجام می دهد. شیدر هیچ آگاهی از رئوس دیگر که شکل گرافیکی اولیه (Graphical Primitive) را تشکیل می دهند و هیچ نشانه از اینکه ورتکس متعلق به چه شکل اولیه ای (Primitive) بوده ندارد. برای هر راس ورودی، این شیدر خروجی یک راس است.

هر راس دارای مجموعه ای از ویژگی های ورودی تعریف شده توسط کاربر است. به عنوان مثال: موقعیت، بردار نرمال و مختصات بافت (Texture Position). شیدرهای ورتکس همچنین به متغیرهای یکنواخت دسترسی دارند که به عنوان متغیرهای جهانی (ReadOnly Global ) برای تمام رئوس در یک رسم صدا زده می شوند.

Geometry Shaders

شیدر های هندسی نسبتا نوع جدیدی از شیدر هستند که در Direct3D 10 و OpenGL 3.2 معرفی شدند. قبلا در OpenGL 2.0+ با استفاده از Extensionsها موجود بوده است. این نوع شیدر می تواند شکل های اولیه ی گرافیکی جدیدی مانند نقاط، خطوط و مثلث را از شکل های اولیه که به ابتدای خط لوله گرافیکی ارسال می شود، تولید کند.

برنامه های Geometry Shader پس از Vertex Shaders اجرا می شوند. آن ها به عنوان ورودی یک شکل اولیه، این امر را با اطلاعات مجاور ممکن می سازند. به عنوان مثال، زمانی که در مثلث عمل می کند، سه رأس؛ ورودی شیدر هندسی است. پس از آن شیدر می تواند صفات اولیه یا بیشتر را که شطرنجی شده اند و قطعات آنها (Fragments)، در نهایت به یک Pixel Shader منتقل  می شود را منتشر کند.

استفاده های معمول از یک شیدر هندسی شامل موارد زیر است:

 Point Sprite Generation, Geometry Tessellation, Shadow Volume Extrusion and Single Pass Rendering to a Cube Map

Tessellation shaders

همانطور که OpenGL 4.0 و Direct3D 11 به عنوان کلاس جدید شیدر نامیده می شوند، یک Tessellation Shader اضافه شده است. این دو مرحله جدید، شیدر را به مدل سنتی اضافه می کند: شیدرهای کنترل تسلیت (همچنین به عنوان شیدرهای بدنه Hull Shaders شناخته می شود) و شیدرهای ارزیابی تسلیت (همچنین به عنوان شیدر های دامنه Domain Shaders شناخته می شوند)، که به موجب آن می توانند مش های ساده تر را به مش های ظریف تر مطابق تابع ریاضی در زمان اجرا تقسیم کنند.

این نوع شیدر اجازه می دهد که وقتی اشیاء به دوربین نزدیک می شوند جزئیات دقیق تر باشد. یعنی پالیگان های بیشتر، در حالی که دورترها می توانند پالیگان های کمتری داشته باشند، اما با کیفیت قابل مقایسه هستند. همچنین می تواند پهنای باند شبکه را به طرز چشمگیری کاهش دهد و اجازه می دهد که شبکه ها در داخل دستگاه های شیدر به جای کم کردن نمونه هایی بسیار پیچیده از حافظه، یک لحظه تصحیح شوند.

بعضی از الگوریتم ها می توانند هر نوع نمونه ی بارز دلخواه را به نمایش بگذارند، در حالی که دیگران اجازه ی “اشاره کردن” در مش برای تعیین رأس ها و لبه ها مشخصی را دارند.

Primitive Shaders

معماری AMD Vega میکرو برای یک مرحله جدید شیدر/شیدر های اولیه اضافه شده است.

Compute Shaders به برنامه های گرافیکی محدود نمی شود، بلکه از منابع مشابه برای General Purpose GPU) GPGPU) استفاده می شود. آن ها ممکن است در خطوط گرافیکی مورد استفاده قرار گیرد. به عنوان مثال برای مراحل اضافی در انیمیشن یا الگوریتم های روشنایی مانند رندر پیش رونده (Tiled Forward Rendering). برخی از APIهای رندر اجازه می دهد که شیدر ها را محاسبه کنید تا به آسانی منابع داده ها را با خط لوله گرافیکی به اشتراک بگذارید.

هر بازی سازی که با انجین یونیتی کار می کند، با کمپوننت ها آشنا می باشد. کمپوننت چارچوبی است که از اجزای بسیار متفاوت تشکیل شده است.

هر شیئی که در بازی است، شامل تعدادی کمپوننت می باشد که روی نگاه و رفتارشان تأثیر می گذارد.
در حالی که اسکریپت ها چگونگی رفتار اشیا را تعیین می کنند، رندرها (Renderers) تصمیم می گیرند که چگونه آن ها را انجام دهند  و چگونه باید بر روی صفحه ظاهر شود. یونیتی دارای چندین رندر، بسته به نوع هدف است که سعی می کنیم آن ها را تجسم کنیم. هر مدل سه بعدی معمولاً دارای MeshRenderer است.

 هر شی باید تنها یک MeshRenderer داشته باشد اما خود MeshRenderer می تواند چندین متریال داشته باشد. به طور کلی هر متریال یک جلد برای یک شیدر است.

 درک تفاوت بین این کمپوننت ها برای درک اینکه شیدر ها چگونه کار می کنند ضروری است.

نمودار زیر به راحتی بیانگر سه نهاد مختلف است که در گردش کار (Workflow) یونیتی نقش اصلی را بازی می کند:

مدل های سه بعدی اساساً مجموعه ای از مختصات سه بعدی به نام رأس هستند. آن ها برای ایجاد مثلث ها (Triangles) به یکدیگر متصل شده اند. هر رأس می تواند چندین اطلاعات دیگر مانند رنگ ، جهت آن به سمت نقطه (نرمال) و برخی از مختصات برای ساخت بافت ها بر روی آن (به نام داده های UV) را داشته باشد.

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

مقالات مرتبط

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا