
MetaTrader 5'te DirectX’i kullanarak 3D grafikler nasıl oluşturulur?
3D bilgisayar grafikleri, nesnelerin düz bir ekranda üç boyutlu uzayda olacak şekilde görüntülenmesiyle ilgilidir. İlgili nesnelerin ve izleyicinin konumu zaman içerisinde değişebilir. Buna dayalı olarak, iki boyutlu görüntü de değişmeli, derinlik yanılsaması yaratmaya devam etmelidir - dönme, hareket, ışıklandırma değişiklikleri ve benzeriyle. MQL5 dili, DirectX fonksiyonları kullanılarak doğrudan MetaTrader 5 terminalinde bilgisayar grafiklerinin oluşturulmasına ve yönetilmesine olanak sağlar. Bu işlevlerin çalışması için ekran kartınızın DX 11 ve Shader Model 5.0'ı desteklemesi gerektiğini lütfen unutmayın.
- Nesne modelleme
- Şekil oluşturma
- Sahne hesaplama ve görüntüleme
- Nesnenin Z ekseni ve bakış noktası etrafında döndürülmesi
- Kamera konumu yönetimi
- Nesne rengi yönetimi
- Dönme ve hareket
- Işıklandırmayla çalışma
- Animasyon
- Fareyle kamera konumunun kontrolü
- Dokular uygulama
- Özel nesneler oluşturma
- Veri tabanlı 3D yüzey
Nesne modelleme
Düz bir yüzey üzerine üç boyutlu bir nesne çizebilmek için öncelikle bu nesnenin X, Y ve Z koordinatlarında modelini oluşturmak gerekir. Bu, nesne yüzeyindeki her noktanın koordinatlar cinsinden tanımlanması gerektiği anlamına gelir. İdeal olarak, ölçeklendirme sırasında görüntü kalitesinin kaybolmaması amacıyla nesne yüzerinde sonsuz sayıda nokta tanımlamak gerekir. Pratikte, 3D modeller çokgenlerden oluşan bir ağ kullanılarak tanımlanır. Ağ ne kadar çok çokgen içerirse, model o kadar gerçekçi olur. Ancak, böyle bir modeli hesaplamak ve 3D grafikler oluşturmak için çok daha fazla bilgisayar kaynağı gerekir.
Çokgenlerden oluşan bir ağ şeklinde çaydanlık modeli
Başlangıçta, bilgisayarlar ve ekran kartları günümüzdeki kadar güçlü olmadığı zamanlarda, çokgenleri üçgenlere bölme fikri ortaya çıkmıştır. Üçgen yardımıyla, küçük yüzey alanının konumunu tam olarak tanımlamak ve ışık yansımaları gibi gerekli parametreleri hesaplamak mümkün olmaktadır. Bu tür birçok küçük üçgenin kombinasyonu, nesnenin gerçekçi bir üç boyutlu görüntüsünün oluşturulmasına olanak sağlar. Bir üçgeni hayal etmek N köşeli bir çokgeni hayal etmekten çok daha kolay olduğu için, buradan itibaren çokgen ve üçgen eşanlamlı olarak kullanılacaktır.
Üçgenlerden oluşan bir küp
Üçgenlerin her bir köşesinin koordinatları tanımlanarak, nesnenin üç boyutlu bir modeli oluşturulabilir, bu da nesne hareket etse veya izleyicinin konumu değişse bile, nesnenin her bir noktasının koordinatlarının devamlı olarak hesaplanmasına olanak tanır. Üçgenler, köşelere (vertex), onları birbirine bağlayan kenarlara (edge) ve kenarların oluşturduğu yüzlere (face) sahiptir. Üçgenin uzaydaki konumunu biliyorsak, lineer cebir yasalarını kullanarak yüzey için bir normal oluşturabiliriz (normal, yüzeye dik olan vektördür). Bu, üçgenin yüzü üzerindeki ışık yansımalarının hesaplanmasını mümkün kılar.
Köşeleri, kenarları, yüzleri ve normalleri olan basit nesne örnekleri. Normal, kırmızı okla gösterilmektedir.
Bir nesnenin modeli farklı şekillerde oluşturulabilir. Topoloji, çokgenlerin nasıl bir 3D ağ oluşturduğunu açıklar. İyi bir topoloji, nesneyi tanımlamak için minimum sayıda çokgen kullanılmasına olanak sağlar ve nesnenin uzayda hareket ettirilmesini ve döndürülmesini kolaylaştırır.
İki topolojide küre modeli
Bir nesneyi üç boyutlu yapan şey, çokgenler üzerindeki ışık ve gölge oyunudur. İşte bu, 3D bilgisayar grafiklerinin görevidir: nesnenin her noktasının uzaydaki konumunu hesaplamak, ışık yansımalarını hesaplamak ve ardından onu ekranda görüntülemek.
Şekil oluşturma
Bir küp oluşturan basit bir program yazalım. Bunu yapmak için 3D grafik kütüphanesinden CCanvas3D sınıfını kullanacağız.
CCanvas3DWindow sınıfı, 3D pencere çizmek için yeterli minimum üyeye ve metoda sahiptir. Ayrıca, DirectX ile çalışmak için fonksiyonlarda uygulanan 3D grafik kavramlarıyla giderek yeni metotlar da ekleyeceğiz.
//+------------------------------------------------------------------+ //| 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) {} };
Sahnenin oluşturulması önce tuvalin oluşturulmasıyla başlar. Ardından projeksiyon matrisi için aşağıdaki parametreler ayarlanır:
- 3D sahneye baktığımız 30 derecelik bakış açısı (M_PI/6)
- Genişliğin yüksekliğe oranı
- Yakın (0.1f) ve uzak (100.f) kırpma düzlemine olan mesafe
Bu, yalnızca ilgili iki sanal duvar (0.1f ve 100.f) arasındaki nesnelerin projeksiyon matrisinde görüntüleneceği ve ayrıca nesnelerin yatay 30 derecelik bakış açısı içerisinde olması gerektiği anlamına gelir. Bilgisayar grafiklerinde mesafeler ve genel olarak konuşursak tüm koordinatlar sanaldır. Çünkü, önemli olan mutlak değerler değil, mesafeler ve boyutlar arasındaki ilişkilerdir.
//+------------------------------------------------------------------+ //| 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); }
Projeksiyon matrisini oluşturduktan sonra, CDXBox sınıfına dayalı bir küp olan 3D nesneyi oluşturmaya devam edebiliriz. Bir küp oluşturmak için, küpün zıt köşelerine işaret eden iki vektör belirtmek gerekli ve yeterlidir. Hata ayıklama modunda küpün oluşturulma sürecini izlersek, DXComputeBox() metodunda neler olduğunu görebiliriz: küpün tüm köşelerinin oluşturulması (koordinatları vertices dizisine yazılır) ve küpün yüzlerinin üçgenlere bölünmesi (üçgenlerin köşeleri numaralandırılır ve indiсes dizininde depolanır). Küpün toplamda 8 köşesi, 12 üçgene bölünmüş 6 yüzü ve bu üçgenlerin köşelerini numaralandıran 36 indeksi vardır.
Küpün yalnızca 8 köşesi olmasına rağmen, tam açıklama için 24 vektör oluşturulur, çünkü 6 yüzün her biri için kendi normalleri olan ayrı köşe kümesi belirtilmesi gerekir. Normalin yönü, her bir yüzün ışıklandırmasının hesaplanmasını etkileyecektir. Üçgenlerin köşelerinin numaralandırılma sırası, hangi kenarların görünür olacağını belirler. Köşelerin ve indekslerin doldurulma sırası DXUtils.mqh kodunda gösterilmektedir:
for(int i=20; i<24; i++) vertices[i].normal=DXVector4(0.0,-1.0,0.0,0.0);
Ayrıca, her yüzün doku kaplaması için doku koordinatları da aynı kodda açıklanmaktadır:
//--- 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 yüz vektörünün her biri, doku kaplama için 4 açıdan birini belirler. Bu, görüntüleme sırasında küpün her yüzüne kare bir yapının gerilerek çizileceği anlamına gelir. Elbette, buna yalnızca doku ayarlanmışsa ihtiyaç vardır.
Sahne hesaplama ve görüntüleme
3D sahne her değiştiğinde, tüm hesaplamaların yeniden yapılması gerekir. Bu, gerekli hesaplamaların bir sırayla yapılması gerektiği anlamına gelir:
- Nesnenin merkezinin dünya koordinatlarında konumu
- Nesnenin her bir elemanının, yani her bir köşesinin konumu
- Piksel derinliği ve izleyiciye görünürlüğü
- Çokgenler üzerindeki her pikselin konumu
- Çokgenler üzerindeki her pikselin belirtilen dokuya dayalı rengi
- Piksele düşen ışığın yönü ve ondan yansıması
- Her piksele dağınık ışık uygulanması
- Tüm dünya koordinatlarının kamera koordinatlarına dönüştürülmesi
- Kamera koordinatlarının projeksiyon matrisindeki koordinatlara dönüştürülmesi
//+------------------------------------------------------------------+ //| 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(); }
Örneğimizde, küp yalnızca bir kez oluşturulmaktadır ve üzerinde başka bir değişiklik olmamaktadır. Dolayısıyla, tuval üzerindeki çerçevenin yalnızca yeniden boyutlandırma gibi fiyat grafiğinde değişiklikler olduğunda güncellenmesi gerekecektir. Böyle bir değişiklik durumunda, tuval boyutu mevcut fiyat grafiği boyutlarına göre ayarlanır, projeksiyon matrisi sıfırlanır ve tuval üzerindeki görüntü güncellenir.
//+------------------------------------------------------------------+ //| 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 Uzman Danışmanını başlatalım. Siyah arka plan üzerinde beyaz bir kare görüyoruz. Varsayılan olarak, nesneler oluşturulurken renk beyaz olarak ayarlanır. Işıklandırmayı henüz ayarlamadık.
Beyaz küp ve uzaydaki yerleşimi
Burada, X ekseni sağa doğru, Y ekseni yukarı doğru ve Z ekseni de 3D sahnenin içerisine doğru yönlü haldedir. Böyle bir koordinat sistemi solak olarak adlandırılır.
Küpün merkezi, koordinatları X=0, Y=0 ve Z=6 olan noktadadır. Küpe baktığımız konum, varsayılan değer olan koordinatların merkezindedir. Bakış noktasının konumunu değiştirmek istiyorsak, ViewPositionSet() fonksiyonunu kullanarak uygun koordinatları açıkça ayarlamalıyız.
Programdan Escape tuşuyla çıkılabilir.
Nesnenin Z ekseni ve bakış noktası etrafında döndürülmesi
Sahneyi biraz canlandırmak için küpü Z ekseni etrafında döndürelim. Bunu yapmak için bir zamanlayıcı ekleyelim - olaylarına bağlı olarak küp saat yönünün tersine döndürülecektir.
Nesnenin Z ekseni etrafında belirli bir açıyla dönmesini sağlamak adına DXMatrixRotationZ() metodunu kullanarak bir dönme matrisi oluşturalım. Ardından onu TransformMatrixSet() metoduna parametre olarak iletelim. Bu, küpün uzaydaki konumunu değiştirecektir. Devamında da tuval üzerindeki görüntüyü güncellemek için tekrar Redraw() fonksiyonunu çağıralım.
//+------------------------------------------------------------------+ //| 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(); }
Çalıştırdıktan sonra dönen beyaz bir kare göreceğiz.
Küp Z ekseni etrafında saat yönünün tersine dönmektedir
Bu örneğin kaynak kodu Step2 Rotation Z.mq5 dosyasındadır. Sahne oluşturulurken artık M_PI/5 açısının belirtildiğine ve bu açının önceki örnekteki M_PI/6 açısından daha büyük olduğuna lütfen dikkat edin.
//--- 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
Ancak aynı zamanda ekrandaki küpün boyutu da görsel olarak küçülmüştür. Projeksiyon matrisi ayarlanırken belirtilen bakış açısı ne kadar küçük olursa, nesne tarafından o kadar büyük çerçeve alanı işgal edilir. Bu, nesnelere teleskopla bakmaya benzetilebilir: bakış açısı daha küçük olmasına rağmen nesne daha büyük görülür.
Kamera konumu yönetimi
CCanvas3D sınıfı, 3D sahnenin önemli parametrelerini ayarlamak için birbirine bağlı üç metoda sahiptir:
- ViewPositionSet - bakış noktasının konumunu ayarlar
- ViewTargetSet - bakışın yönlendirildiği noktanın koordinatlarını ayarlar
- ViewUpDirectionSet - çerçevenin üst sınırının yönünü ayarlar
Tüm bu parametreler birlikte kullanılır, yani 3D sahnede bu parametrelerden herhangi birini ayarlamak istiyorsak, diğer iki parametrenin de başlatılması gerekir. Bu, en azından sahne oluşturma aşamasında yapılmalıdır. Bunu, çerçevenin üst sınırının sağa ve sola sallandığı aşağıdaki örnekte göstereceğiz. Bu amaçla Create() metoduna aşağıdaki üç satırı ekleyelim:
//+------------------------------------------------------------------+ //| 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() metodunu, ufuk vektörünün sağa ve sola sallanmasını sağlayacak şekilde değiştirelim.
//+------------------------------------------------------------------+ //| 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(); }
Örneği Step3 ViewUpDirectionSet.mq5 olarak kaydedelim ve çalıştıralım. Aslında hareketsiz olmasına rağmen sallanan bir küp görüyoruz. Bu efekt, kameranın kendisi sağa ve sola sallandığında elde edilir.
Üst sınır sağa ve sola sallanmaktadır
Nesne rengi yönetimi
Kodumuzu şu şekilde değiştirelim: küpü koordinatların merkezine koyalım ve kamerayı hareket ettirelim.
//+------------------------------------------------------------------+ //| 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); }
Ayrıca küpü maviye boyayalım. Renk, alfa kanallı RGB renk formatında belirtilir (alfa kanalı en son belirtilir), ancak değerler bire normalleştirilir. Böylece, 1 değeri 255, 0,5 değeri de 127 anlamına gelir.
X ekseni etrafında dönme ekleyelim ve değişiklikleri Step4 Box Color.mq5 olarak kaydedelim.
Sağ üstten dönen küpe bakış
Dönme ve hareket
Nesneler aynı anda üç yönde döndürülebilir ve hareket ettirilebilir. Nesnelerdeki tüm değişiklikler matrisler kullanılarak gerçekleştirilir. Ayrı ayrı olarak hesaplanabilirler - dönme ve hareket. Örneğimizi tekrar değiştirelim: bu sefer kamera küpe ön üstten bakıyor olsun.
//+------------------------------------------------------------------+ //| 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); }
Sırayla dönme ve hareket matrisleri oluşturalım, elde edilen dönüşüm matrisini uygulayalım ve küpü görüntüleyelim. Değişiklikleri Step5 Translation.mq5 dosyasına kaydedelim ve çalıştıralım.
Küpte dönme ve hareket
Kameranın hareketsiz olduğuna ve koordinatların merkezine biraz yukarıdan doğrultulduğuna dikkat edin. Küp üç yönde döndürüldü ve sağa, aşağıya ve sahnenin içerisine doğru hareket ettirildi.
Işıklandırmayla çalışma
Gerçekçi bir üç boyutlu görüntü elde etmek için nesne yüzeyindeki her noktanın ışıklandırmasının hesaplanması gerekir. Bu, şu üç ışıklandırma bileşeninin yoğunluğunu hesaplayan Phong gölgelendirme modeli kullanılarak yapılır: ortam, dağınık ve düzgün. Bu amaçla, aşağıdaki parametreler ayarlanır:
- DirectionLight - yönlü ışıklandırmanın yönü CCanvas3D'de ayarlanır
- AmbientLight - ortam ışıklandırmasının rengi ve yoğunluğu CCanvas3D'de ayarlanır
- DiffuseColor - dağınık ışıklandırma bileşeni CDXMesh ve alt sınıflarında ayarlanır
- EmissionColor - arka plan ışıklandırma bileşeni CDXMesh ve alt sınıflarında ayarlanır
- SpecularColor - düzgün ışıklandırma bileşeni CDXMesh ve alt sınıflarında ayarlanır
Phong gölgelendirme modeli
Işıklandırma modeli standart gölgelendiricilerde uygulanır, model parametreleri CCanvas3D'de ayarlanır, nesne parametreleri de CDXMesh ve alt sınıflarında ayarlanır. Örneğimizi aşağıdaki gibi değiştirelim:
- Küpü koordinatların merkezine getirelim.
- Beyaza ayarlayalım.
- Sahneyi yukarıdan aşağıya doğru ışıklandıran yönlü sarı ışık kaynağı ekleyelim.
- Ortam ışıklandırması için mavi rengi ayarlayalım.
//--- 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));
Yönlü ışık kaynağının konumunun Canvas3D'de ayarlanmadığına, yalnızca ışığın yayıldığı yönün ayarlandığına lütfen dikkat edin. Yönlü ışık kaynağının sonsuz bir mesafede olduğu ve sahneyi tam paralel bir ışık akışının ışıklandırdığı kabul edilir.
m_canvas.LightDirectionSet(DXVector3(0.0,-1.0,0.0));
Burada, ışık yayılma vektörü, Y ekseninde negatif yönde, yani yukarıdan aşağıya doğrudur. Ayrıca, yönlü ışık kaynağı için parametreler (LightColorSet ve LightDirectionSet) ayarlanırsa, ortam ışığının rengi de (AmbientColorSet) ayarlanmalıdır. Çünkü varsayılan olarak ortam ışığının rengi maksimum yoğunlukta beyaza ayarlanır ve dolayısıyla tüm gölgeler beyaz olur. Bu, sahnedeki nesnelerin ortam ışıklandırmasından gelen beyaz ışıkla dolup taşacağı, yönlü ışıklandırmanın ise beyaz ışık tarafından kesintiye uğrayacağı anlamına gelir.
//--- 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
Aşağıdaki gif animasyonu, ışıklandırma eklediğimizde görüntünün nasıl değiştiğini göstermektedir. Örneğin kaynak kodu Step6 Add Light.mq5 dosyasındadır.
Mavi ortam ışığında sarı ışık kaynağı altında yeşil parıltıya sahip beyaz küp
Nasıl çalıştığını görmek adına yukarıdaki koddaki renk metotlarını kapatabilirsiniz.
Animasyon
Animasyon, sahne ve nesne parametrelerinde zaman içerisinde değişiklik olmasıdır. Mevcut tüm parametreler zamana/olaylara bağlı olarak değiştirilebilir. Sahnenin güncellenmesini kontrol edecek 10 milisaniyelik bir zamanlayıcı ayarlayalım:
int OnInit() { ... //--- create canvas ExtAppWindow=new CCanvas3DWindow(); if(!ExtAppWindow.Create(width,height)) return(INIT_FAILED); //--- set timer EventSetMillisecondTimer(10); //--- return(INIT_SUCCEEDED); }
Zamanlayıcının olayları için CCanvas3DWindow sınıfına nesne parametrelerini (dönme ve hareket) ve ışıklandırma yönünü değiştirecek işleyici ekleyelim:
//+------------------------------------------------------------------+ //| 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(); }
Lütfen nesne değişikliklerinin ilk değerlerin üzerine yapıldığını unutmayın, yani sanki her zaman küpün ilk durumunu ele alıyormuşuz ve dönme ve hareketle ilgili tüm işlemleri sıfırdan uyguluyormuşuz gibi, bu da küpün mevcut durumunun kaydedilmediği anlamına gelir. Ancak, ışık kaynağının yönü ise mevcut değerden itibaren deltatime artışlarıyla değiştirilir.
Dinamik olarak değişen ışık kaynağı yönüne sahip dönen küp
Sonuç olarak, çok karmaşık bir 3D animasyon elde ediyoruz. Örnek kod Step7 Animation.mq5 dosyasındadır.
Fareyle kamera konumunun kontrolü
3D grafiklerdeki son animasyon unsurunu ele alalım: kullanıcı eylemlerine tepki. Örneğimize fareyle kamera konumunu kontrol etme özelliği ekleyelim. Bunu yapmak için fare olaylarını alıyoruz ve uygun işleyicileri oluşturuyoruz:
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);
Fare hareketi olayları için CCanvas3DWindow sınıfına sol fare tuşuna basılıyken fare hareket ettirildiğinde kamera yönü açılarını değiştirecek işleyici ekleyelim:
//+------------------------------------------------------------------+ //| 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; } }
Ayrıca, fare tekerleği olayları için de kameranın sahnenin merkezine olan mesafesini değiştirecek işleyici ekleyelim:
//+------------------------------------------------------------------+ //| 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(); }
Her iki işleyici de güncellenen parametrelere göre kamera konumunu güncellemek için UpdateCameraPosition() metodunu çağırır:
//+------------------------------------------------------------------+ //| 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)); }
Kaynak kodu Step8 Mouse Control.mq5 dosyasındadır.
Fareyle kamera konumunun kontrolü
Dokular uygulama
Dokular, nesneye materyal, kabartma yanılsaması vermek için çokgen yüzeyine uygulanan bitmap görüntüleridir. Dokular, çokgenler kullanılarak oluşturulması aşırı derecede kaynak gerektirecek tekrarlı küçük yüzey nesnelerinin görünümünü sunmaya olanak tanır. Örneğin bu materyaller taş, ahşap, toprak vb. olabilir.
CDXMesh ve alt sınıfları doku ayarlamaya olanak sağlar. Doku, standart piksel gölgelendiricide DiffuseColor ile birlikte kullanılır. Nesne animasyonunu kaldıralım ve ona bir taş dokusu uygulayalım. Dokular, terminalin çalışma klasörünün MQL5\Files klasöründe bulunmalıdır:
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); }
Taş dokulu küp
Özel nesneler oluşturma
Tüm nesneler köşelerden (DXVector3) oluşur ve köşeler de indekslerle belirli temel öğelere bağlanır. En yaygın temel öğe üçgendir. Basit bir 3D nesne oluşturulması için şunların varlığı gereklidir: köşelerin en azından koordinatlarının (ancak normal, renk vb. gibi birçok ek veri de içerebilir) listesi, köşelerin bağlandıkları temel öğe türü ve temel öğelerin indekslerinin listesi oluşturulması gerekir.
Standart Kütüphane, DXVertex köşe türüne sahiptir: koordinatı, ışıklandırma hesaplaması için normali, doku koordinatlarını ve rengi içerir. Standart köşe gölgelendirici bu köşe türüyle çalışır.
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 yardımcı dosyası, temel öğelerin geometrisinin (köşeler ve indeksler) oluşturulması ve obj dosyalarından 3D geometri yüklenmesi için bir dizi metot içerir.
Bir küre ve bir torus oluşturalım ve onlara aynı taş dokusunu uygulayalım:
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); }
Yeni nesneler için animasyon ekleyelim:
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(); }
Değişiklikleri Three Objects.mq5 olarak kaydedelim ve çalıştıralım.
Küpün yörüngesinde dönen şekiller
Veri tabanlı 3D yüzey
Rapor oluşturmak ve verileri analiz etmek için genellikle çizgi grafikleri, histogramlar, pasta grafikleri vb. gibi çeşitli grafikler kullanılır. MQL5, bu amaçlar için kullanışlı bir grafik kütüphanesi sunmaktadır, ancak bu kütüphane yalnızca 2D grafikler oluşturabilmektedir.
CDXSurface sınıfı, iki boyutlu bir dizide depolanan özel verilerin kullanılarak bir yüzey görselleştirilmesine olanak sağlar. Aşağıdaki matematiksel fonksiyon örneğini görselleştirelim:
z=sin(2.0*pi*sqrt(x*x+y*y))
Yüzeyi çizmek için bir nesne ve verileri depolamak için bir dizi oluşturalım:
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); }
Yüzey, tabanı 4x4 ve yüksekliği 1 olan bir kutu içerisinde çizilecektir. Doku boyutları 0,25x0,25'tir.
- SF_TWO_SIDED, kameranın yüzeyin altına hareket etmesi durumunda yüzeyin hem yukarı hem de aşağı çizileceğini belirtir.
- SF_USE_NORMALS, yönlü ışık kaynağının neden olduğu yüzeyden yansımaları hesaplamak için normallerin kullanılacağını belirtir.
- CS_COLD_TO_HOT, yüzeyin ısı haritasını yeşil ve sarı geçişiyle maviden kırmızıya ayarlar.
Yüzeyi canlandırmak için sinüs işaretinin altına zaman ekleyelim ve onu zamanlayıcı ile güncelleyelim.
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(); } }Kaynak kodu 3D Surface.mq5 dosyasındadır. Çalışması aşağıdaki videoda gösterilmektedir:
Bu makalede, görsel veri analizi için basit geometrik şekiller ve animasyonlu 3D grafikler oluşturma konusunda DirectX fonksiyonlarının yeteneklerini ele aldık. MetaTrader 5 terminalinin klasöründen daha karmaşık örnekler bulabilirsiniz: Correlation Matrix 3D ve Math 3D Morpher Uzman Danışmanları ve Remnant 3D komut dosyası.
MQL5'in yardımıyla, üçüncü taraf paketlere başvurmadan algoritmik ticaretin önemli görevlerini çözebilirsiniz:
- Çok sayıda girdi parametresi içeren karmaşık ticaret stratejilerini optimize etme
- Optimizasyon sonuçlarını alma
- Alınan verileri en uygun üç boyutlu grafikte görselleştirme
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/7708





- Ücretsiz ticaret uygulamaları
- 24 saat boyunca ücretsiz Forex VPS
- Ticaret kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Web sitesi politikasını ve kullanım şartlarını kabul edersiniz