یک مثلث بزرگ مشکی را تصور کنید که در یک صفحهی سفید از یک سمت به سمت دیگر در حرکت است. شدت نور سلولهای صفحهی نمایش باید به نرمی از سیاه به سفید منتقل شوند ولی چیزی که اغلب میبینیم این است که سلولها ناگهان بین این دو رنگ تغییر میکنند.
همین مشکل برای خط هم اتفاق میافتد و لبههای دندانه دار را در آن میبینیم. به صورت رسمی به این مشکل aliasing میگویند و کارهایی که برای بر طرف کردن آن انجام میشود را anti-aliasing میگویند.
در حوزه پردازش سیگنالهای دیجیتال (Digital signal processing) مبحث گستردهای در مورد نظریه نمونهگیری و فیلترینگ (Sampling Theory) وجود دارد که در اینجا فقط به صورت ساده و خلاصه آن را مطرح میکنیم.
عکاسی را به صورت فیزیکی میتوان به یک عمل نمونهگیری (Sampling) تشبیه کرد، نمونهگیری از نورهای یک صحنهی خاص در یک لحظهی خاص. این نمونه گیری میتواند به صورت آنالوگ و یا دیجیتال باشد. حتی ضبط کردن صوت هم نوعی نمونهگری است که میتواند به صورت آنالوگ یا دیجیتال صورت گیرد.
فرایند پرداخت (Rendering) یک تصویر از فضای سه بعدی ذاتا یک عمل نمونهگیری محسوب میشود. فرآیند به دست آوردن مقادیر رنگ پیکسلها از روی اشیا موجود در صحنهی سه بعدی و ساخت یک تصویر از آنها. در خط لولهی پرداخت (Rendering Pipeline) نمونهگیری عملا در مرحلهی Rasterisation انجام میشود.
وقتی بحث نمونهگری مطرح میشود باید به بازسازی (Reconstruction) نیز فکر کنیم، یعنی چطور دادههای نمونهگیری شده را دوباره به حالتی که بوده برگردانیم. مثلا در عکاسی دیجیتال نمایش عکس در صفحهی نمایش یا پخش دادههای صوتی در بلندگو. برای سادگی مطلب به چند نمونه سیگنال یک بعدی نگاه میکنیم، این مفاهیم به صورت طبیعی به ابعاد بالاتر نیز قابل گسترش است.
همانطور که در شکل بالا میبینیم یک سیگنال پیوسته در بازهای مشخص و هم اندازه نمونهگیری شده و به صورت گسسته در آمده. هدف از نمونهگیری بازنمایی اطلاعات به صورت دیجیتال است. در این فرآیند مقدار اطلاعات کاهش مییابد.
به هر حال سیگنال نمونهگیری شده باید به صورت اصلی خود بازسازی شود که از طریق فیلترینگ (filtering) میتوان آن را انجام داد.
هر زمان که نمونهگیری انجام شود ممکن است aliasing هم اتفاق بیافتد که باید برای به دست آوردن یک تصویر صاف و نرم با آن مقابله کرد.
یک نمونه کلاسیک از aliasing در فیلمهای وسترن قدیمی اتفاق میافتاد، زمانی که قطار به حرکت در میآمد و دوربین از چرخهای قطار تصویر میگرفت، چرخها یا به نظر خیلی کند میچرخیدند یا در خلاف جهت میچرخیدند یا کاملا بدون حرکت به نظر میرسیدند. برای اطلاع بیشتر در مورد temporal aliasing جستجو کنید.
نمونهی امروزی aliasing همان گوشههای تیز و دندانه دندانه در تصاویر سه بعدی است.
در واقع aliasing زمانی رخ میدهد که سیگنال مورد نظر با بسامد بسیار کمی نمونهگیری شده باشد.
در شکل زیر نقاط قرمز نقاط نمونهگیری شده است و خط چین سبز سیگنال بازسازی شده است که میبینیم تفاوت زیادی با سیگنال اصلی دارد.
برای آگاهی بیشتر از نظریه نمونهگیری و نحوهی بازسازی سیگنالها اینجا را ببینید.
یکی از مشکلات تصویر مثلثی که در ابتدا دیدیم کم بودن نرخ نمونه برداری بود. اولین تکنیکی که برای رفع این مشکل استفاده میشد Super Sample Anti-Aliasing یا SSAA بود که به صورت موقتی تصویر صحنه را با تفکیک پذیری بالاتری Render میکرد و بعد خروجی نهایی را کوچک میکرد و به حالت طبیعی در میآورد و نمایش میداد. این تفکیک پذیری بالاتر باعث میشد که مشکل لبههای دندانه دار حل شود اما به شدت باعث کاهش کارایی رندر میشد. بنابراین این تکنیک دورهی شهرت کوتاهی داشت اما باعث به وجود آمدن تکنیک دیگری شد که امروز مورد استفاده قرار میگیرد.
در مرحلهی Rasterisation راسهای یک شکل هندسی اولیه (عموما مثلث) گرفته شده و کل شکل به بخشهایی که به آنها fragment گفته میشود تبدیل و سپس تقسیم میشود. از آنجایی که این تبدیل هندسی روی اعداد float اتفاق میافتد، مقادیر fragment میتوانند هر عددی باشند ولی باید در محدودهی تفکیک پذیری پنجرهی نمایش باشند. اما نکته اینجاست که نگاشت یک به یکی بین fragment ها و پیکسل های تصویر وجود ندارد چون با جدولی از نقاط صحیح (Integer) رو به رو هستیم. بنابراین rasterizer باید راهی برای نگاشت صحیح بین fragmentها و پیکسلها پیدا کند.
در تصویر بالا پیکسلهای صفحه نمایش را میبینیم که مرکز هر پیکسل یک نقطه نمونهگیری را نشان میدهد، این نقاط برای تعیین این که آیا پیکسل توسط مثلث پوشیده شده یا نه استفاده میشوند. نقاط نمونهگیری قرمز توسط مثلث پوشش داده شدهاند و یک fragment برای آنها تولید خواهد شد. با وجود این که بخشی از بعضی پیکسلها توسط لبههای مثلث پوشیده شده اما چون نقطهی نمونهگیری را پوشش نداده هیچ fragment ای برای آنها تولید نشده است. تصویر زیر نسخه رندر شدهی مثلث را نشان میدهد.
به دلیل کم بودن تعداد پیکسلهای تصویر برخی پیکسلهایی که در طول لبههای مثلث هستند رندر نخواهند شد و برخی با یک تغییر کوچک رندر میشوند که این باعث ایجاد مشکل است.
می توانیم به جای در نظر گرفتن یک نقطه در مرکز هر پیکسل برای نمونهگیری، از چندین نقطه استفاده کنیم. استفاده از چند نقطهی نمونهگیری به این معنا است که بافر رنگ (Color Buffer) ما نیز به نسبت تعداد نقاطی که برای نمونهگیری در نظر میگیریم بزرگتر خواهد شد.
تصویر سمت چپ روش معمول برای تعیین پوشش یک پیکسل توسط مثلث را نشان میدهد. از آنجایی که نقطهی نمونهگیری آن پیکسل توسط مثلث پوشش داده نشده است هیچ fragment خاصی برای آن تولید نشده و fragment shader برای آن اجرا نمیشود. سمت راست استفاده از چند نقطهی نمونهگیری را میبینیم که از چهار نقطه، ۲تای آن توسط مثلث پوشش داده شدهاند.
تعداد نقاط نمونهگیری هر چندتا که بخواهیم میتواند باشد و هر چه تعداد نمونهگیری بیشتر باشد دقت پوشش آن پیکسل را بهتر نشان میدهد.
اینجا جایی است که روش Multi sampling جالب میشود. ما تعیین کردیم که ۲تا از زیر نمونهها
(sub-samples) توسط مثلث پوشش داده شدهاند، قدم بعدی این است که رنگ آن پیکسل خاص را تعیین کنیم. حدس اولیه ما این خواهد بود که fragment shader را برای هر زیر نمونهی پوشش داده شده اجرا کنیم و بعد به ازای هر پیکسل رنگ زیرنمونهها را میانگین بگیریم. در این حالت ما باید fragment shader را ۲ بار اجرا کنیم و نتیجه هر زیر نمونه را جدا ذخیره کنیم. خوشبختانه این کاری نیست که روش Multi sampling انجام میدهد چون به معنای این است که ما نیاز داریم تا fragment shader را به دفعات بسیار بیشتری نسبت به زمانی که Multi sampling نداریم اجرا کنیم که باعث کاهش شدید کارایی میشود.
روشی که MSAA واقعا کار میکند بدین صورت است که fragment shader را صرف نظر از این که چند زیر نمونه از آن توسط مثلث پوشش داده شده، برای هر پیکسل فقط یک بار اجرا میکند. ورودی fragment shader با توجه به مرکز هر پیکسل تعیین میشود و خروجی آن در هر زیر نمونهای که پوشش داده شده ذخیره میشود. زمانی که زیر نمونهها با رنگهای مثلث پر شدند، از رنگهای زیر نمونههای یک پیکسل میانگین میگیریم تا رنگ نهایی هر پیکسل تعیین شود. برای مثال در تصویر قبل چون فقط دو زیر نمونه از چهار زیر نمونه پوشش داده شدهاند، رنگ پیکسل از میانگین رنگ مثلث و دو زیر نمونهی دیگری که رنگی ندارند به دست میآید، در نتیجه رنگ آبی روشنتری است.
نتیجه، بافرِ رنگی است که لبههای اشکال در آن الگوی نرم تری خواهند داشت.
در اینجا هر پیکسل چهار زیر نمونه دارد که زیر نمونههایی که آبی هستند توسط مثلث پوشیده شدهاند و آنهایی که پوشش داده نشدهاند خاکستری هستند. در ناحیه درون مثلث به ازای هر پیکسل یک بار fragment shader اجرا خواهد شد و رنگ خروجی در همهی چهار زیر نمونه ذخیره میشود. اما در لبهها، مثلث همهی زیر نمونهها را پوشش نمیدهد بنابراین خروجی fragment shader فقط در برخی از آنها ذخیره میشود. مبتنی بر این که چه تعداد از زیر نمونهها پوشش داده شدهاند رنگ نهایی هر پیکسل تعیین و ذخیره میشود.
هر چه تعداد زیر نمونههایی که پوشش داده شدهاند بیشتر باشد پیکسل بیشتر به رنگ مثلث نزدیک خواهد شد و هر چه تعداد کمتری زیر نمونه پوشش داده شوند آن پیکسل رنگ کمتری از مثلث را به خود میگیرد. در شکل بالا میبینیم که لبههای تیز با رنگهای روشنتری از خود لبهها احاطه شدهاند که باعث میشود وقتی از فاصلهی بیشتر به لبهها نگاه میکنیم بسیار نرم تر دیده شوند.
چیزی که تا اینجا در مورد آن توضیح داده شد یک بررسی اجمالی در مورد Multi-sampling Anti-aliasing بود اما منطق اصلی درون rasterizer کمی پیچیدهتر از این است که برای درک بهتر مفاهیم ساده سازی شده. روش های پیشرفتهتر و پیچیدهتری نیز برای برخورد با aliasing وجود دارد که هر یک مزایا و معایب خود را دارند.
Real-Time Rendering, Tomas Möller, Eric Haines, Naty Hoffman
https://en.wikipedia.org/wiki/Anti-aliasing_filter
https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing
https://en.wikipedia.org/wiki/Supersampling
https://en.wikipedia.org/wiki/Multisample_anti-aliasing