این ویدیو از کانال Branch Education رو میدیدم، دربارهی اینکه چطور یک مشت صفر و یک در نهایت تبدیل میشن به یک صحنهی سهبعدیِ واقعی تو بازیها. ویدیو از یک صحنهی ایستگاه قطار در Red Dead Redemption 2 شروع میکنه؛ صحنهای که فقط خودش حدود ۲.۱ میلیون vertex، حدود ۳.۵ میلیون triangle و نزدیک به هزار texture و materia مختلف داره.
💬 خلاصهی ایده اینه که GPU برای ساختن هر فریم، یک pipeline سهمرحلهای رو با سرعت وحشتناک تکرار میکنه:
1. Vertex Shading
2. Rasterization
3. Fragment Shading
یعنی اول مختصات سهبعدی اشیاء رو به تصویر دوبعدی دوربین تبدیل میکنه، بعد حساب میکنه هر مثلث روی کدوم پیکسلهای مانیتور میافته، و در نهایت رنگ، نور، سایه و بافت هر پیکسل رو محاسبه میکنه.
🗂 مرحلهی اول: Vertex Shading
هر مدل سهبعدی، حتی اگر گرد و نرم به نظر بیاد، در واقع از تعداد زیادی مثلثِ تخت ساخته شده. مثلا یک لوکوموتیو در همین صحنه حدود ۷۶۲ هزار مثلث و ۳۸۲ هزار vertex داره.
پس GPU اول بررسی میکنه کدوم اشیاء اصلا داخل میدان دید دوربین هستن. مثلا از ۱۱۰۰ مدل موجود در صحنه، شاید فقط ۶۰۰تاشون واقعا در کادر دیده بشن. بعد برای هر vertex یک سری تبدیل ماتریسی انجام میده:
از فضای مدل به فضای جهان
از فضای جهان به فضای دوربین
و در نهایت projection روی صفحهی دوبعدی نمایشگر
خروجی این مرحله اینه که GPU میفهمه هر نقطهی سهبعدی، روی صفحهی دوبعدی کجا باید دیده بشه و چقدر از دوربین فاصله داره.
📊 نکتهی جالب اینه که کارت گرافیکها دقیقا برای همین جنس محاسبات ساخته شدن. هزاران هستهی پردازشی موازی دارن و میتونن تریلیونها عملیات ضرب و جمع ماتریسی رو در ثانیه انجام بدن. برای همین میتونن میلیونها مثلث رو ظرف چند میلیثانیه پردازش کنن.
📈 مرحلهی دوم: Rasterization
بعد از اینکه مختصات دوبعدی رأسهای مثلثها مشخص شد، GPU باید بفهمه هر مثلث دقیقا روی کدوم پیکسلها میافته. مثلا در نمایشگر 4K، با ۸.۳ میلیون پیکسل طرفیم.
اینجا مفهوم fragment به وجود میاد؛ یعنی یک پیکسل یا نمونهی پیکسلی که ممکنه توسط یک مثلث پوشانده شده باشه و حالا باید برایش رنگ و نور محاسبه بشه.
مشکل مهم این مرحله اینه که خیلی از اشیاء پشت هم قرار گرفتن. مثلا بدنهی قطار جلوی ساختمان ایستگاه رو گرفته، یا پشتِ خود قطار نباید دیده بشه. برای حل این مسئله از Z-Buffer استفاده میشه.
اینجا Z-Buffer برای هر پیکسل یک عدد نگه میداره: فاصلهی نزدیکترین شیء تا دوربین. اگر یک مثلث جدید بخواد روی همون پیکسل کشیده بشه، GPU عمقش رو مقایسه میکنه. اگر نزدیکتر بود، جای قبلی رو میگیره؛ اگر دورتر بود، دور ریخته میشه.
💬 یک مسئلهی دیگر هم لبههای دندانهداره. چون مثلثها گاهی مورب از وسط پیکسلها رد میشن، تصویر میتونه jagged بشه. برای نرم کردن این لبهها از anti-aliasing استفاده میشه؛ مثلا در SSAA داخل هر پیکسل چند نمونه گرفته میشه و بر اساس اینکه چه درصدی از پیکسل توسط مثلث پوشانده شده، رنگ نهایی محاسبه میشه.
و نکتهی دیوانهکننده اینه که همهی این مراحل برای هر فریم از اول تکرار میشن. اگر بازی ۱۲۰ فریم بر ثانیه اجرا بشه، GPU فقط حدود ۸.۳ میلیثانیه برای ساختن هر فریم وقت داره.
🖼 مرحلهی سوم: Fragment Shading
تا اینجا فقط فهمیدیم هر شیء کجای تصویر قرار میگیره. اما تصویر هنوز واقعی نیست. حالا باید مشخص بشه هر پیکسل چه رنگی داشته باشه، چقدر روشن باشه، چقدر سایه بخوره، نور از کجا بهش برخورد کنه و آیا بازتاب خاصی دارد یا نه.
برای نورپردازی، GPU جهت تابش نور و جهت سطح رو مقایسه میکنه. هر مثلث یک بردار عمود بر سطح داره که بهش surface normal میگن. بعد زاویهی بین نور و این نرمال محاسبه میشه. اگر سطح مستقیم رو به نور باشه، روشنتره؛ اگر زاویهدار یا پشت به نور باشه، تاریکتر میشه.
برای اینکه بخشهای پشت به نور کاملا سیاه مطلق نشن، یک مقدار ambient light شبیه اپسیلون به معادله هم اضافه میشه؛ یعنی روشنایی محیطی. روزها بیشتره، شبها کمتر.
🔻 اما اگر برای هر مثلث فقط یک normal داشته باشیم، سطحهای منحنی زشت و چندضلعی دیده میشن. راهحل اینه که برای هر vertex یک normal جدا در نظر گرفته میشه و بعد بین این نرمالها interpolation (یعنی درونیابی و محاسبهی مقادیر میانی بین چند مقدار) انجام میدن. نتیجه اینه که نور روی سطح بهصورت نرم و پیوسته تغییر میکنه و مثلا بدنهی استوانهای قطار واقعا گرد به نظر میاد، نه تکهتکه.
🤖 بخش آخر و مدرن ماجرا: Ray Tracing و DLSS
ویدیو بعدش توضیح میده که تکنیکهای جدیدتر مثل Ray Tracing جایگزین کامل pipeline قدیمی نشدن، بلکه بیشتر روی اون سوار شدن. بازی هنوز هندسهی اصلی صحنه رو با همون روش vertex shading و rasterization میسازه، ولی برای سایههای نرمتر، بازتابهای واقعیتر و مسیر نور دقیقتر از ray tracing کمک میگیره.
بعد هم DLSS وارد میشه؛ یک سیستم مبتنی بر شبکهی عصبی که تصویر کمرزولوشنتر رو میگیره و با کمک AI به تصویر 4K نزدیک میکنه. یعنی به جای اینکه همهچیز از اول با هزینهی کامل 4K رندر بشه، بخشی از کار با هوش مصنوعی upscaling میشه.
📑 کارت گرافیکهای جدید هم عملا سه نوع هستهی تخصصی دارن:
هستههای CUDA یا shading برای pipeline اصلی
هستههای RT برای ray tracing
هستههای Tensor برای AI و DLSS
همین تقسیم کار باعث میشه یک فریم سنگین 4K در کمتر از ۱۰ میلیثانیه ساخته بشه؛ در حالی که اگر همهی کار فقط روی هستههای معمولی میافتاد، چند برابر کندتر میشد.
👥 جمعبندی
چیزی که از بیرون شبیه یک تصویر ساده در بازی میبینیم، پشت صحنهاش یک زنجیرهی عظیم از جبر خطی، هندسه، فیزیک نور، محاسبات موازی و هوش مصنوعیه. هر فریم بازی در واقع نتیجهی میلیونها مثلث، میلیونها پیکسل، چندین بافر عمق، محاسبهی نور و سایه، تکسچر، anti-aliasing، و گاهی ray tracing و DLSS هست.
خلاصه اینکه گرافیک بازیها فقط کشیدن تصویر نیست، یک کارخانهی محاسباتی فوقالعاده سریع و دقیق هست که در هر ثانیه دهها یا صدها بار از صفر تصویر جهان بازی رو دوباره میسازه.
🔗 https://www.youtube.com/watch?v=C8YtdC8mxTU
✈️ @Mirage_Notes