مقالات

آشنایی با Open VDB

مقاله ایی از سجاد ربیعی

آشنایی با Open VDB

آشنایی با Open VDB

آشنایی با Open VDB :

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

اما حقیقت این است که VDB (آشنایی با Open VDB) خیلی فراتر از این تعریفه و کاربردهای گسترده تری داره ، با توجه به اینکه من همیشه تاکیید میکنم که باید به جای حفظ کردن ، مفاهیم و اصول نرم افزار را یاد بگیریم ، بنابراین سعی میکنم در این مطلب هم در حد توانم توضیحاتی را ارائه بدم که شما را با ماهیت VDB و انواع کاربرد های آن آشنا کند و به واسطه اون خیلی از کارهایی را که با VDB در فیلم های آموزشی یا پروژه های مختلف انجام میدن براتون معنا پیدا بکنه.

یک مطلب مهم این که اگر چه در این مقاله VDB(آشنایی با Open VDB) رو از طریق نرم افزار Houdini بررسی میکنم ، اما باز خواندن آن برای کاربران سایر نرم افزار ها مانند مکس و مایا خالی از لطف نیست ، چون الان کم کم خیلی از برنامه ها دارن ابزارهای مختلف VDB رو به خودشون اضافه میکنند یا حداقل خیلی از موتور رندر ها امروزه امکان رندر مستقیم آبجکت های VDB رو دارند ، شاید یک روزی این امکان فقط در هودینی و رندرمن وجود داشت و بعدش در آرنولد ولی امروز بقیه انجین ها مانند Vray یا برنامه Clarisse هم از امکانات آن بهره میبرند.


آشنایی با VDB :

در حقیقت VDB یک کتابخانه اوپن سورس و رایگان برای زبان C++ میباشد که به کمک آن ما میتونیم روی انواع اطلاعات Volumetric و Voxel Base کار کنیم ، آنها را دستکاری کنیم و ذخیره کنیم. جالبه بدونید این تکنولوژی توسط کمپانی Dream Works برای تولید پروژه های فیلم و انیمیشن ساخته شد.

همانطور که گفتم و از اسم OpenVDB هم مشخصه ، این یک کتابخانه و کدهای سورس باز برای زبان C++ میباشد که هر شخص میتونه آنها را از سایت خود VDB دانلود کنه و به کمک آن ابزارهایی برای کار با عناصر Volumetric در نرم افزار خودش بنویسه .

خوشبختانه بسیاری از شرکت های تولید کننده نرم افزار نظیر Houdini و Maya , Arnold و Render Man و غیره کار ما رو راحت کردن و این کتابخانه عظیم VDB رو برای این برنامه ها کامپایل کردن و در غالب ابزار های مختلف در اختیار کاربران خودشون قرار دادن.

شاید بتوان گفت اولین برنامه ای که VDB رو تقریبا کامل به خودش اضافه کرد نرم افزار Houdini بود و همچنان نسبت به سایر نرم افزارها از ابزارهای VDB بیشتری استفاده میکنه و همچنین همیشه از آخرین نسخه VDB استفاده میکنه.

در برخی دیگه از برنامه ها و موتور های رندر مانند Arnold یا Renderman e شاید فقط از VDB برای خوندن دود و آتش از روی هارد و رندر آن استفاده شود ، اما بعدا خواهید فهمید که VDB امکانات بسیار گسترده تری به شما میده که تقریبا از تمام این امکانات میتونید در Houdini بهره ببرید.

آشنایی با عملکرد و ماهیت VDB :

کار اصلی VDB پیاده سازی یک سیستم سلسله مراتبی برای ذخیره ، دسته بندی و دستکاری اطلاعات از نوع Volumetric میباشد. بنابراین طبق این تعریف مثلا اگر ما یک انفجار رو در هودینی شبیه سازی کرده باشیم ، میتونیم با فرمت VDB اون رو روی هارد ذخیره کنیم و در برنامه دیگه مانند مایا اون انفجار رو وارد و رندر کنیم. همچنین بر طبق تعریف ما به جز ذخیره انفجار ، میتونیم روی اطلاعات آن یکسری تغییرات را هم اعمال کنیم ، مثلا Density اونرو تغییر بدیم ، یا دوتا انفجار رو با هم تلفیق کنیم ، اطلاعات اضافی رو پاک کنیم که این باعث کم شدن حجم فایل میشه و خیلی کارهای دیگه که بعدا توضیح میدم. همچنین خود VDB از یک ساختار ویژه ای برای دسته بندی و ذخیره اطلاعات Volumetric که ما بهش Voxel میگیم استفاده میکنه که خودش هم حجم فایل رو کم میکنه و به دنبال آن زمان خوندن اطلاعات از روی هارد رو سریعتر میکنه که مسلما میتونه در زمان سرعت رندر هم تاثیر بزاره.


VDB Optimization :

تعریف Voxel و Container

همنطور که میدونید دود ، انفجار و آتش به صورت Voxel Base در نرم افزارهای سه بعدی ساخته میشوند. یعنی یک مکعب بزرگ رو در نظر بگیرید که بهش میگیم Container , خود این مکعب بزرگ از یکسری مکعب کوچکتر تشکیل شده که بهش میگیم Voxel و این Voxel ها هستند که اطلاعات دود یا آتش مانند Density یا Temperature در داخلشون ذخیره میشه ، در حقیقت Voxel ها چیزی شبیه Pixel در ذخیره عکس هستند ، هر پیکسل اطلاعات تصویر مانند رنگ RGB یا Alpha را در داخل خودشون ذخیره میکنند و فقط فرقشون با Voxel اینه که چون تصویر یک ماهیت دوبعدی داره ، پس پیکسل ها هم مربع هستند ، اما آتش و انفجار چون ماهیت سه بعدی دارن ، پس Voxel ها مکعب هستند.

دقیقا مانند عکس که هر چه تعداد پیکسل ها بیشتر باشد کیفیت عکس بالاتر میره ، در انفجار یا دود هم هر چه تعداد Voxel ها بیشتر باشه کیفیت و جزییات دود بالاتر میره ، این همون چیزیه که در تنطیمات دود یا آتش در برنامه های مختلف با پارامتر هایی مانند Voxel Size یا Volume Resolution میتونیم کنترلش کنیم ، چون هر چه واکسل ها کوچکتر باشند یعنی تعداد بیشتری در داخل مکعب بزرگ
یا Container جا میشوند.

حالا این توضیح رو راجب Container و Voxel دادم که یه چیز جالب راجب VDB رو خدمتتون عرض کنم :

در دود و آتشی که به صورت Volume استاندارد در نرم افزار وجود دارند ، همواره باید در داخل یک Container مکعبی شکل وجود داشته باشند ، حالا فرض کنید همه جای این Container دارای دود نیست و کاملا خالیه ، ولی در هر صورت در آن بخش ها Voxel ها وجود دارند و فقط اطلاعات عددیشون صفره ، بنابراین خودتون حساب کنید که در یک پروژه دود ، آتش و انفجار تعداد بسیار زیادی Voxel اضافی در داخل Container میتونه وجود داشته باشه که هیچ کاربردی ندارن ولی وجود این Voxel ها باعث بالا رفتن حجم فایل شده و حتی در مرحله رندر رم اضافی رو اشغال میکنه و به طبع اون سرعت رندر رو کم میکنه.

اما وقتی ما انفجار رو به صورت VDB تبدیل میکنیم خود VDB با توجه به ماهیت و قابلیتی که داره فقط اون Voxel هایی را ذخیره میکنه که دارای اطلاعات هستند و واکسل های اضافی رو پاک میکنه و این به بهینه سازی پروژه کمک زیادی میکنه.

فرق Volume و VDB از لحاظ نوع اطلاعات :

همانطور که میدونید در نرم افزارهای سه بعدی ما با انواع مختلفی از اطلاعات مانند Integer , Float , Vector , String و غیره سرو کار داریم که به ترتیب میشن اعداد صحیح ، اعشاری ، برداری ، متنی و غیره …

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

حرارت یا Temperature که برای مرحله رندر و متریال از اون برای مشخص کردن بخش های دارای آتش در داخل دود استفاده میکنیم.
شتاب یا Velocity که در مرحله رندر با کمک اون میتونیم روی آتش یا انفجار Motion Blur را اعمال کنیم.
البته در هودینی به جز این موارد اطلاعات دیگری مانند Heat و Rest و غیره هم وجود داره که فعلا به توضیح آنها در این مقاله نیازی نیست.

خوب حالا هر کدوم از این اطلاعات میتونند نوع متفاوتی داشته باشند ، مثلا Density و Temperature از نوع اعشاری یا Float هستند ، چون وقتی میگیم حرارت یعنی ما تنها با یک عدد سرو کار داریم یا وقتی میگیم Density فقط با یک عددچگالی یا تراکم سروکار داریم که مثلا صفر یعنی ما هیچ چیز رو نمیبینیم و یک یعنی ما حالت دود رو میبینیم ، شاید بشه گفت کمی شبیه Alpha میباشد.

اما وقتی میگیم Velocity یا شتاب ، دیگه با یک عدد سروکار نداریم ، چون شتاب جهت داره و باید بدونیم به سمت محور های XYZ چه شتابی دارن ، پس Velocity یک ماهیت برداری یا Vector داره.

حالا این هم جالبه بدونید که در هودینی وقتی با Volume های استاندارد کار میکنیم تمامی اطلاعات فقط میتونند از نوع Float و تکی باشند !

پس ما مجبوریم برای ذخیره اطلاعات برداری مانند Velocity ابتدا آن را به سه عدد Float مجزا برای تک تک محور های XYZ ذخیره کنیم و این یعنی اینکه به جای استفاده مثلا یک حافظه از رم ، از سه حافظه در رم استفاده کنیم و همچنین حجم فایل روی هارد هم بیشتر میشه !

برای همینم هست که وقتی در هودینی دود سیمولیت میکنید اگر اطلاعاتش رو ببینید میبینید نوشته velx و vely و velz .

اما یکی از قابلیت های دیگر VDB اینه که ما میتوانیم اطلاعات برداری رو مستقیما به همان صورت Vector در واکسل ها ذخیره کنیم که باز این به بهینه سازی پروژه کمک میکنه.


انواع Volumetric و VDB :

قبل از توضیح این بخش باید تعریف درست و صحیح کلمه Volume رو یاد بگیرید ،شاید تعریفی که خیلی از دوستان داشته باشند این باشه که ما به هر نوع آبجکتی که ماهیت مه یا Fog داشته باشه میگیم Volume , چیزایی مانند ابر ، دود ، بخار ، آتش ، انفجار ، مه . اما این صحیح نیست چون ما میتونیم آبجکت هایی هم داشته باشیم که هیچ شباهتی به مه و دود ندارن ولی باز از جنس Volume هستند.

کلا در دنیای سه بعدی وقتی از کلمه Volume استفاده میکنین یعنی فضای تو پر ، مثلا وقتی یک کره به شکل Volume داریم یعنی یک فضاى کروی که کاملا تو پر میباشد ، خوب در یک کره Polygon معمولی ما فقط با فیس ها و ورتکس ها سروکار داریم و در داخل کره چیزی وجود ندارد و اطلاعاتی نداریم ، اما اگر یک کره به شکل مه رو در نظر بگیرید ما در داخل کره هم چگالی و تراکم داریم ، برای همینم دود و آتش باید از نوع Volume باشند چون یک پوسته تو خالی نیستند.

البته این رو هم بگم منظور از تو پر صرفا این نیست که حتما باید به صورت کامل در داخل مدل ما density داشته باشیم ، بلکه میتونه یک مدل ضخامت دار و دولایه هم باشه ، برای همینم شما یک مدل پوسته مانند Grid رو نمیتونید به Volume تبدیل کنید ولی اگر بهش ضخامت بدید اونوقت میتونید به Volume تبدیلش کنید.

خوب حالا فهمیدیم که آبجکت های Volume تو پر هستند و بر خلاف مدل های Polygon که از ورتکس ، فیس و غیره تشکیل شده اند ، آبجکت های نوع Volumeبرای ترسیم شکل ظاهری خود تنها نیاز به عدد Density دارن و این عدد با مات یا شفاف کردن Voxel ها شکل ظاهری آبجکت Volume رو ترسیم میکنه.

آشنایی با Open VDB

voxel Grid in maya

اما نکته مهم اینجاست که حالا ما برای نمایش این Voxel ها در فصای سه بعدی با کمک عدد Density دو راه داریم ، یا اینکه از عدد Density مانند Alpha برای مشخص کردن میزان شفافیت Voxel ها استفاده کنیم که نتیجه چیزی شبیه به دود یا مه میشه ، این همون حالت معمولیه که باعث میشه ما انفجار و دود رو به همین شکلی که هستند در نرم افزار ببینیم.

اما حالت دوم اینه که ما با توجه به Density آن Voxel های مورد نظر رو کلا یا به صورت مکعب مات ببینیم یا کلا نبینیم ! نتیجه این میشه که یک کره از جنس Volume به جای اینکه به صورت مه دیده بشه تقریبا به صورت همون کره معمولی Polygon. دیده میشود !!!

به این حالت میگیم Volume از نوع SDF و به حالتی که Volume به صورت دود دیده بشه میگیم Volume از نوع Fog

آشنایی با Open VDB

پس با این حساب دیگه نمیشه گفت صرفا هر چیزی که ظاهری نیمه شفاف یا Semi Transparent دارد رو میگیم Volume .

این حالت SDF اگرچه مانند دود نیست و معمولا مستقیما در رندر استفاده نمیشه ، اما یکسری قابلیت ویژه به ما میده که ما با کمکش میتونیم یکسری کارهای خیلی جالب در هودینی انجام بدیم.

با توجه به اینکه SDF صرفا برای VDB نیست و کاربردهای بسیار بسیار زیادی به خصوص در داینامیک و جلوه های ویژه داره ، من نحوه کار با اونرو در قالب یک مقاله جدا براتون مینویسم و فقط اینجا یه توضیح مختصر راجبش میدم که فقط حدودی بدونید چیه :


آشنایی با SDF :

این SDF مخفف کلمات Signed Distance Field میباشد و به واسطه اون ما این قابلیت رو داریم هر نقطه ای در فضاى سه بعدی رو دقیقامشخص کنیم که آیا در داخل آبجکت SDF قرار داره یا اینکه خارج از اون یا حتی روی سطح اون قرار داره !

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

همچنین مقدار عدد خروجی هم فاصله اون نقطه رو تا سطح مدل SDF مشخص میکنه ، پس اگر خروجی مثلا -2 بود یعنی اون نقطه در فاصله 2 و خارج مدل قرار داره.

همین قابلیت اساس کار محاسبه Collision از نوع Volume رو در داخل محیط داینامیک هودینی در بر میگیره که شاید هر روز نا خود آگاه دارید باهاش کار میکنید .

اجازه بدید با کاربردهای دیگر SDF در مقاله بعدی آشنا شوید.


ابزارهای VDB :

نکته : مجددا به ماربران سایر برنامه ها بگم که کلیه این ابزارهایی که برای VDB در هودینی وجود داره(آشنایی با Open VDB ) ، در حقیقت همه توابعی هستند که در سورس کد VDB وجود داره ، بنابراین اگر شرکتی این ابزارها را برای برنامه خودش کامپایل کنه ، احتمالا آنها هم باید اسم و تنظیمات مشابهی با هودینی داشته باشند.

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

برخی از ابزارهای پرکاربرد VDB در Houdini :

خوب همانطور که ما در هودینی ندهایی مانند ISO Offset داریم که به کمکش میتونیم مدل رو به Volume تبدیل کنیم ، یکسری ند مخصوص هم داریم که مستقیما میتونیم مدل رو به جنس VDB تبدیل کنیم.

ندهای VDB From Polygon و VDB from Points و VDB from Particle از این ندها هستند که به ترتیب برای تبدیل مدل polygon به VDB , برای تبدیل نقاط به VDB و برای تبدیل پارتیکل ها به VDB استفاده میشوند.در داخل تنظیمات این ندها هم میتونید مشخص کنید که میخواهید VDB از نوع Fog داشته باشید یا SDF.
همچنین با کمک ند VDB Convert میتونید دود یا آتشی رو که شبیه سازی کردید رو به VDB تبدیل کنید و بعد اون رو با کمک فرمت VDB ذخیره کنید و در سایر برنامه ها رندر کنید. از کاربردهای دیگه این ند اینه که میتونید بالعکس عمل کنید و اون آبجکت از نوع VDB رو که دارید را به مدل Polygon معمولی تبدیل کنید.متلا آتشی رو در نظر بگیرید که شبیه مدل Polygon باشد.

البته از این قابلیت بیشتر برای شبیه سازی مایعات استفاده میشه ، به این صورت که وقتی آب در سیستم Flip به شکل پارتیکل شبیه سازی میشه ، شما میتونید با کمک ند VDB from Particle Field اونو به VDB از نوع SDF تبدیل کنید و بعد با کمک ند VDB Convert به شکل polygon تبدیل کنید و نهایتا به مدل آب پلی گان شده متریال بدید و رندر کنید.

ندهایی نظیر VDB Mix , VDB Combine و غیره هم وجود دارند که برای تلفیق دو VDB مختلف با هم استفا ده میشوند ، مثلا وقتی میخواهید دوتا آتش رو با هم یکی کنید یا یکی رو از داخل یکی دیگه حذف کنید. ند مهم دیگه ند VDB Fracture میباشد که برای ایجاد برش در داخل VDB استفاده میشه .

ند VDB Smooth که برای نرم کردن شکل VDB استفاده میشه ، مثلا همون مثال تبدیل پارتیکل های Flip به مدل Polygon آب رو در نظر بگیرید که میخواهید سطح آب خیلی نرم تر و Smooth تر بشه ،با کمک این ند ابتدا VDB آب رو نرم میکنید و بعد به پلی گان تبدیلش میکنید.

ند VDB Boolean هم چیزی شبیه همون ابزار Boolean در polygon میباشد و فقط با این تفاوت که روی آبجکت های VDB کار میکنه.

یکی از ندهای جالب دیگه ند VDB Visualizer میباشد که با کمک این ند میتونید تک تک Voxel های اصلی رو در مدل VDB به صورت مکعب های کوچک ببینید ، یعنی چیزی مثل افکت Pixelization که خیلی از دوستان علاقه دارن.

البته این ند بیشتر برای عیب یابی و اطلاع از تعداد واکسل هاست. با کمک همین ند میتونید ببینید که برخلاف Volume معمولی ، آبجکت های VDB اتوماتیک واکسل های اضافی رو خودش پاک میکنه ، همون جیزی که در بالا توضیح دادم.

کلا بخش VDB ندهای بسیار زیاد دیگری برای انجام عملیات مختلف مانند کم کردن تعداد Voxel ها و غیره داره که بهتره لیست کاملشون و نحوه کارشون رو در داخل help هودینی نگاه بندازید.


نحوه انتقال VDB از هودینی به سایر برنامه ها

انجام این عملیات کار زیاد دشواری نیست ، البته به این شرط که نرم افزار و موتور رندر مقصد هم VDB رو ساپورت بکنه ، عملیات به این صورته که ابتدا باید دود یا آتش خود رو شبیه سازی کنید ، سپس نتیجه شبیه سازی شده دود و آتش رو با کمک ند VDB Convert به جنسیت VDB از نوع Smoke تبدیل کنید ، فقط در تنطیمات تبدیل باید حواستون باشه که به جز اطلاعات Density اطلاعات Temperature و vel هم تبدیل بشه تا بعدا در برنامه دیگه بتونید از اونها برای رندر استفاده کنید.
بعد از تبدیل به جنس VDB با کمک همون ند معمول ROP Geometry میتونید آتش یا انفجار تبدیل شده به VDB (آشنایی با Open VDB)رو روی هارد ذخیره کنید و فقط حواستون باشه که فرمت خروجی رو حتما باید vdb بنویسید.

سپس در برنامه مقصد با توجه به موتور رندرتون و ابزارهایی که داره اون فایل های VDB رو وارد کنید ، مثلا در Arnold یک منو و ند برای وارد کردن VDB به مایا اضافه میشه.
بعد از اینکه VDB رو به نرم افزار خودتون وارد کردید با کمک متریال های مخصوص Volume به اون متریال اعمال میکنید ، نکته مهم اینجاست که اون اطلاعات density و temperature رو که در هودینی داخل VDB ریخته بودید رو باید اینجا در بخش متریال بهش دسترسی داشته باشید و از اون برای کنترل رنگ ، یا emission متریال و غیره استفاده کنی. و همین باعث میشه براحتی بتونید آتش ، انفجار و غیره رو رندر بگیرید.

فقط یک مطلب مهم خدمتتون عرض کنم و اون اینه که همانطور که گفتم ، Houdini از بقیه برنامه ها زودتر خودش رو برای VDB آپدیت میکنه ، بنابراین ممکنه مثلا اون موتور Arnold که در مایا استفاده میکنید از نسخه های قدیمی تر VDB استفاده کنه و به مشکل بخورید ، اینجا باید بعد از فهمیدن اینکه موتور رندرتون دقیقا از چه نسخه VDB پشتیبانی میکنه ، به هودینی بگید که VDB رو با نسخه قدیمی تر خروجی بگیره ، برای انجام اینکار هم کافیه یک Environment Variable برای هودینی تعریف کنید که روش تعریف کردنش رو کامل در فوروم هودینی نوشته

اگر هم به این مقوله رندر دود و آتش هودینی در آرنولد علاقه مند بودید پیشنهاد میکنم حتما سری به خود سایت Solid Angle بزنید ،تمامی مراحل را از هودینی تا مایا مرحله به مرحله توضیح داده است.

در پایان مجددا عرض میکنم که بزرگترین ویژگی کار با VDB (آشنایی با Open VDB) سبکی و سرعت زیادشه و بنابراین در پروژه های بزرگ معمولا حتی به جز مرحله رندر ،دود و آتش ها رو به VDB تبدیل میکنند و بعد باهاش کار میکنند تا هم رم کمتریاز سیستم اشغال بشه و هم سرعت کارشون بالا بره ، فقط شاید تنها محدودیت VDB (آشنایی با Open VDB)این باشه که برخی از ندهای استاندارد هودینی تنها روی Volume های معمولی کار میکنند و با VDB سازگاری ندارن که البته مشکلی نیست ، چون شما با همون ند VDB Convert میتونید VDB رو دوباره به Volume معمولی تبدیل کنید و بعد از انجام کارتون دوباره اونو به حالت اول برگردونید.

سخن پایانی :

اگرچه میدونم این مطلب(آشنایی با Open VDB) خیلی طولانی شده ولی امیدوارم دوستان علاقه مند وقت کنند و کامل این مقاله رو مطالعه کنند تا شاید بتونند از مزایای این تکنولوژی برای بالابردن کیفیت کارهاشون و همچنین رهایی از برخی محدودیت های کار باVolumetric ها استفاده کنند.

تمامی مطالب توسط تیم سافت ساز ترجمه و جمع آوری می شود . منبع سافت ساز

امتیاز دهی به این مطلب:

امتیاز سافت ساز

لطفا به این مطلب امتیاز دهید.

User Rating: 4.8 ( 3 votes)

نوشته های مشابه

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