گرافیک های سه بعدی کامپیوتری نمایش اشیای سه بعدی را در یک صفحه تخت فراهم میکنند. چنین اشیایی و همچنین موقعیت بیننده میتوانند با گذشت زمان تغییر کنند. بر این اساس، تصویر دو بعدی نیز باید ایجاد شود تا توهم عمق تصویر ایجاد شود، یعنی باید از چرخش، بزرگنمایی، تغییر در نور و غیره پشتیبانی کند. MQL5 امکان ایجاد و مدیریت گرافیک رایانه را مستقیماً در ترمینال MetaTrader 5 با استفاده از توابع DirectX فراهم میکند. لطفاً توجه داشته باشید که کارت گرافیک های سه بعدی شما برای کارکرد توابع باید از DX 11 و Shader Model 5.0 پشتیبانی کند.
- مدلسازی شی
- ایجاد یک شکل
- محاسبه و ارائه صحنه
- چرخش اشیا حول محور Z و Viewpoint
- مدیریت موقعیت دوربین
- مدیریت رنگ شی
- چرخش و حرکت
- کار با روشنایی
- انیمیشن
- موقعیت دوربین را با استفاده از ماوس کنترل کنید
- استفاده از Textureها
- ایجاد اشیای سفارشی
- سطح سه بعدی مبتنی بر داده
مدل سازی گرافیک های سه بعدی شی
برای ترسیم یک جسم سه بعدی در یک فضای مسطح ابتدا باید مدلی از این جسم در مختصات X ، Y و Z به دست آورد. این بدان معناست که هر نقطه از سطح جسم باید با تعیین مختصات آن توصیف شود. در حالت ایده آل، برای حفظ کیفیت تصویر در هنگام مقیاس گذاری، باید تعداد نامحدودی از نقاط روی سطح جسم را توصیف کنید. در عمل، مدل های سه بعدی با استفاده از mesh متشکل از چند ضلعی توصیف میشوند. mesh با جزییات بیشتر با تعداد چند ضلعی بیشتر، یک مدل واقع گرایانه تر را ارائه میدهد. با این وجود، برای محاسبه چنین مدلی و ارائه گرافیک های سه بعدی، منابع رایانه ای بیشتری لازم است.
تقسیم چند ضلعی ها به مثلث، مدتها پیش ظهور پیدا کرد هنگامی که گرافیک کامپیوتری اولیه باید روی کارت گرافیک ضعیف اجرا میشد. مثلث، شرح دقیق موقعیت یک قسمت کوچک سطح و همچنین محاسبه پارامترهای مربوطه مانند چراغ ها و بازتاب نور را امکان پذیر میکند. مجموعه ای از این مثلث های کوچک امکان ایجاد یک تصویر واقعاً سه بعدی از جسم را فراهم میکند. از این پس، چند ضلعی و مثلث به عنوان مترادف استفاده خواهند شد، زیرا تصور مثلث بسیار ساده تر از چند ضلعی با N رأس است.
با توصیف مختصات هر رأس مثلث میتوان یک مدل سه بعدی از یک جسم ایجاد کرد که امکان محاسبه بیشتر مختصات را برای هر نقطه از جسم فراهم میکند، حتی اگر جسم حرکت کند یا موقعیت بیننده تغییر کند. بنابراین، ما با رئوس، یالهایی که آنها را متصل میکند و وجهی که توسط یالها تشکیل میشود، سروکار داریم. اگر موقعیت یک مثلث مشخص باشد، میتوانیم با استفاده از قوانین جبر خطی یک حالت نرمال برای وجه ایجاد کنیم (حالت نرمال، برداری است که عمود بر سطح است). این امر اجازه میدهد تا چگونگی روشن شدن وجه و چگونگی انعکاس نور از آن محاسبه شود.
یک شی مدل را میتوان به روش های مختلف ایجاد کرد. توپولوژی، چگونگی تشکیل چند ضلعی ها به شکل mesh گرافیک سه بعدی را توصیف میکند. یک توپولوژی خوب امکان استفاده از مینیمم تعداد چند ضلعیها برای توصیف یک شی را فراهم میکند و میتواند حرکت و چرخش شی را آسانتر کند.
جلوه حجم با استفاده از نورها و سایه ها روی چند ضلعیهای شی ایجاد میشود. بنابراین، هدف از گرافیک کامپیوتری سه بعدی محاسبه موقعیت هر نقطه از یک جسم، محاسبه نورها و سایه ها و نمایش آن بر روی صفحه است.
ایجاد یک شکل باگرافیک های سه بعدی
بگذارید یک برنامه ساده بنویسیم که یک مکعب ایجاد میکند. از کلاس CCanvas3D از کتابخانه گرافیکی سه بعدی استفاده کنید.
کلاس CCanvas3DWindow، که یک پنجره سه بعدی ارائه میدهد، حداقل اعضا و متدها را دارد. ما به تدریج با توضیح مفاهیم گرافیکی سه بعدی که در توابع برای کار با یکپارچه سازی DirectX پیاده سازی شدهاند، متدهای جدیدی اضافه خواهیم کرد.
//+------------------------------------------------------------------+ //| Application window | //+------------------------------------------------------------------+ class CCanvas3DWindow { protected: CCanvas3D m_canvas; //--- canvas size int m_width; int m_height; //--- the Cube object CDXBox m_box; public: CCanvas3DWindow(void) {} ~CCanvas3DWindow(void) {m_box.Shutdown();} //-- create a scene virtual bool Create(const int width,const int height){} //--- calculate the scene void Redraw(){} //--- handle chart events void OnChartChange(void) {} };
ایجاد صحنه با ایجاد صفحه آغاز میشود. سپس پارامترهای زیر برای ماتریسprojection تنظیم میشوند:
- یک زاویه دید 30 درجه (M_PI / 6)، که از آن به صحنه سه بعدی نگاه میکنیم.
- نسبت ابعاد به عنوان نسبت عرض و ارتفاع
- فاصله تا near clipping plane (1f) و far clipping plane (100. f)
این بدان معنی است که فقط اشیای بین این دو دیوار مجازی (0.1f و 100. f) در ماتریس projection ارائه میشوند. علاوه بر این، شی باید در زاویه دید 30 درجه افقی قرار گیرد. لطفا توجه داشته باشید که فواصل و همچنین همه مختصات موجود در گرافیک رایانه ای مجازی هستند. آنچه مهم است روابط بین فاصله ها و اندازه ها است، اما نه مقادیر مطلق.
//+------------------------------------------------------------------+ //| Create | //+------------------------------------------------------------------+ virtual bool Create(const int width,const int height) { //--- save canvas dimensions m_width=width; m_height=height; //--- create a canvas to render a 3D scene ResetLastError(); if(!m_canvas.CreateBitmapLabel("3D Sample_1",0,0,m_width,m_height,COLOR_FORMAT_ARGB_NORMALIZE)) { Print("Error creating canvas: ",GetLastError()); return(false); } //--- set projection matrix parameters - angle of view, aspect ratio, distance to the near and far clip planes m_canvas.ProjectionMatrixSet((float)M_PI/6,(float)m_width/m_height,0.1f,100.0f); //--- create cube - pass to it the resource manager, scene parameters and coordinates of two opposite corners of the cube if(!m_box.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),DXVector3(-1.0,-1.0,5.0),DXVector3(1.0,1.0,7.0))) { m_canvas.Destroy(); return(false); } //--- add the cube to the scene m_canvas.ObjectAdd(&m_box); //--- redraw the scene Redraw(); //--- succeed return(true); }
پس از ایجاد ماتریس projection، میتوانیم به ساخت شی 3D – مکعبی مبتنی بر کلاس CDXBox بپردازیم. برای ایجاد یک مکعب، کافی است دو بردار را نشان دهید که به گوشه های مخالف مکعب اشاره دارند. با تماشای ایجاد مکعب در حالت debug، میتوانید ببینید که در DXComputeBox () چه اتفاقی میافتد: ایجاد همه رئوس مکعب (مختصات آنها در آرایه “رئوس” نوشته شده است)، همچنین تقسیم یال های مکعب به مثلث که در آرایه indiсesبرشمرده و ذخیره میشوند. در مجموع، مکعب دارای 8 رأس 6 وجهی است که به 12 مثلث تقسیم میشود و 36 indiсes که رئوس این مثلث ها را برشمردهاند.
اگرچه این مکعب فقط 8 رأس دارد، 24 بردار برای توصیف آنها ایجاد میشود، زیرا مجموعه جداگانه ای از رئوس که دارای حالت نرمال هستند باید برای هر 6 وجه مشخص شود. جهت نرمال در محاسبه نور روی هر وجه تأثیر میگذارد. ترتیب قرارگیری رأس های یک مثلث در indiсes تعیین میکند که کدام یک از اضلاع آن قابل مشاهده باشد. ترتیب پر شدن رئوس و شاخص ها در کد DXUtils.mqh نشان داده شده است:
for(int i=20; i<24; i++) vertices[i].normal=DXVector4(0.0,-1.0,0.0,0.0);
مختصات texture برای نگاشت texture برای هر وجه در همان کد شرح داده شده است:
//--- texture coordinates for(int i=0; i<faces; i++) { vertices[i*4+0].tcoord=DXVector2(0.0f,0.0f); vertices[i*4+1].tcoord=DXVector2(1.0f,0.0f); vertices[i*4+2].tcoord=DXVector2(1.0f,1.0f); vertices[i*4+3].tcoord=DXVector2(0.0f,1.0f); }
هر 4 بردار وجه یکی از 4 زاویه را برای نگاشت texture تعیین میکند. این بدان معنی است که یک ساختار دست های به هر وجه مکعب نگاشت میشود تا texture، render شود. البته این فقط در صورت تنظیم texture مورد نیاز است.
محاسبه و ارائه صحنه در گرافیک های سه بعدی
در هر بار تغییر صحنه 3D ، همه محاسبات باید از نو انجام شود. در اینجا ترتیب محاسبات مورد نیاز آمده است:
- مرکز هر شی را در مختصات جهانی محاسبه کنید
- موقعیت هر عنصر از شی، یعنی هر رأس را محاسبه کنید
- عمق پیکسل و قابلیت مشاهده بودن آن را برای بیننده تعیین کنید
- موقعیت هر پیکسل را روی چند ضلعی مشخص شده توسط رئوس آن محاسبه کنید
- موقعیت هر پیکسل را روی چند ضلعی مشخص شده توسط رئوس آن محاسبه کنید
- رنگ هر پیکسل را متناسب با texture مشخص شده روی چند ضلعی تنظیم کنید
- جهت پیکسل نور و بازتاب آن را محاسبه کنید
- نور پراکنده را به هر پیکسل اعمال کنید
- همه مختصات جهانی را به مختصات دوربین تبدیل کنید
- مختصات دوربین را به مختصات ماتریس projection تبدیل کنید
همه این عملیات در متد Render از شی CCanvas3D انجام میشود. پس از render، با فراخوانی متد Update، تصویر محاسبه شده از ماتریس projection به canvas منتقل میشود.
//+------------------------------------------------------------------+ //| Update the scene | //+------------------------------------------------------------------+ void Redraw() { //--- calculate the 3D scene m_canvas.Render(DX_CLEAR_COLOR|DX_CLEAR_DEPTH,ColorToARGB(clrBlack)); //--- update the picture on the canvas in accordance with the current scene m_canvas.Update(); }
در مثال ما، مکعب فقط یک بار ایجاد میشود و دیگر تغییری نمیکند. بنابراین، تنها در صورت تغییر در نمودار، مانند تغییر اندازه نمودار، باید فریم روی canvas تغییر یابد. در این حالت، ابعاد canvas با ابعاد نمودار فعلی تنظیم میشود، ماتریس projection مجدداً تنظیم میشود و یک تصویر روی canvas به روز میشود.
//+------------------------------------------------------------------+ //| Process chart change event | //+------------------------------------------------------------------+ void OnChartChange(void) { //--- get current chart sizes int w=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS); int h=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS); //--- update canvas dimensions in accordance with the chart size if(w!=m_width || h!=m_height) { m_width =w; m_height=h; //--- resize canvas m_canvas.Resize(w,h); DXContextSetSize(m_canvas.DXContext(),w,h); //--- update projection matrix in accordance with the canvas sizes m_canvas.ProjectionMatrixSet((float)M_PI/6,(float)m_width/m_height,0.1f,100.0f); //--- recalculate 3D scene and render it onto the canvas Redraw(); } }
“Step1 Create Box.mq5” EA را راهاندازی کنید. یک مربع سفید روی زمینه سیاه خواهید دید. به طور پیش فرض، رنگ سفید برای سطح اشیا در هنگام ایجاد تنظیم شده است. روشنایی هنوز تنظیم نشده است.
محور X به سمت راست، Y به سمت بالا و Z به سمت داخل صحنه 3D هدایت میشود. به چنین سیستم مختصاتی چپ دستی گفته میشود.
مرکز مکعب در نقطهای با مختصات X = 0 ، Y = 0 ، Z = 6 قرار دارد. موقعیتی که از آن به مکعب نگاه میکنیم در مرکز مختصات است که مقدار پیش فرض است. اگر میخواهید موقعیتی را که صحنه 3D از آن مشاهده میشود تغییر دهید، مختصات مناسب را با استفاده از تابع ViewPositionSet () مشخص کنید.
برای تکمیل عملیات برنامه، “Escape” را فشار دهید.
چرخش اشیا حول محور Z و Viewpoint
برای متحرک سازی صحنه، اجازه دهید چرخش مکعب را حول محور Z فعال کنیم. برای این کار، یک تایمر اضافه کنید – بر اساس رویدادهای آن مکعب در خلاف جهت عقربه های ساعت چرخانده میشود.
با استفاده از متد DXMatrixRotationZ () یک ماتریس چرخش ایجاد کنید تا بتواند چرخش حول محور Z را در یک زاویه مشخص انجام دهد. سپس آن را به عنوان یک پارامتر به متد TransformMatrixSet () منتقل کنید. این، وضعیت مکعب را در فضای سه بعدی تغییر میدهد. مجدداً Redraw () را برای به روزرسانی تصویر روی canvas، فراخوانی کنید.
//+------------------------------------------------------------------+ //| Timer handler | //+------------------------------------------------------------------+ void OnTimer(void) { //--- variables for calculating the rotation angle static ulong last_time=0; static float angle=0; //--- get the current time ulong current_time=GetMicrosecondCount(); //--- calculate the delta float deltatime=(current_time-last_time)/1000000.0f; if(deltatime>0.1f) deltatime=0.1f; //--- increase the angle of rotation of the cube around the Z axis angle+=deltatime; //--- remember the time last_time=current_time; //--- set the angle of rotation of the cube around the Z axis DXMatrix rotation; DXMatrixRotationZ(rotation,angle); m_box.TransformMatrixSet(rotation); //--- recalculate 3D scene and render it onto the canvas Redraw(); }
پس از راه اندازی، یک مربع سفید چرخشی مشاهده خواهید کرد.
کد منبع این مثال در فایل ” ” Step2 Rotation Z.mq5موجود است. لطفاً توجه داشته باشید که اکنون هنگام ایجاد صحنه، زاویه M_PI / 5 مشخص شده است که بزرگتر از زاویه M_PI / 6 مثال قبلی است.
//--- set projection matrix parameters - angle of view, aspect ratio, distance to the near and far clip planes m_matrix_view_angle=(float)M_PI/5; m_canvas.ProjectionMatrixSet(m_matrix_view_angle,(float)m_width/m_height,0.1f,100.0f); //--- create cube - pass to it the resource manager, scene parameters and coordinates of two opposite corners of the cube
با این حال، ابعاد مکعب در صفحه از نظر بصری کوچکتر است. هرچه زاویه دید کمتری هنگام تنظیم ماتریس projection مشخص شود، قسمت بزرگتر فریم توسط شی اشغال میشود. این را میتوان با دیدن اشیا با تلسکوپ مقایسه کرد: جسم بزرگتر است، گرچه زاویه دید کوچکتر است.
مدیریت موقعیت دوربین
کلاس CCanvas3D دارای سه متد برای تنظیم پارامترهای مهم صحنه 3D است که بهم پیوسته اند:
- ViewPositionSet، viewpoint صحنه سه بعدی را تنظیم میکند
- ViewTargetSet مختصات نقطه gaze را تنظیم میکند
- ViewUpDirectionSet جهت مرز بالای فریم را در فضای سه بعدی تنظیم میکند
همه این پارامترها به صورت ترکیبی استفاده میشوند – این بدان معنی است که اگر میخواهید هر یک از این پارامترها را در صحنه سه بعدی تنظیم کنید، دو پارامتر دیگر نیز باید مقداردهی اولیه شوند. این باید حداقل در مرحله تولید صحنه انجام شود. این در مثال زیر نشان داده شده است، که در آن مرز بالای فریم به چپ و راست چرخانده میشود. این نوسان با اضافه کردن سه خط کد زیر در متد Create() اجرا میشود:
//+------------------------------------------------------------------+ //| Create | //+------------------------------------------------------------------+ virtual bool Create(const int width,const int height) { .... //--- add the cube to the scene m_canvas.ObjectAdd(&m_box); //--- set the scene parameters m_canvas.ViewUpDirectionSet(DXVector3(0,1,0)); // set the direction vector up, along the Y axis m_canvas.ViewPositionSet(DXVector3(0,0,0)); // set the viewpoint from the center of coordinates m_canvas.ViewTargetSet(DXVector3(0,0,6)); // set the gaze point at center of the cube //--- redraw the scene Redraw(); //--- succeed return(true); }
متد OnTimer () را تغییر دهید تا بردار افقی به چپ و راست تغییر کند.
//+------------------------------------------------------------------+ //| Timer handler | //+------------------------------------------------------------------+ void OnTimer(void) { //--- variables for calculating the rotation angle static ulong last_time=0; static float max_angle=(float)M_PI/30; static float time=0; //--- get the current time ulong current_time=GetMicrosecondCount(); //--- calculate the delta float deltatime=(current_time-last_time)/1000000.0f; if(deltatime>0.1f) deltatime=0.1f; //--- increase the angle of rotation of the cube around the Z axis time+=deltatime; //--- remember the time last_time=current_time; //--- set the rotation angle around the Z axis DXVector3 direction=DXVector3(0,1,0); // initial direction of the top DXMatrix rotation; // rotation vector //--- calculate the rotation matrix DXMatrixRotationZ(rotation,float(MathSin(time)*max_angle)); DXVec3TransformCoord(direction,direction,rotation); m_canvas.ViewUpDirectionSet(direction); // set the new direction of the top //--- recalculate 3D scene and render it onto the canvas Redraw(); }
مثال را با عنوان ” Step3 ViewUpDirectionSet.mq5″ذخیره کرده و آن را اجرا کنید. تصویر مکعب در حال چرخش را مشاهده خواهید کرد، گرچه در واقع بی حرکت است. این اثر زمانی بدست میآید که دوربین خودش به چپ و راست بچرخد.
مدیریت رنگ شی در گرافیک های سه بعدی
ضمن حرکت دادن دوربین، اجازه دهید کد خود را اصلاح کرده و مکعب را در مرکز مختصات قرار دهیم.
//+------------------------------------------------------------------+ //| Create | //+------------------------------------------------------------------+ virtual bool Create(const int width,const int height) { ... //--- create cube - pass to it the resource manager, scene parameters and coordinates of two opposite corners of the cube if(!m_box.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),DXVector3(-1.0,-1.0,-1.0),DXVector3(1.0,1.0,1.0))) { m_canvas.Destroy(); return(false); } //--- set the color m_box.DiffuseColorSet(DXColor(0.0,0.5,1.0,1.0)); //--- add the cube to the scene m_canvas.ObjectAdd(&m_box); //--- set positions for camera, gaze and direction of the top m_canvas.ViewUpDirectionSet(DXVector3(0.0,1.0,0.0)); // set the direction vector up, along the Y axis m_canvas.ViewPositionSet(DXVector3(3.0,2.0,-5.0)); // set camera on the right, on top and in front of the cube m_canvas.ViewTargetSet(DXVector3(0,0,0)); // set the gaze direction at center of the cube //--- redraw the scene Redraw(); //--- succeed return(true); }
علاوه بر این، مکعب را به رنگ آبی رنگ کنید. رنگ در قالب یک رنگ RGB با یک کانال آلفا تنظیم شده است (کانال آلفا بعداً نشان داده شده است)، اگرچه مقادیر به یک، نرمال میشوند. بنابراین مقدار 1 به معنی 255 و 5.0 به معنی 127 است.
چرخش را به دور محور X اضافه کنید و تغییرات را با عنوان “Step4 Box Color.mq5” ذخیره کنید.
چرخش و حرکت درگرافیک های سه بعدی
اشیا را میتوان همزمان در سه جهت جابه جا کرد و چرخاند. تمام تغییرات شی با استفاده از ماتریسها پیاده سازی میشوند. هر یک از آنها، به عنوان مثال چرخش، حرکت و تغییر شکل، میتواند به طور جداگانه محاسبه شود. بگذارید مثال را تغییر دهیم: نمای دوربین اکنون از بالا و جلو است.
//+------------------------------------------------------------------+ //| Create | //+------------------------------------------------------------------+ virtual bool Create(const int width,const int height) { ... m_canvas.ProjectionMatrixSet(m_matrix_view_angle,(float)m_width/m_height,0.1f,100.0f); //--- position the camera in top and in front of the center of coordinates m_canvas.ViewPositionSet(DXVector3(0.0,2.0,-5.0)); m_canvas.ViewTargetSet(DXVector3(0.0,0.0,0.0)); m_canvas.ViewUpDirectionSet(DXVector3(0.0,1.0,0.0)); //--- create cube - pass to it the resource manager, scene parameters and coordinates of two opposite corners of the cube if(!m_box.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),DXVector3(-1.0,-1.0,-1.0),DXVector3(1.0,1.0,1.0))) { m_canvas.Destroy(); return(false); } //--- set the cube color m_box.DiffuseColorSet(DXColor(0.0,0.5,1.0,1.0)); //--- calculate the cube position and the transfer matrix DXMatrix rotation,translation; //--- rotate the cube sequentially along the X, Y and Z axes DXMatrixRotationYawPitchRoll(rotation,(float)M_PI/4,(float)M_PI/3,(float)M_PI/6); //-- move the cube to the right/downward/inward DXMatrixTranslation(translation,1.0,-2.0,5.0); //--- get the transformation matrix as a product of rotation and transfer DXMatrix transform; DXMatrixMultiply(transform,rotation,translation); //--- set the transformation matrix m_box.TransformMatrixSet(transform); //--- add the cube to the scene m_canvas.ObjectAdd(&m_box); //--- redraw the scene Redraw(); //--- succeed return(true); }
ماتریس چرخش و انتقال را به ترتیب ایجاد کنید، ماتریس تبدیل حاصل را اعمال کنید و مکعب را ارائه دهید. تغییرات را در ” Step5 Translation.mq5″ ذخیره کرده و آن را اجرا کنید.
دوربین هنوز هست و کمی از بالا به مرکز مختصات اشاره شده است. مکعب در سه جهت چرخانده شد و به سمت راست، پایین و داخل صحنه منتقل شد.
کار با روشنایی
برای به دست آوردن یک تصویر سه بعدی واقع گرایانه، لازم است نور هر نقطه از سطح جسم را محاسبه کنید. این کار با استفاده از مدل سایه زنی Phong انجام میشود، که شدت رنگ سه مولفه روشنایی زیر را محاسبه میکند: محیط، انتشار و بازتاب. این پارامترها در اینجا استفاده میشود:
- DirectionLight – جهت روشنایی جهت دار در CCanvas3D تنظیم شده است
- AmbientLight – رنگ و شدت نور محیط در CCanvas3D تنظیم شده است
- DiffuseColor – مولفه روشنایی انتشار محاسبه شده در CDXMesh و کلاسهای فرزند آن تنظیم شده است
- EmissionColor – مولفه نور پس زمینه در CDXMesh و کلاسهای فرزند آن تنظیم شده است
- SpecularColor – مولفه بازتاب در CDXMesh و کلاسهای فرزند آن تنظیم شده است
مدل روشنایی در سایه زنی استاندارد پیادهسازی میشود، پارامترهای مدل در CCanvas3D و پارامترهای شی در CDXMesh و کلاسهای فرزند آن تنظیم میشوند. مثال را به صورت زیر تغییر دهید:
- مکعب را به مرکز مختصات برگردانید.
- آن را روی رنگ سفید قرار دهید.
- یک منبع جهت دار از رنگ زرد اضافه کنید که صحنه را از بالا به پایین روشن میکند.
- رنگ آبی را برای روشنایی غیر جهت دار تنظیم کنید.
//--- set yellow color for the source and direct it from above downwards m_canvas.LightColorSet(DXColor(1.0,1.0,0.0,0.8f)); m_canvas.LightDirectionSet(DXVector3(0.0,-1.0,0.0)); //--- set the blue color for the ambient light m_canvas.AmbientColorSet(DXColor(0.0,0.0,1.0,0.4f)); //--- create cube - pass to it the resource manager, scene parameters and coordinates of two opposite corners of the cube if(!m_box.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),DXVector3(-1.0,-1.0,-1.0),DXVector3(1.0,1.0,1.0))) { m_canvas.Destroy(); return(false); } //--- set the white color for the cube m_box.DiffuseColorSet(DXColor(1.0,1.0,1.0,1.0)); //--- add green glow for the cube (emission) m_box.EmissionColorSet(DXColor(0.0,1.0,0.0,0.2f));
لطفاً توجه داشته باشید که موقعیت منبع نور هدایت شده در Canvas3D تنظیم نشده است، در حالی که فقط جهتی است که در آن نور پخش میشود. منبع نور جهت دار در یک فاصله بی نهایت در نظر گرفته میشود و یک جریان نوری کاملاً موازی صحنه را روشن میکند.
m_canvas.LightDirectionSet(DXVector3(0.0,-1.0,0.0));
در اینجا، بردار پخش نور در امتداد محور Y در جهت منفی نشان داده میشود، یعنی از بالا به پایین. بعلاوه، اگر پارامترهایی را برای منبع نور هدایت شده تنظیم کنید (LightColorSet و LightDirectionSet)، باید رنگ نور محیط (AmbientColorSet) را نیز تعیین کنید. به طور پیش فرض، رنگ نور محیط با حداکثر شدت روی سفید تنظیم شده و بنابراین تمام سایه ها سفید خواهند بود. این بدان معنی است که اشیای موجود در صحنه با نور سفید از نور محیط پرتاب میشوند، در حالی که نور منبع جهت دار با نور سفید قطع میشود.
//--- set yellow color for the source and direct it from above downwards m_canvas.LightColorSet(DXColor(1.0,1.0,0.0,0.8f)); m_canvas.LightDirectionSet(DXVector3(0.0,-1.0,0.0)); //--- set the blue color for the ambient light m_canvas.AmbientColorSet(DXColor(0.0,0.0,1.0,0.4f)); // must be specified
انیمیشن gif زیر نحوه تغییر تصویر هنگام اضافه کردن نور را نشان میدهد. کد منبع مثال در فایل ” Step6 Add Light.mq5 ” موجود است.
سعی کنید روش های رنگ را در کد بالا خاموش کنید تا ببینید چطور کار میکند.
انیمیشن با گرافیک های سه بعدی
انیمیشن به معنای تغییر پارامترهای صحنه و اشیا در طول زمان است. هر ویژگی موجود بسته به زمان یا رویدادها قابل تغییر است. تایمر را برای 10 میلی ثانیه تنظیم کنید – این رویداد روی به روزرسانی صحنه تأثیر میگذارد:
int OnInit() { ... //--- create canvas ExtAppWindow=new CCanvas3DWindow(); if(!ExtAppWindow.Create(width,height)) return(INIT_FAILED); //--- set timer EventSetMillisecondTimer(10); //--- return(INIT_SUCCEEDED); }
کنترل کننده رویداد مناسب را به CCanvas3DWindow اضافه کنید. ما باید پارامترهای شی (مانند چرخش، حرکت و بزرگنمایی) و جهت روشنایی را تغییر دهیم:
//+------------------------------------------------------------------+ //| Timer handler | //+------------------------------------------------------------------+ void OnTimer(void) { static ulong last_time=0; static float time=0; //--- get the current time ulong current_time=GetMicrosecondCount(); //--- calculate the delta float deltatime=(current_time-last_time)/1000000.0f; if(deltatime>0.1f) deltatime=0.1f; //--- increase the elapsed time value time+=deltatime; //--- remember the time last_time=current_time; //--- calculate the cube position and the rotation matrix DXMatrix rotation,translation,scale; DXMatrixRotationYawPitchRoll(rotation,time/11.0f,time/7.0f,time/5.0f); DXMatrixTranslation(translation,(float)sin(time/3),0.0,0.0); //--- calculate the cube compression/extension along the axes DXMatrixScaling(scale,1.0f+0.5f*(float)sin(time/1.3f),1.0f+0.5f*(float)sin(time/1.7f),1.0f+0.5f*(float)sin(time/1.9f)); //--- multiply the matrices to obtain the final transformation DXMatrix transform; DXMatrixMultiply(transform,scale,rotation); DXMatrixMultiply(transform,transform,translation); //--- set the transformation matrix m_box.TransformMatrixSet(transform); //--- calculate the rotation of the light source around the Z axis DXMatrixRotationZ(rotation,deltatime); DXVector3 light_direction; //--- get the current direction of the light source m_canvas.LightDirectionGet(light_direction); //--- calculate the new direction of the light source and set it DXVec3TransformCoord(light_direction,light_direction,rotation); m_canvas.LightDirectionSet(light_direction); //--- recalculate the 3D scene and draw it in the canvas Redraw(); }
لطفاً توجه داشته باشید که تغییرات شی بر روی مقادیر اولیه اعمال میشوند، گویی که ما همیشه با وضعیت مکعب اولیه سروکار داریم و همه عملیات مربوط به چرخش / حرکت / فشرده سازی را از ابتدا اعمال میکنیم، به این معنی که حالت فعلی مکعب ذخیره نشده است. با این حال، جهت منبع نور با افزایش زمان deltatime از مقدار فعلی تغییر میکند.
نتیجه، یک انیمیشن سه بعدی بسیار پیچیده است. کد نمونه در فایل ” Step7 Animation.mq5″ موجود است.
موقعیت دوربین را با استفاده از ماوس کنترل کنید
بگذارید آخرین عنصر انیمیشن را در گرافیک سه بعدی، واکنش به اقدامات کاربر، در نظر بگیریم. با استفاده از ماوس در مثال ما مدیریت دوربین را اضافه کنید. ابتدا، رویدادهای ماوس را تأیید کنید و کنترل کننده های مربوطه را ایجاد کنید:
int OnInit() { ... //--- set the timer EventSetMillisecondTimer(10); //--- enable receiving of mouse events: moving and button clicks ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,1); ChartSetInteger(0,CHART_EVENT_MOUSE_WHEEL,1) //--- return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { //--- Deleting the timer EventKillTimer(); //--- disable the receiving of mouse events ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,0); ChartSetInteger(0,CHART_EVENT_MOUSE_WHEEL,0); //--- delete the object delete ExtAppWindow; //--- return chart to the usual display mode with price charts ChartSetInteger(0,CHART_SHOW,true); } void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { ... //--- chart change event if(id==CHARTEVENT_CHART_CHANGE) ExtAppWindow.OnChartChange(); //--- mouse movement event if(id==CHARTEVENT_MOUSE_MOVE) ExtAppWindow.OnMouseMove((int)lparam,(int)dparam,(uint)sparam); //--- mouse wheel scroll event if(id==CHARTEVENT_MOUSE_WHEEL) ExtAppWindow.OnMouseWheel(dparam);
در CCanvas3DWindow، کنترل کننده رویداد حرکت ماوس را ایجاد کنید. با حرکت دکمه سمت چپ ماوس، زوایای جهت دوربین تغییر خواهد کرد:
//+------------------------------------------------------------------+ //| Handle mouse movements | //+------------------------------------------------------------------+ void OnMouseMove(int x,int y,uint flags) { //--- left mouse button if((flags&1)==1) { //--- there is no information about the previous mouse position if(m_mouse_x!=-1) { //--- update the camera angle upon change of position m_camera_angles.y+=(x-m_mouse_x)/300.0f; m_camera_angles.x+=(y-m_mouse_y)/300.0f; //--- set the vertical angle in the range between (-Pi/2,Pi2) if(m_camera_angles.x<-DX_PI*0.49f) m_camera_angles.x=-DX_PI*0.49f; if(m_camera_angles.x>DX_PI*0.49f) m_camera_angles.x=DX_PI*0.49f; //--- update camera position UpdateCameraPosition(); } //--- save mouse position m_mouse_x=x; m_mouse_y=y; } else { //--- reset the saved position if the left mouse button is not pressed m_mouse_x=-1; m_mouse_y=-1; } }
در اینجا کنترل کننده رویداد چرخ ماوس قرار دارد که فاصله دوربین و مرکز صحنه را تغییر میدهد:
//+------------------------------------------------------------------+ //| Handling mouse wheel events | //+------------------------------------------------------------------+ void OnMouseWheel(double delta) { //--- update the distance between the camera and the center upon a mouse scroll m_camera_distance*=1.0-delta*0.001; //--- set the distance in the range between [3,50] if(m_camera_distance>50.0) m_camera_distance=50.0; if(m_camera_distance<3.0) m_camera_distance=3.0; //--- update camera position UpdateCameraPosition(); }
هر دو کنترل کننده برای به روزرسانی موقعیت دوربین با توجه به پارامترهای به روز شده، از متد UpdateCameraPosition () استفاده میکنند:
//+------------------------------------------------------------------+ //| Updates the camera position | //+------------------------------------------------------------------+ void UpdateCameraPosition(void) { //--- the position of the camera taking into account the distance to the center of coordinates DXVector4 camera=DXVector4(0.0f,0.0f,-(float)m_camera_distance,1.0f); //--- camera rotation around the X axis DXMatrix rotation; DXMatrixRotationX(rotation,m_camera_angles.x); DXVec4Transform(camera,camera,rotation); //--- camera rotation around the Y axis DXMatrixRotationY(rotation,m_camera_angles.y); DXVec4Transform(camera,camera,rotation); //--- set camera to position m_canvas.ViewPositionSet(DXVector3(camera)); }
کد منبع در فایل ” Step8 Mouse Control.mq5 “در زیر موجود است.
استفاده از Textureها در گرافیک های سه بعدی
Texture یک تصویر bitmap است که برای نشان دادن الگوها یا مواد، روی سطح چند ضلعی اعمال میشود. استفاده از texture باعث تولید مجدد اشیای کوچک روی سطح میشود که اگر ما آنها را با استفاده از چند ضلعی ایجاد کنیم، به منابع زیادی نیاز خواهیم داشت. به عنوان مثال، این میتواند تقلیدی از یک سنگ، چوب، خاک و سایر مواد باشد.
CDXMesh و کلاس های فرزند آن امکان تعیین texture را دارند. در سایه زن پیکسل استاندارد این texture همراه با DiffuseColor استفاده میشود. انیمیشن شی را بردارید و یک texture سنگی اعمال کنید. این باید در فولدر MQL5 \ Files از دایرکتوری کاری ترمینال قرار داشته باشد:
virtual bool Create(const int width,const int height) { ... //--- set the white color for the non-directional lighting m_box.DiffuseColorSet(DXColor(1.0,1.0,1.0,1.0)); //--- add texture to draw the cube faces m_box.TextureSet(m_canvas.DXDispatcher(),"stone.bmp"); //--- add the cube to the scene m_canvas.ObjectAdd(&m_box); //--- redraw the scene Redraw(); //--- succeed return(true); }
ایجاد اشیای سفارشی
همه اجسام از رئوس (DXVector3) تشکیل شده اند که با استفاده از شاخص ها به primitive ها متصل میشوند. معمول ترین primitive مثلث است. یک شی سه بعدی اساسی با ایجاد لیستی از رئوس که حداقل شامل مختصات است (اما همچنین میتواند حاوی بسیاری از داده های اضافی مانند نرمال، رنگی و غیره باشد)، نوع primitiveهایی که در آن ترکیب میشوند و یک لیستی از شاخص های رأس که توسط آنها در primitive ترکیب میشوند ایجاد میشود.
کتابخانه استاندارد از نوع رأس DXVertex برخوردار است که شامل مختصات آن است، که برای محاسبه روشنایی، مختصات texture و رنگ، طبیعی است. سایه زن رأس استاندارد با این نوع رأس کار میکند.
struct DXVertex
{
DXVector4 position; // vertex coordinates
DXVector4 normal; // normal vector
DXVector2 tcoord; // face coordinate to apply the texture
DXColor vcolor; // color
};
نوع کمکی MQL5 \ Include \ Canvas \ DXDXUtils.mqh شامل مجموعه ای از متدها برای تولید هندسه (رئوس و شاخص ها) از primitiveهای اولیه و بارگیری هندسه سه بعدی از فایلهای .OBJ است.
ایجاد کره و torus را اضافه کنید، همان texture سنگ را اعمال کنید:
virtual bool Create(const int width,const int height) { ... // --- vertices and indexes for manually created objects DXVertex vertices[]; uint indices[]; //--- prepare vertices and indices for the sphere if(!DXComputeSphere(0.3f,50,vertices,indices)) return(false); //--- set white color for the vertices DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f); for(int i=0; i<ArraySize(vertices); i++) vertices[i].vcolor=white; //--- create the sphere object if(!m_sphere.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),vertices,indices)) { m_canvas.Destroy(); return(false); } //--- set diffuse color for the sphere m_sphere.DiffuseColorSet(DXColor(0.0,1.0,0.0,1.0)); //--- set white specular color m_sphere.SpecularColorSet(white); m_sphere.TextureSet(m_canvas.DXDispatcher(),"stone.bmp"); //--- add the sphere to a scene m_canvas.ObjectAdd(&m_sphere); //--- prepare vertices and indices for the torus if(!DXComputeTorus(0.3f,0.1f,50,vertices,indices)) return(false); //--- set white color for the vertices for(int i=0; i<ArraySize(vertices); i++) vertices[i].vcolor=white; //--- create the torus object if(!m_torus.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),vertices,indices)) { m_canvas.Destroy(); return(false); } //--- set diffuse color for the torus m_torus.DiffuseColorSet(DXColor(0.0,0.0,1.0,1.0)); m_torus.SpecularColorSet(white); m_torus.TextureSet(m_canvas.DXDispatcher(),"stone.bmp"); //--- add the torus to a scene m_canvas.ObjectAdd(&m_torus); //--- redraw the scene Redraw(); //--- succeed return(true); }
برای اشیای جدید انیمیشن در گرافیک های سه بعدی اضافه کنید:
void OnTimer(void) { ... m_canvas.LightDirectionSet(light_direction); //--- sphere orbit DXMatrix translation; DXMatrixTranslation(translation,1.1f,0,0); DXMatrixRotationY(rotation,time); DXMatrix transform; DXMatrixMultiply(transform,translation,rotation); m_sphere.TransformMatrixSet(transform); //--- torus orbit with rotation around its axis DXMatrixRotationX(rotation,time*1.3f); DXMatrixTranslation(translation,-2,0,0); DXMatrixMultiply(transform,rotation,translation); DXMatrixRotationY(rotation,time/1.3f); DXMatrixMultiply(transform,transform,rotation); m_torus.TransformMatrixSet(transform); //--- recalculate the 3D scene and draw it in the canvas Redraw(); }
تغییرات را به صورت Three Objects.mq5 ذخیره کرده و آن را اجرا کنید.
سطح گرافیک های سه بعدی مبتنی بر داده
نمودارهای مختلفی معمولاً برای ایجاد گزارش و تجزیه و تحلیل داده ها مانند نمودارهای خطی، هیستوگرام ها، نمودارهای دایرهای و غیره استفاده میشود.
کلاس CDXSurface امکان تجسم یک سطح را با استفاده از داده های سفارشی ذخیره شده در یک آرایه دو بعدی فراهم میکند. اجازه دهید مثالی از تابع ریاضی زیر را مشاهده کنیم.
z=sin(2.0*pi*sqrt(x*x+y*y))
یک شی برای رسم سطح و یک آرایه برای ذخیره داده ایجاد کنید:
virtual bool Create(const int width,const int height) { ... //--- prepare an array to store data m_data_width=m_data_height=100; ArrayResize(m_data,m_data_width*m_data_height); for(int i=0;i<m_data_width*m_data_height;i++) m_data[i]=0.0; //--- create a surface object if(!m_surface.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),m_data,m_data_width,m_data_height,2.0f, DXVector3(-2.0,-0.5,-2.0),DXVector3(2.0,0.5,2.0),DXVector2(0.25,0.25), CDXSurface::SF_TWO_SIDED|CDXSurface::SF_USE_NORMALS,CDXSurface::CS_COLD_TO_HOT)) { m_canvas.Destroy(); return(false); } //--- create texture and reflection m_surface.SpecularColorSet(DXColor(1.0,1.0,1.0,1.0)); m_surface.TextureSet(m_canvas.DXDispatcher(),"checker.bmp"); //--- add the surface to the scene m_canvas.ObjectAdd(&m_surface); //--- succeed return(true); }
سطح در یک جعبه با پایه 4×4 و ارتفاع 1 رسم میشود. ابعاد texture، 0.25×0.25 است.
- SF_TWO_SIDED نشان میدهد که در صورت حرکت دوربین در زیر سطح، سطح، بالا و پایین آن ترسیم میشود.
- SF_USE_NORMALS نشان میدهد که از محاسبات نرمال برای محاسبه بازتاب از سطح ناشی از منبع نور جهت دار استفاده خواهد شد.
- CS_COLD_TO_HOT رنگآمیزی حرارت سطح را از آبی به قرمز با عبور از سبز و زرد تنظیم میکند.
برای تحریک سطح، زمان را زیر علامت سینوس اضافه کنید و آن را با تایمر به روز کنید.
void OnTimer(void) { static ulong last_time=0; static float time=0; //--- get the current time ulong current_time=GetMicrosecondCount(); //--- calculate the delta float deltatime=(current_time-last_time)/1000000.0f; if(deltatime>0.1f) deltatime=0.1f; //--- increase the elapsed time value time+=deltatime; //--- remember the time last_time=current_time; //--- calculate surface values taking into account time changes for(int i=0; i<m_data_width; i++) { double x=2.0*i/m_data_width-1; int offset=m_data_height*i; for(int j=0; j<m_data_height; j++) { double y=2.0*j/m_data_height-1; m_data[offset+j]=MathSin(2.0*M_PI*sqrt(x*x+y*y)-2*time); } } //--- update data to draw the surface if(m_surface.Update(m_data,m_data_width,m_data_height,2.0f, DXVector3(-2.0,-0.5,-2.0),DXVector3(2.0,0.5,2.0),DXVector2(0.25,0.25), CDXSurface::SF_TWO_SIDED|CDXSurface::SF_USE_NORMALS,CDXSurface::CS_COLD_TO_HOT)) { //--- recalculate the 3D scene and draw it in the canvas Redraw(); } }
کد منبع در Surface.mq5 سه بعدی موجود است، مثال برنامه در فیلم نشان داده شده است.
https://youtu.be/YV-tayQCAGY
در این مقاله، ما توانایی های DirectX را در ایجاد اشکال هندسی ساده و گرافیک سه بعدی متحرک برای تجزیه و تحلیل داده های بصری در نظر گرفته ایم. نمونه های پیچیده تری را میتوان در فهرست نصب ترمینال گرافیک های سه بعدی MetaTrader 5 یافت: مشاوران خبره ” Correlation Matrix 3D ” و ” Math 3D Morpher “و همچنین اسکریپت Remnant 3D
MQL5 شما را قادر میسازد بدون استفاده از بسته های شخص ثالث، کارهای مهم تجاری الگوریتمی را حل کنید:
- استراتژی های پیچیده تجارت را که حاوی پارامترهای ورودی بسیاری هستند بهینه کنید
- نتایج بهینه سازی را به دست آورید
- داده ها را در راحت ترین فروشگاه گرافیک های سه بعدی تجسم کنید
از قابلیت های پیشرفته برای تجسم داده های سهام و توسعه استراتژی های معاملاتی در MetaTrader 5 – اکنون با گرافیک های سه بعدی استفاده کنید!
Attached files |
این مقاله ترجمه شده توسط تیم آکادمی ایران ام کیو ال می باشد.
پاسخها