-
طراحي فرم هاي دلخواه براي برنامه هاي ويژوال بيسيک
فرض کنيد يک image غير مستطيلي داريد که مي خواهيد از آن بعنوان فرم برنامه تان استفاده کنيد
۱ - تصوير فرم مورد نظرتان را طراحي کرده و با فرمت bmp ذخيره کنيد . دقت نماييد که بايستي image خود را درون يک کادر مستطيلي قرار دهيد که با يک رنگ با RGB مشخص رنگ آميزي شده است :
۲ - يک فرم ويژوال بيسيک ايجاد کنيد و خاصيت BorderStyle آنرا صفر نماييد .
3 - در متد Form Load بايستي image مورد نظر را به فرمتان assign کنيد :
Me.picture=loadpicture(yourimagename)x
Me.width=Me.picture.width
Me.height=Me.picture.height
4 - سپس بايستي يک ناحيه از اين image بسازيد که نسبت به رنگ RGB اي که در بالا به آن اشاره کردم transparent باشد . اگر فرض کنيم اين رنگ ، رنگ سياه باشد ( r=0 , g=0, b=0 ) :
LRegion=MakeRgn(yourimagename,0,0,0)x
5 - حال بايستي ناحيه مشخص شده را بعنوان فرم برنامه تان قرار دهيد :
call SetWindowRgn(Me.hwnd,LRegion,True)x
6 - يک ماژوال ايجاد کنيد و خطوط زير را در آن بنويسيد :
Public Declare Function SetWindowRgn Lib "user32" (ByVal hwnd As Long, ByVal hRgn As Long, ByVal bRedraw As Boolean) As Long
Public Declare Function MakeRgn Lib "Region.dll" (ByVal FileName As String, ByVal R As Integer, ByVal g As Integer, ByVal b As Integer) As Long
Public Declare Function DeleteRgn Lib "Region.dll" (ByVal Region As Long)x
Global lRegion As Long
7 - در متد Form Unload عبارت زير را قرار دهيد :
Call DeleteRgn(LRegion)x
نکته ۱: بوسيله روتين زير مي توانيد فرم خود را در وسط صفحه قرار دهيد :
Sub CenterForm(frm As Form)
frm.Left = (Screen.Width - frm.Width) / 2
frm.Top = (Screen.Height - frm.Height) / 2
End Sub
نکته ۲ : براي دريافت Region.dll با من تماس بگيريد .
-
قرار دادن آيکون برنامه در کنار ساعت ويندوز ۲
براي قرار دادن آيکون برنامه در system tray ابتدا يک ماژول تعريف کرده و اطلاعات زير را در آن قرار دهيد :
ابتدا تعريف constant هاي مورد نياز :
Public Const WM_RBUTTONUP = &H205
Global Const WM_MOUSEMOVE = &H200
Global Const NIM_ADD = 0
Global Const NIM_DELETE = 2
Global Const NIM_MODIFY = 1
Global Const NIF_ICON = 2
Global Const NIF_MESSAGE = 1
Global Const ABM_GETTASKBARPOS = &H5
سپس تعريف يک type با نام RECT براي نشان دادن يک مستطيل :
Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
سپس تعريف يک type با نام NOTIFYICONDATA براي توصيف آيکون :
Type NOTIFYICONDATA
cbSize As Long
hwnd As Long
uID As Long
uFlags As Long
uCallbackMessage As Long
hIcon As Long
szTip As String * 64
End Type
حال تعريف يک type با نام APPBARDATA براي توصيف اطلاعات application bar :
Type APPBARDATA
cbSize As Long
hwnd As Long
uCallbackMessage As Long
uEdge As Long
rc As RECT
lParam As Long
End Type
دو متغير را بصورت زير تعريف مي کنيم :
Global Notify As NOTIFYICONDATA
Global BarData As APPBARDATA
حال نياز به declare کردن توابع Shell_NotifyIcon و SHAppBarMessage از کتابخانه shell32 داريم :
Private Declare Function Shell_NotifyIcon Lib "shell32.dll" Alias "Shell_NotifyIconA" (ByVal dwMessage As Long, lpData As NOTIFYICONDATA) As Long
Private Declare Function SHAppBarMessage Lib "shell32.dll" (ByVal dwMessage As Long, pData As APPBARDATA) As Long
روتين قراردادن آيکون بصورت زير است :
Sub AddIcon(Form1 As Form, IconID As Long, Icon As Object, ToolTip As String)x
Dim Result As Long
BarData.cbSize = 36&
Result = SHAppBarMessage(ABM_GETTASKBARPOS, BarData)x
Notify.cbSize = 88&
Notify.hwnd = Form1.hwnd
Notify.uID = IconID
Notify.uFlags = NIF_ICON Or NIF_MESSAGE Or NIF_TIP
Notify.uCallbackMessage = WM_MOUSEMOVE
Notify.hIcon = Icon
Notify.szTip = ToolTip & Chr$(0)x
Result = Shell_NotifyIcon(NIM_ADD, Notify)x
End Sub
روتين حذف آيکون بصورا زير است :
Sub delIcon(IconID As Long)
Dim Result As Long
Notify.uID = IconID
Result = Shell_NotifyIcon(NIM_DELETE, Notify)
End Sub
در فرم مورد نظرتان ابتدا يک متغير از نوع object تعريف کنيد :
Public IconObject As Object
در Form load عبارات زير را بنويسيد :
Set IconObject = Form.Icon
AddIcon Form, IconObject.Handle, IconObject, "TrayIcon"x
در Form unload عبارات زير را بنويسيد :
delIcon IconObject.Handle
delIcon Form.Icon.Handle
فرض کنيد يک منو با نام popmenu در فرم داريد و مي خواهيد با کليک راست روي آيکون برنامه در system tray ، آن منو باز شود . ابتدا visible اين منو را false کنيد و سپس متد زير را براي mousemove بنويسيد :
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Static Message As Long
Message = X / Screen.TwipsPerPixelX
Select Case Message
Case WM_RBUTTONUP:
Me.PopupMenu Popup
End Select
End Sub
-
اضافه کردن آيکون به منو
براي اضافه آيکون به منوهاي موجود در يک برنامه visual basic بايستي از توابع زير که موجود در کتابخانه User32 هستند استفاده کنيد :
۱ - GetMenu
۲ - GetSubMenu
۳ - GetMenuItemID
۴ - SetMenuIcon
ابتدا يک ماژول ايجاد کنيد و توابع فوق را در آن declare کنيد :
Public Declare Function GetMenu Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function GetSubMenu Lib "user32" (ByVal hMenu As Long, ByVal nPos As Long) As Long
Public Declare Function GetMenuItemID Lib "user32" (ByVal hMenu As Long, ByVal nPos As Long) As Long
Public Declare Function SetMenuItemBitmaps Lib "user32" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long, ByVal hBitmapUnchecked As Long, ByVal hBitmapChecked As Long) As Long
براي قرار دادن يک آيکون در کنار يکي از آيتمهاي منو نياز به handle فرم ، شماره منو ، شماره آيتم مورد نظر و نيز يک picture داريم :
Public Function SetMenuIcon(FrmHwnd As Long, MainMenuNumber As Long, MenuItemNumber As Long, Flags As Long, BitmapUncheckedHandle As Long, BitmapCheckedHandle As Long)x
Dim lngMenu As Long
Dim lngSubMenu As Long
Dim lngMenuItemID As Long
lngMenu = GetMenu(FrmHwnd)x
lngSubMenu = GetSubMenu(lngMenu, MainMenuNumber)x
lngMenuItemID = GetMenuItemID(lngSubMenu, MenuItemNumber)x
,SetMenuIcon = SetMenuItemBitmaps(lngMenu, lngMenuItemID, Flags
BitmapUncheckedHandle, BitmapCheckedHandle)x
End Function
image هاي مورد نظر خود را با ابعادي حدود 16*16 پيکسل و بصورت PictureBox در فرم خود قرار دهيد و خاصيت Visible مربوط به PictureBox ها را False کنيد .
سپس منوهاي خود را توسط Menu Editor طراحي کنيد .
File و Edit منوهاي اصلي هستند . پارامتر MainMenuNumber در تابع فوق شماره منوي اصلي است که براي File برابر صفر و براي Edit برابر يک مي باشد . پارامتر MenuItemNumber شماره هر آيتم در يک منو است که اين پارامتر نيز از صفر شروع مي شود .
اکنون براي اضافه کردن سه آيکون به سه آيتم منوي File کدهاي زير را در Form_Load بنويسيد :
Private Sub Form_Load()x
SetMenuIcon Me.hwnd, 0, 0, 0, pic1.Picture, pic1.Picture
SetMenuIcon Me.hwnd, 0, 1, 0, pic2.Picture, pic2.Picture
SetMenuIcon Me.hwnd, 0, 2, 0, pic3.Picture, pic3.Picture
-
کنترل WinSock - قسمت 1,2
مقدمه :
کنترل WinSock نسبت به تمام کنترلهاي اينترنت در سطح پايينتري قرار دارد . اين کنترل امکان ايجاد سرويسهاي شبکه اي مبتني بر پروتکلهاي TCP و UDP را مهيا مي کند . بعبارت ديگر توسط اين کنترل مي توان برنامه هاي کاربردي Client/Server ( سرويس گيرنده / سرويس دهنده ) ايجاد و با استفاده از پروتکل TCP و يا UDP بين آنها ارتباط برقرار نمود .
با تنظيم خصوصيات و فراخواني متدهاي اين کنترل مي توانيد به راحتي به يک کامپيوتر راه دور متصل شويد و داده ها را در هر دو جهت جابجا نمائيد . نمونه کاربرهايي که مي توان با اين کنترل ايجاد نمود :
Client-server chat ، Mail client ، Mail server ، Proxy Server ، Network Game ، Port Scanner ، پياده سازي الگوريتم هاي موازي و …
مباني TCP :
پروتکل کنترل اينترنت ( Transfer Control Protocol ) اجازه مي دهد يک اتصال ( Connection ) را از طريق سوکت ( socket ) به يک کامپيوتر راه دور ( Remote Computer ) ساخته و استفاده کنيد . با استفاده از اين اتصال ، هر دو کامپيوتر مي توانند داده ها را بين خودشان انتقال دهند . برقراري ارتباط از طريق TCP همانند صحبت کردن با تلفن است که بايد حتماً اتصالي بين دو کامپيوتر صورت گيرد تا بتوانند با هم ارتباط برقرار کنند .
اگر يک برنامه Client مي سازيد بايستي بدانيد که نام يا آدرس IP کامپيوتر Server چيست ( Remote Host IP ) و همچنين از طريق چه پورتي مي توانيد به آن متصل شويد ( Remote Port ) . حال بايستي به آن پورت Connect کنيد .
همچنين اگر يک برنامه Server مي سازيد بايستي پورتي را که روي آن به درخواستها گوش مي دهيد مشخص کنيد ( LocalPort ) و سپس به پورت گوش دهيد ( Listen ) .
زمانيکه يک کامپيوتر Client تقاضاي يک اتصال را مي دهد Server اين درخواست را Accept مي کند .
زمانيکه يک اتصال ساخته مي شود ، هر دو کامپيوتر مي توانند داده را فرستاده و دريافت کنند .
مباني UDP :
پروتکل ديتاگرام کاربر ( User Datagram Protocol ) پروتکلي بدون اتصال ( Connectionless ) است . برخلاف TCP ، کامپيوترها نياز به برپا کردن يک اتصال ندارند بنابراين يک برنامه مي تواند يک client و يا يک server باشد . برقراري ارتباط در UDP شبيه ارسال نامه از طريق پست است .
براي انتقال داده توسط UDP ابتدا بايد Local Port کامپيوتر Client تنظيم گردد . کامپيوتر Server تنها بايستي RemoteHost را برابر آدرس کامپيوتر Client قرار دهد و همچنين Remote Port را همان Local Port کامپيوتر Client قرار دهد . سپس دو کامپيوتر مي توانند داده ها را بين خود جابجا کنند .
استفاده از کنترل WinSock :
1 – انتخاب پروتکل: در زمان استفاده از کنترل WinSock اولين کاري که بايد انجام دهيد انتخاب يکي از پروتکلهاي TCP يا UDP است . طبيعت برنامه اي که شما مي سازيد نوع پروتکلي را که بايد استفاده کنيد مشخص مي کند . چند سوال زير به شما کمک مي کند که پروتکل مورد نيازتان را انتخاب کنيد :
- آيا برنامه شما در زمانيکه داده فرستاده مي شود يا دريافت مي شود نياز به اطلاعاتي از طرف Server يا Client دارد ؟ اگر چنين است بايستي يک اتصال TCP قبل از ارسال يا دريافت داده ايجاد شود .
- آيا داده بسيار بزرگ است ( مثل تصوير يا فايلهاي صوتي ) ؟ زمانيکه يک اتصال TCP ساخته مي شود پروتکل TCP اتصال را باقي نگه مي دارد و درستي ارسال داده تضمين شده است . اين اتصال در هر حال به منابع محاسباتي بيشتري نياز دارد و بنابراين پرهزينه تر است .
- آيا داده متناوب ارسال مي شود يا در يک نشست ( Session ) ارسال خواهد شد ؟ براي مثال اگر شما يک برنامه مي سازيد که کامپترهاي مشخصي را در يک زمان خاص از انجام شدن عملياتي مطلع مي کند پروتکل UDP مناسب تر است . پروتکل UDP همچنين براي ارسال مقادير کوچک داده اي مناست تر مي باشد .
2 – تنظيم پروتکل : براي تنظيم پروتکلي که مي خواهيد در برنامه تان از آن استفاده کنيد در زمان طراحي برنامه خاصيت Protocol کنترل WinSock را برابر sckTCPProtocol و يا sckUDPProtocol قرار دهيد . همچنين مي توانيد پروتکل خود را توسط کد زير تنظيم کنيد :
WinSock.Protocol=sckTCPProtocol
3 – مشخص کردن نام کامپيوتان : براي اتصال به کامپيوتر راه دور بايستي آدرس IP و يا نام کامپوتر را بدانيد .
نام کامپيوتر در Control Panel/Network/Identification موجود است . در صورتيکه مي خواهيد دو برنامه Client و Server خود را روي يک کامپيوتر تست کنيد از آدرس IP 127.0.0.1 براي هر دو استفاده کنيد اما اگر دو برنامه را روي دو کامپيوتر مجزا در شبکه قرار داده ايد با اجراي دستور ipconfig در DOS Prompt مي توانيد آدرس IP کامپيوتر ها را بدست آوريد .
4 – ايجاد اتصال TCP : در زمان ساخت برنامه اي که از پروتکل TCP استفاده مي کند ابتدا بايد تصميم بگيريد که اين برنامه Client است يا Server . براي ساخت يک برنامه Server بايستي روي يک پورت خاص Listen کنيد . زمانيکه Client تقاضاي يک اتصال را مي دهد ، برنامه Server مي تواند آنرا Accept کند و بنابراين اتصال کامل شده است . حال Client و Server مي توانند با هم ارتباط داشته باشند .
مراحل زير ساخت يک سرور چت ساده بر مبناي TCP را نشان مي دهد :
- از منوي Project گزينه Components را انتخاب کنيد و در ليست Component ها مورد Microsoft WinSock 6.0 را انتخاب کنيد .
- يک کنترل WinSock در فرم خود قرار دهيد و نام آنرا tcpserver بگذاريد
- دو textbox با نامهاي txtSendData و txtReceiveData و نيز يک دکمه در فرم قرار دهيد .
- کد زير را در رويداد Form_Load بنويسيد :
Tcpserver.LocalPort=1000
tcpserver.Listen
- زمانيکه درخواستي از طرف Client مي آيد رويداد ConnectionRequest اجرا مي شود . در اين رويداد ابتدا بايد چک کنيد که حالت کنترل بسته باشد . اگر چنين نيست اتصال را قبل از پذيرفتن اتصال جديد ببنديد . سپس تقاضا را بر اساس پارامتر requestID مي پذيريم :
Private Sub tcpserver_ConnectionRequest(ByVal requestID As Long)
If tcpserver.State <> sckClosed Then tcpserver.Close
tcpserver.Accept requestID
End Sub
- حال اتصال بين Client و Server برقرار شده است . کد زير را براي event مربوط به کليک دکمه Send بنويسيد :
Tcpserver.SendData txtSendData.text
- اگر داده اي از طرف Client بيايد رويداد DataArrival اجرا مي شود . کد زير را براي اين رويداد بنويسيد :
Private Sub tcpserver_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
tcpserver.GetData strData
txtReceiveData.Text = strData
End Sub
- کد زير را براي رويداد Form_Unload بنويسيد :
Tcpserver.Close
مراحل ساخت يک TCP Client بصورت زير است :
- يک کنترل WinSock در فرم قرار دهيد و نام آنرا tcpclient بگذاريد .
- دو textbox با نامهاي txtsend و txtreceive و نيز يک دکمه با نام sendدر فرم قرار دهيد .
- يک دکمه با نام connect در فرم قرار دهيد .
- کد زير را براي متد Form_Load بنويسيد :
tcpclient.RemoteHost=”yourservername”x
tcpclient.RemotePort=1000
- کد زير را براي رويداد کليک شدن دکمه connect بنويسيد :
tcpclient.Connect
- کد زير را براي رويداد کليک شدن دکمه send بنويسيد :
tctclient.SendData txtsend.Text
- کد زير را براي رويداد DataArrival بنويسيد :
Private Sub tcpclient_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
tcpclient.GetData strData
txtreceive.Text = strData
End Sub
- کد زير را باري رويداد Form_Unload بنويسيد :
Tcpclient.Close
کدهاي فوق يک سيستم Client-Server ساده را نشان مي دهد . فايل exe هر دو برنامه را بسازيد و آنها را اجرا کنيد تا بتوانيد سيستم خود را تست کنيد .
5 – پذيرفتن بيش از يک تقاضاي اتصال : Server اي که در بالا ساخته شد تنها مي تواند تقاضاي يک اتصال را بپذيرد . با استفاده از ايجاد يک آرايه از کنترل WinSock مي توان چندين تقاضاي اتصال را پذيرفت . براي اينکار کافي است يک کپي ( instance ) از کنترل بسازيم ( با تنظيم خاصيت Index ) و متد Accept را براي instance جديد بکار ببريم . فرض کنيد يک کنترل WinSock با نام sckServer در فرم داريم که خاصيت Index آنرا صفر قرار داده ايم . همچنين يک متغير intMax از نوع Long تعريف مي کنيم که تعداد اتصالات همزمان به Server را نگه مي دارد . در event مربوط به Form_Load کد زير را بنويسيد :
intMax=0
sckServer(0).LocalPort=1000
sckServer(0).Listen
هر بار که تقاضاي يک اتصال مي رسد کد ابتدا تست مي کند که مقدار Index چقدر است . اگر مقدار Index صفر باشد متغير intMax يکي افزايش مي يابد و از intMax براي ساخت يک instance جديد از کنترل استفاده مي شود . حال از اين instance براي پذيرفتن تقاضاي اتصال استفاده مي گردد . براي اينکار کد زير را براي رويداد ConnectionRequest بنويسيد :
Private Sub sckServer_ConnectionRequest(Index As Integer, ByVal requestID As Long)
If Index = 0 Then
intmax = intmax + 1
Load sckServer(intmax)x
sckServer(intmax).LocalPort = 0
sckServer(Index).Accept requestID
End If
End Sub
6 – ايجاد اتصال UDP : ساخت يک برنامه UDP ساده تر از برنامه هاي TCP است زيرا پروتکل UDP به اتصال نياز ندارد . در برنامه TCP بالا يک کنترل WinSock بايستي حتماً Listen مي کرد و يک کنترل ديگر يک اتصال را توسط متد Connect ايجاد نمود . در عوض پروتکل UDP نيازي به اتصال ندارد . براي ارسال داده بين دو کنترل WinSock سه مرحله بايستي انجام شود :
- پارامتر RemoteHost برابر نام کامپيوتر مقابل است .
- پارامتر RemotePort برابر پارامتر LocalPort کامپيوتر مقابل
- استفاده از متد Bind براي مشخص کردن LocalPort
چون هر دو کامپيوتر از نظر ارتباط مساوي هستند ، اين نوع برنامه ها را Peer-to-Peer گويند . براي نمونه از کد زير براي ساخت يک برنامه chat استفاده مي کنيم :
- يک کنترل WinSock در فرم قرار دهيد و نام آنرا udppeerA بگذاريد .
- خاصيت Protocol آنرا UDPProtocol قرار دهيد .
- دو textbox با نامهاي txtsend و txtreceive و نيز يک دکمه در فرم قرار دهيد .
- کد زير را براي متد Form_Load بنويسيد :
udppeerA.RemoteHost=”nameofpeerB”x
udppeerA.RemotePort=1001
udppeerA.Bind 1002
- کد زير را براي event مربوط به کليک دکمه بنويسيد :
udppeerA.SendData txtsend.text
- کد زير را براي رويداد DataArrival بنويسيد :
Dim strData as String
udppeerA.GetData strData
txtreceive.Text=strData
براي ساخت UDP peerB مشابه مراحل بالا عمل کنيد فقط خاصيت RemoteHost آنرا نام کامپيوتر PeerA و خاصيت RemotePort آنرا 1002 و خاصيت Bind آنرا 1001 قرار دهيد .
-------------------
بررسی خواص کنترل WinSock :
ByteReceived : مقدار داده دريافت شده ( موجود در بافر receive ) را نشان مي دهد . توسط متد GetData مي توان اين داده را دريافت نمود .
LocalHostName : نام ماشين محلي را نشان مي دهد . اين پارامتر فقط خواندني است .
LocalIP : آدرس IP ماشين محلي را بصورت يک string برمي گرداند . اين پارامتر فقط خواندني است .
LocalPort : براي خواندن و يا تنظيم شماره پورت محلي بکار مي رود .
Protocol : براي خواندن و يا تنظيم پروتوکل مورد استفاده توسط کنترل WinSock بکار مي رود .
RemoteHost : براي خواندن و يا تنظيم نام يا آدرس IP ماشين راه دور بکار مي رود .
RemoteHostIP : آدرس IP ماشين راه دور را برمي گرداند :
۱- براي برنامه هاي Client بعد از زمانيکه يک اتصال توسط متد Connect پذيرفته شد ، اين خاصيت حاوي آدرس IP ماشين راه دور است .
۲ - براي برنامه Server ، بعد از آمدن يک Connection Request اين خاصيت شامل آدرس IP ماشين راه دور است .
۳ - در زمان استفاده از پروتکل UDP بعد از اينکه رويداد Data Arrival رخ داد اين خاصيت حاوي آدرس IP ماشيني است که داده را فرستاده .
RemotePort : براي خواندن و يا تنظيم شماره پورت ماشين راه دوري که مي خواهيد به آن متصل شويد بکار مي رود .
SocketHandle : مقداري را برمي گرداند که مرتبط با سوکتي است که کنترل WinSock را مديريت مي کند و براي ارتباط با لايه WinSock بکار مي رود . اين پارامتر فقط خواندني است و تنها براي ارسال به API هاي WinSock طراحي شده است .
State : وضعيت کنترل WinSock را نشان مي دهد . وضعيتهاي ممکن براي State عبارتند از :
۱ - sckClosed : اتصال بسته است .
۲ - sckOpen : اتصال باز است .
۳ - sckListening : حالت گوش دادن به پورت
4 - sckConnectionPending : معلق شدن اتصال
۵ - sckResolvingHost : تصميم گيري در مورد ميزبان
۶ - sckHostResolved : در مورد ميزبان تصميم گيري شد .
۷ - sckConnecting : حالت برقراري ارتباط
۸ - sckConnected : ارتباط برقرار شد .
۹ - sckClosing : حالت قطع اتصال
۱۰ - sckError : حالت خطا
بررسی متدهای کنترل WinSock :
متد Accept : تنها براي برنامه هاي TCP Server بکار مي رود . اين متد براي پذيرفتن يک اتصال در زمان مديريت رويداد ConnectionRequest استفاده مي شود .
متد Bind : اين پارامتر LocalPort و LocalIP يک اتصال را مشخص مي کند .
متد Close : براي بستن يک اتصال TCP و يا بستن يک listening socket بکار مي رود .
متد GetData : بلوک جاري داده دريافت شده را گرفته و آنرا در متغيري از نوع Variant ذخيره مي کند . شکل کلي اين متد بصورت زير است :
WinSock.GetData data[,type][,maxlen]x
که data داده دريافتي است . اگر داده کافي موجود نباشد data برابر empty خواهد بود .
type نوع داده دريافتي است که مي تواند مقادير زير باشد :
vbByte - vbInteger - vbLong - vbSingle - vbDouble - vbDate - vbBoolean - vbError - vbString - vbArray+vbByte
maxlen حداکثر سايز را در زمان دريافت يک byte Array و يا يک string مشخص مي کند .
متد Getdata در رويداد Data Arrival استفاده مي شود که اين رويداد يک پارامتر با نام TotalBytes دارد . اگر maxlen اي که شما تعيين کرده ايد کمتر از TotalBytes باشد پيغام هشدار شماره ۱۰۰۴۰ دريافت مي کنيد بدين معني که بايتهاي باقيمانده گم خواهند شد .
متد Listen : يک سوکت مي سازد و آنرا در حالت Listen قرار مي دهد . اين متد تنها در اتصالات TCP بکار ميرود .
متد PeekData : مشابه GetData است با اين تفاوت که داده را از صف ورودي حذف نمي کند . اين متد تنها براي اتصالات TCP بکار مي رود .
متد SendData : براي ارسال داده به کامپيوتر راه دور بکار مي رود .
بررسي event هاي کنترل WinSock :
رويداد Close : زماني رخ مي دهد که کامپيوتر راه دور اتصال را ببندد .
رويداد Connect : بعد از اينکه يک اتصال به Server ايجاد شد روي مي دهد . شکل کلي آن بصورت زير است :
Private Sub WinSock_Connect(ErrorOccurred As Boolean)x
که پارامتر ErrorOccurred دو مقدار دارد : اگر True باشد يعني اتصال Fail شده است و اگر False باشد يعني اتصال با موفقيت انجام شده است .
با رويداد Connect مي توانيد error هايي که در زمان فرايند باز کردن اتصال برگردانده شده را چک کنيد .
رويداد ConnectionRequest : زماني رخ مي دهد که يک کامپيوتر راه دور تقاضاي يک اتصال را بدهد . اين رويداد فقط براي برنامه هاي TCP Server بکار مي رود .
رويداد DataArrival : زماني رخ مي دهد که داده جديدي بيايد .
رويداد Error : زماني رخ مي دهد که يک خطا در فرايند ارتباط رخ دهد ( مثلاً Failed to Connect و يا Failed to Send ) . شکل کلي آن بصورت زير است :
Private WinSock_Error(number as Integer,description as String,scode as Long,source as String,helpfile as String,helpcontext as Long,canceldisplay as Boolean)x
number شماره کد خطا است .
description توضيحي در مورد خطا است .
source توصيف منبع خطا
canceldisplay : مشخص مي کند آيا پيغام خطاي پيش فرض نشان داده شود يا نه
رويداد SendComplete : زماني رخ مي دهد که يک عمل Send تکميل شده باشد .
رويداد SendProgress : زماني رخ مي دهد که کنترل شروع به ارسال داده نمايد . شکل کلي آن بصورت زير است :
WinSock_SendProgress (bytesSent As Long, bytesRemaining As Long)x
که bytesSent تعداد بايتهاي ارسال شده و bytesRemaining تعداد بايتهاي باقيمانده است .
نکته ۱ : براي دريافت جدول خطاهاي WinSock با من تماس بگيريد .
نکته ۲ : موضوع بعدي : آشنايي با الگوريتم Collision Detection در ساخت انيميشن هاي دوبعدي
-
مباحث پيشرفته Direct3D -از مقدمه تا دروس پایانی
موضوع : مروري بر مباحث قبلي - ساخت يک موتور گرافيکي سه بعدي
قبل از شروع مباحث جديد برنامه نويسي Direct3D ، با هم مروري بر مباحث قبلي خواهيم داشت . ( مباحث قبلي در آرشيو موجود مي باشند ) .
در اين درس با استفاده از مطالب قبلي يک Engine سه بعدي ساخته و از امکانات آن در يک برنامه نمونه استفاده خواهيم کرد .
اين engine داراي دو کلاس است :
1 – کلاس MainD3D
2 – کلاس D3Dobject
در کلاس MainD3D متغيرها و توابع لازم براي ساخت يک device سه بعدي ، تنظيمات ماتريسي ، تابع رندر و غيره موجود مي باشد .
متغيرهاي عمومي اين کلاس عبارتند از :
Public g_DX As New DirectX8
Public g_D3D As Direct3D8
Public g_D3DX As New D3DX8
Public g_D3DDevice As Direct3DDevice8
Public NTextures As Long
روتين ها و توابع اين کلاس عبارتند از :
1 - InitD3D : اين روتين ، اشيا D3D و D3Ddevice را مي سازد و پارامترهاي آنها را تنظيم مي کند .
2 – ApplyCameraChanges : روتين ايجاد ماتريس View
3 – SetupMatrices : روتين ايجاد ماتريس Projection
4 – StartRender : در اين روتين عمليات لازم براي شروع عمل رندر صورت مي گيرد .
5 – RenderObject : اين تابع ، يک شي سه بعدي از نوع کلاس D3Dobject را مي گيرد و بردارهاي مورد نياز و نيز بافت شي را تنظيم مي کند و در پايان شي را ترسيم مي کند .
6 – FinishRender : در اين روتين به عمليات رندر پايان داده مي شود .
7 – Cleanup: روتين از بين بردن اشيا Direct3D
8 – CreateVector : تابع ساخت يک بردار سه بعدي
9 – CreateTextures : روتين ساخت يک بافت جديد
10 – InitTexture: تابع مقداردهي به يک بافت
در کلاس D3Dobject متغيرها و توابع لازم براي ايجاد يک شي سه بعدي و اختصاص بافت به آن موجود مي باشد .
در اين کلاس دو type عمومي تعريف شده است :
1 - NormalVERTEX
2 - TeturedVERTEX
همچنين روتين ها و توابع اين کلاس عبارتند از :
1 – InitObject : تابعي که تنظيمات اوليه vertex ها و بافت شي را انجام مي دهد .
2 – Vertex : روتين ايجاد vertex هاي مورد نياز
3 – GetRenderingMode: تابعي که مد رندر را مشخص مي کند .
و نيز يکسري تابع ساخت vertex نرمال و ساخت vertex داراي بافت و غيره
اين دو کلاس در يک پروژه ويژوال بيسيک قرارداده شده و پروژه با نام D3Dengine.dll کامپايل شده است .
حال با استفاده از اين engine مي خواهيم يک منظره سه بعدي را ايجاد کنيم :
اين منظره شامل سه object است : ديوار ، آسمان و زمين.
ابتدا بايد يک شي از کلاس MainD3D تعريف کنيم :
Dim D3D8Main As MainD3D8
در متد Form Load نيز سه شي Floor ، Sky و Wall را بصورت زير تعريف مي کنيم :
Dim Floor As D3DObject
Dim Sky As D3DObject
Dim Walls As D3Dobject
سپس اين سه شي را به اضافه شي D3D8Main ، ايجاد مي کنيم :
Set D3D8Main = New D3DEngine.MainD3D8
Set Floor = New D3DEngine.D3DObject
Set Sky = New D3DEngine.D3DObject
Set Walls = New D3DEngine.D3Dobject
در ابتدا شي MainD3D را Initial مي کنيم و سپس بافتهاي مورد نيز خود را مي سازيم :
D3D8Main.InitD3D True, Me.hWnd
D3D8Main.CreateTextures 3
D3D8Main.InitTexture 1, App.Path + "\floor.jpg"
D3D8Main.InitTexture 2, App.Path + "\sky.bmp"
D3D8Main.InitTexture 3, App.Path + "\wall.bmp"
حال به سراغ ايجاد و مقداردهي vertex هاي floor مي رويم . floor شامل شش vertex مي باشد و بنابراين دو face مثلثي دارد :
Floor.InitObject 6, 2, TriangleList, True, 1
Floor.Vertex 0, -55, -2, -55, vbWhite, 0, 10
Floor.Vertex 1, 55, -2, -55, vbWhite, 10, 10
Floor.Vertex 2, 55, -2, 55, vbWhite, 10, 0
Floor.Vertex 3, -55, -2, -55, vbWhite, 0, 10
Floor.Vertex 4, 55, -2, 55, vbWhite, 10, 0
Floor.Vertex 5, -55, -2, 55, vbWhite, 0, 0
سپس به سراغ ايجاد و مقداردهي vertex هاي wall مي رويم . wall شامل بيست و چهار vertex مي باشد و بنابراين هشت face مثلثي دارد :
Walls.InitObject 24, 8, TriangleList, True, 3
Walls.Vertex 0, -55, -2, -55, &HBCE8FC, 0, 1
Walls.Vertex 1, 55, -2, -55, &HBCE8FC, 5, 1
Walls.Vertex 2, 55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 3, -55, -2, -55, &HBCE8FC, 0, 1
Walls.Vertex 4, 55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 5, -55, 8, -55, &HBCE8FC, 0, 0
Walls.Vertex 6, -55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 7, 55, -2, 55, &HBCE8FC, 5, 1
Walls.Vertex 8, 55, 8, 55, &HBCE8FC, 5, 0
Walls.Vertex 9, -55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 10, 55, 8, 55, &HBCE8FC, 5, 0
Walls.Vertex 11, -55, 8, 55, &HBCE8FC, 0, 0
Walls.Vertex 12, -55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 13, -55, -2, -55, &HBCE8FC, 5, 1
Walls.Vertex 14, -55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 15, -55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 16, -55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 17, -55, 8, 55, &HBCE8FC, 0, 0
Walls.Vertex 18, 55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 19, 55, -2, -55, &HBCE8FC, 5, 1
Walls.Vertex 20, 55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 21, 55, -2, 55, &HBCE8FC, 0, 1
Walls.Vertex 22, 55, 8, -55, &HBCE8FC, 5, 0
Walls.Vertex 23, 55, 8, 55, &HBCE8FC, 0, 0
ادامه دارد
-
حال به سراغ ايجاد و مقداردهي vertex هاي sky مي رويم . sky شامل شش vertex مي باشد و بنابراين دو face مثلثي دارد :
Sky.InitObject 6, 2, TriangleList, True, 2
Sky.Vertex 0, -55, 8, -55, &HBCE8FC, 0, 1
Sky.Vertex 1, 55, 8, -55, &HBCE8FC, 0, 1
Sky.Vertex 2, 55, 8, 55, &HBCE8FC, 0, 1
Sky.Vertex 3, -55, 8, -55, &HBCE8FC, 0, 1
Sky.Vertex 4, 55, 8, 55, &HBCE8FC, 0, 1
Sky.Vertex 5, -55, 8, 55, &HBCE8FC, 0, 1
در پايان تابع رندر را صدا مي کنيم . البته در هر بار عمل رندر کردن ، دوربين يک درجه در صفحه X-Z دوران مي کند تا کل ديوار قابل مشاهده باشد :
Dim Angle As Double
PI = 3.1415
Angle = 0
Do
DoEvents
D3D8Main.StartRender vbBlack
D3D8Main.RenderObject Sky
D3D8Main.RenderObject Floor
D3D8Main.RenderObject Walls
D3D8Main.FinishRender
If Sqr(Angle ^ 2) = 360 Then Angle = 0
Angle = Angle + 1
D3D8Main.CamLookAtX = Sin((Angle * 2 * PI) / 360)
D3D8Main.CamLookAtZ = Cos((Angle * 2 * PI) / 360)
D3D8Main.ApplyCameraChanges
Loop
موضوع : استفاده از object هاي 3D Studio Max در Direct3D
تا بحال ما هر شيي را که مي خواستيم در Direct3D بسازيم خودمان بوسيله کد نويسي آنرا توصيف کرده ايم . ممکنست اين سوال برايتان پيش آمده باشد که بازيهاي تجاري براي توليد کاراکترهاي و اشيا پيچيده سه بعدي چگونه عمل مي کنند ؟
منطقي بنظر نمي رسد که اينگونه مدلهاي پيچيده بصورت کد وارد برنامه شده اند زيرا نياز به هزاران خط برنامه براي هر فريم خواهد بود . بجاي اينکار ما object هاي خود را توسط برنامه هاي ديگري مي سازيم و آنها را در برنامه خودمان load مي کنيم سپس بافتها و material هاي مورد نظر را به آنها اختصاص داده و در پايان آنها را رندر مي کنيم . مزيت ديگر اينکار اينست که شما مي توانيد براحتي فايل object خود را تغيير دهيد و مدلهايي با جزئيات متفاوت براي برنامه خود قرار دهيد .
مراحل ساخت چنين برنامه هايي بصورت زير است :
۱ - ساخت object سه بعدي :
اولين چيزي که بايستي بدانيد داشتن دانش پايه اي از چگونگي مدلسازي سه بعدي است . همچنين نياز به يک نرم افزار مدلسازي مثل 3D Studio Max داريد .
بعد از ساخت مدل خود در Max نياز به يک Convertor داريد تا فايلهاي Max را به فايلهاي Direct3D که با فرمت "X." هستند تبديل کنيد .
Convertor هاي زيادي براي تبديل فايلهاي نرم افزارهاي مدلسازي به فايلهاي "X." وجود دارند که برخي از آنها عبارتند از :
- برنامه PolyTrans3D System Translation
- برنامه Deep Exploration 2.0
- برنامه Quick3D
- برنامه 3DWin
- DirectX Explorer Plugin
- ابزارهاي موجود در DirectX 8.0 SDK که عبارتند از :
برنامه Conv3DS براي تبديل فايلهاي 3DS به فايلهاي X
DX SDK Exporter Plugin براي تبديل فايلهاي 3DS و Max به فايلهاي X
از بين اين برنامه ها و plugin ها من برنامه Deep Exploration را به شما پيشنهاد مي کنم .
در آدرس زير مي توانيد اطلاعات بيشتري در مورد اين برنامه بدست آوريد و همچنين آنرا Download کنيد :
Deep Exploration 2.0
s/n: 0XE2A0000000000
Authorization s/n: REJ1HYXSR1A77Q10
2 - Load کردن يک Object ساخته شده :
زمانيکه فايل X شي مورد نظر را ساختيد ، load کردن آن در direct3D ساده است . براي اينکار نياز به يک مش داريم که اطلاعات شي ما را نگهداري کند :
Dim Mesh As D3DXMesh
همچنين براي اختصاص material و texture به شي ، نياز به تعريف متغيرهاي زير داريم :
Dim MeshMaterial As D3DMATERIAL8
Dim MeshTexture As Direct3DTexture8
حال به سراغ بازنويسي روتين InitGeometry مي رويم :
- تعريف متغيرهاي مورد نياز :
Dim mtrlBuffer as D3DXBuffer
Dim TextureFile as String
Dim n as Long
- گرفتن داده هاي شي از فايل X :
Set Mesh=D3DX.LoadMeshFromX app.path&"\"&"yourfilename",D3DMESH_MANAGED,D3DDev ice,Nothing,mtrlBuffer,n
- استخراج اطلاعات materiasl شي و تنظيم پارامتر Ambient :
D3DX.BufferGetMaterial mtrlBuffer,0,MeshMaterial
MeshMaterial.Ambient=MeshMaterial.Diffuse
- استخراج نام بافت بکار رفته براي شي :
TextureFile=D3DX.BufferGetTextureName(mtrlBuffer,0 )x
- ساخت بافت :
If TextureFile<>"" Then
Set MeshTexture=D3DX.CreateTextureFromFile D3DDevice,app.path&"\"&TextureFile,128,128,D3DX_DE FAULT,0,
D3DFMT_UNKNOWN,D3DPOOL_MANAGED,D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR,0,Byval 0,Byval 0
End If
۳ - رندر نمودن شي : رندر نمودن شي چندان مشکل نيست اما همچنان بايد ماتريسها و تبديلاتي را که مي خواهيد ، خودتان مديريت کنيد .
D3DDevice.SetMaterial MeshMaterial
D3DDevice.SetTexture 0,MeshTexture
Mesh.DrawSubset 0
-
موضوع : مباحث تکميلي نورپردازي در Direct3D
در بخش اول آموزش Direct3D با مباني نورپردازي آشنا شديد . در اين درس قصد دارم آن مباحث را کاملتر برايتان مطرح کنم .
نورپردازي يکي از بخشهاي مهم طراحي يک بازي و يا يک انيميشن سه بعدي است . بمنظور پياده سازي نورپردازي يک صحنه ابتدا بايد با تئوري آن آشنا شويد .
تئوري نورپردازي : نورپردازي در Direct3D تخميني از چگونگي عملکرد نور در دنياي واقعي مي باشد . چهار نوع اصلي نور در Direct3D قابل استفاده است ( همچنين شما مي توانيد خودتان انواع جديدي از نور ايجاد کنيد که موضوع ما نيست ) :
۱ - Point Light : توسط يک نقطه در فضاي سه بعدي ايجاد مي شود و داراي سه پارامتر رنگ ، دامنه و تضعيف مي باشد . دامنه يک نور مسافتي است که نور مي تواند طي کند . تضعيف ، مقدار کاهش نور در اثر افزايش مسافت مي باشد . نور نقطه اي در تمام جهات تششع مي کند - شبيه يک لامپ حبابي و يا يک شمع
۲ - Spot Light : داراي يک موقعيت و يک جهت است و تنها نور را در يک جهت خاص مي تاباند - شبيه يک چراغ قوه . اين نور داراي يک زاويه مخروطي و يک دامنه است .
۳ - Directional Light : داراي موقعيت نيست و براي پياده سازي نورهايي که از فاصله بسيار دور مي آيند - مثل خورشيد - مناسب است .
۴ - Ambient Light : اين نور تضمين مي کند که تمام vertex هاي يک صحنه تاريکتر از يک رنگ خاص نباشند .
عملي کردن نورپردازي : ضمن اينکه اغلب کارت هاي گرافيک سه بعدي از نورپردازي پشتيباني مي کنند اما اين نکته بايد مورد توجه قرار گيرد که با افزايش تعداد نور در يک صحنه محاسبات Direct3D بيشتر مي شود و اين باعث کند شدن رندر صحنه خواهد شد و بنابراين کارت هاي گرافيکي سه بعدي نيز داراي يک ماکزيمم تعداد نور هستند - مثلاً ۱۶ نور در GeForce 2 - همچنين توجه داشته باشيد که نورهاي مختلف داراي زمان پردازشي متفاوتي هستند . نور ambient سريعترين زمان پردازشي را دارد ، سپس نورdirectional ، سپس نور point و کندترين آنها Spot Light است .
همچنين نکته ديگري که بايد توجه کنيد دامنه نور است . اگر نور ، يک منطقه بزرگي را پوشش دهد بر تعداد زيادي از vertex ها تاثير مي گذارد و اين باعث افزايش محاسبات مي شود .
نورپردازي Specular - که در درسهاي بعدي در مورد آن صحبت مي کنم و براي ايجاد اشيا درخشان استفاده مي شود - نيز زمان پردازشي زيادي دارد و بهتر است کمتر از آن استفاده شود .
پارامتر ديگري که بايد در نظر بگيريد جزئيات هندسه شما مي باشد . هر چه پيچيدگي صحنه بيشتر باشد ، نورپردازي نيز زمان بيشتري را مصرف مي کند .
سايه زني نيز يک بخش بسيار پيچيده در مدل سازي نور است و محاسبات آن بسيار زمان گير خواهد بود بنابراين Direct3D مستقيماً محاسبات سايه زني را انجام نمي دهد بلکه رنگ نور را بر مبناي جهت هر مثلث scale مي کند بنابراين قسمت پشتي يک شي که رو به نور نيست ، هيچ نوري را دريافت نمي کند .
بردار نرمال : Direct3D هر vertex را بر مبناي بک بردار نرمال نورپردازي مي کند و نوري که يک vertex دريافت مي کند به زاويه بين نور و بردار نرمال آن vertex بستگي دارد . بردار نرمال توسط سه vertex يک face مثلثي ايجاد مي شود و اين بردار نرمال ساخته شده به vertex ها اختصاص مي يابد . بردار نرمال در واقع سمت يک مثلث را مشخص مي کند بنابراين اگر نور پشت مثلث باشد ، مثلث هيچ نوري را دريافت نميکند . بردار نرمال بايستي داراي طول ۱ باشد .
مراحل توليد بردار نرمال يک face مثلثي :
۱ - مطمئن شويد که face در جهت عقربه هاي ساعت ساخته شده است .
۲ - يک بردار از vertex شماره صفر به vertex شماره يک بسازيد .
۳ - يک بردار از vertex شماره صفر به vertex شماره دو بسازيد .
۴ - حاصلضرب برداري ( cross droduct ) اين دو بردار را بدست آوريد .
۵ - نتيجه حاصلضرب را نرمال کنيد .
Private Function GenerateTriangleNormals(p0 As UnlitVertex, p1 As UnlitVertex, p2 As UnlitVertex) As D3DVECTOR
Dim v01 As D3DVECTOR
Dim v02 As D3DVECTOR
Dim vNorm As D3DVECTOR
D3DXVec3Subtract v01, MakeVector(p1.X, p1.Y, p1.Z), MakeVector(p0.X, p0.Y, p0.Z)x
D3DXVec3Subtract v02, MakeVector(p2.X, p2.Y, p2.Z), MakeVector(p0.X, p0.Y, p0.Z)x
D3DXVec3Cross vNorm, v01, v02
D3DXVec3Normalize vNorm, vNorm
GenerateTriangleNormals.X = vNorm.X
GenerateTriangleNormals.Y = vNorm.Y
GenerateTriangleNormals.Z = vNorm.Z
End Function
اگر دو face در يک vertex مشترک باشند ( مثل گوشه دو ديوار ) براي توليد نرمال اين vertex ابتدا نرمال دو face را با روش فوق بدست آوريد سپس دو بردار نرمال را با هم جمع کنيد و در پايان بردار حاصلجمع را نرمال کنيد .
برپاسازي نورپردازي : اولين چيزي که قبل از برپاسازي نورپردازي بايستي اعمال کنيم تغيير ساختار vertex است . براي اينکار بايد پارامتر color را از ساختار vertex حذف و سه پارامتر را براي نگهداري نرمال اضافه کنيم :
Private Type UnlitVertex
X As Single
Y As Single
Z As Single
nx As Single
ny As Single
nz As Single
tu As Single
tv As Single
End Type
Const Unlit_FVF = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_TEX1)x
همچنين بايد براي تمام vertex هاي شي خود بردار نرمال را محاسبه کنيد براي مثال اگر شي شما يک مکعب است براي هر ۱۲ face آن بردار نرمال را بدست آوريد . در زير من کد لازم براي ساخت نرمال يکي از اين face ها را نوشته ام :
Cube2(0) = CreateVertex(-1, -1, 1, 0, 0, 0, 0, 0)x
Cube2(1) = CreateVertex(1, 1, 1, 0, 0, 0, 1, 1)x
Cube2(2) = CreateVertex(-1, 1, 1, 0, 0, 0, 0, 1)x
vN = GenerateTriangleNormals(Cube2(0), Cube2(1), Cube2(2))x
Cube2(0).nx = vN.X: Cube2(0).ny = vN.Y: Cube2(0).nz = vN.Z
Cube2(1).nx = vN.X: Cube2(1).ny = vN.Y: Cube2(1).nz = vN.Z
Cube2(2).nx = vN.X: Cube2(2).ny = vN.Y: Cube2(2).nz = vN.Z
براي برپا سازي نور ابتدا بايستي يک material به device خود اضافه کنيد :
Dim Mtrl As D3DMATERIAL8, Col As D3DCOLORVALUE
Col.a = 1: Col.r = 1: Col.g = 1: Col.b = 1
Mtrl.Ambient = Col
Mtrl.diffuse = Col
D3DDevice.SetMaterial Mtrl
سپس بايستي طوري device خود را تنظيم کنيد که نور شما را بشناسد - lights يک شي از نوع D3DLight8 است - يکبار که اين خط را بنويسيد مي توانيد از نور استفاده کنيد اما اگر خصوصيات نور را تغيير دهيد بايستي دوباره اين دستور را فراخواني کنيد :
D3DDevice.SetLight 0, Lights
حال بايد نور را روشن کنيد :
D3DDevice.LightEnable 0, 1
و در پايان بايد به Direct3D بگوئيد که نورپردازي را براي شما انجام دهد :
D3DDevice.SetRenderState D3DRS_LIGHTING, 1
-
چگونگي ايجاد يک نور : براي ايجاد هر يک از ۴ نوع اصلي نور بايد به روشي خاص عمل کنيد :
۱ - نورپردازي Ambient : اين نوع نورپردازي بسيار ساده است و تنها با فراخواني تابع SetRenderState ايجاد مي شود . رنگ ambient يک عدد هگزادسيمال بصورت RRGGBB است :
D3DDevice.SetRenderState D3DRS_AMBIENT, &H202020
۲ - نورپردازي Directional : داراي دو پارامتر رنگ و جهت مي باشد :
Lights.Type = D3DLIGHT_DIRECTIONAL
Lights.diffuse.r = 1
Lights.diffuse.g = 1
Lights.diffuse.b = 1
Lights.Direction = MakeVector(0, -1, 0)x
3 - نورپردازي Point : داراي سه پارامتر موقعيت ، رنگ و تضعيف مي باشد :
Lights.Type = D3DLIGHT_POINT
Lights.position = MakeVector(5, 0, 2)x
Lights.diffuse.b = 1
Lights.Range = 100
Lights.Attenuation1 = 0.05
۴ - نورپردازي Spot : اين نور داراي دو مخروط است که نقاط خارج مخروط اول روشنتر از نقاط داخل آن هستند . دو زاويه براي مخروط وجود دارد - زاويه داخلي theta و زاويه خارجي phi - که برحسب راديان هستند :
Lights.Type = D3DLIGHT_SPOT
Lights.position = MakeVector(-4, 0, 0)x
Lights.Range = 100
Lights.Direction = MakeVector(1, 0, 0)x
Lights.Theta = 30 * (Pi / 180)x
Lights.Phi = 50 * (Pi / 180)x
Lights.diffuse.g = 1
Lights.Attenuation1 = 0.05
موضوع : استفاده از Index Buffer براي ذخيره سازي اشکال سه بعدي
مقدمه : مکعبي که در درسهاي قبلي ساختيم را درنظر بگيريد . با دانشي که اکنون داريد ، دو راه براي ساخت يک مکعب داريم : ۱ - استفاده از 36 عدد vertex براي تعريف face هاي مکعب ۲ - ساخت مکعب با استفاده از يک مدلساز و ذخيره آن با فرمت X
روش اول غيرکارامد است زيرا شما بايستي از تعداد زيادي vertex براي يک شکل بسيار ساده استفاده کنيد . روش دوم مناسب است اما زمانيکه بخواهيم رنگها و بافتها را تغيير دهيم دچار مشکل خواهيم شد . روش جديدي که امروز در مورد آن صحبت مي کنم استفاده ار Index Buffer است .
Index Buffer شامل يکسري عدد integer است که اين اعداد مرجعي براي vertex هاي ذخيره شده در يک Vertex Buffer هستند . براي مثال فرض کنيد يک Vertex Buffer شامل 8 عدد vertex داريم که يک مکعب را براي ما توصيف مي کند . ما مي توانيم يک Index Buffer با ۳۶ عضو بسازيم بطوريکه ترتيب اتصال vertex ها را براي ما مشخص کنند . مثلاً Index هاي ۰ و ۱و ۳ براي مشخص کردن face شماره ۱ مکعب بکار مي روند . بنابراين بجاي استفاده از ۳۶ عدد vertex مي توانيم مکعب را با ۸ عدد vertex و يک Index Buffer بسازيم .
گرچه استفاده از Index Buffer بسيار کارامد است اما چندين محدوديت در استفاده از آن وجود دارد . مهمترين آنها اينست که تمام انديسهايي که يک vertex مشابه را share مي کنند بايستي خصوصيات مشابهي داشته باشند - موقعيت ، رنگ ، بافت و نرمال يکسان - براي مثال نمي توانيد مکعبي بسازيد که هر face آن يک رنگ داشته باشد .
-
ساخت Index Buffer : ابتدا به متغيرهاي زير نياز داريم :
Dim VBuffer as Direct3DVertexBuffer8
Dim IBuffer as Direct3DIndexBuffer8
Dim Vlist(0 to 7) as LITVERTEX
Dim Ilist(0 to 35) as Integer
تابع InitGeometry بصورت زير بازنويسي مي شود:
۱- توليد هشت vertex براي مکعب :
Vlist(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x
Vlist(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x
Vlist(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x
Vlist(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x
Vlist(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x
Vlist(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x
Vlist(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x
Vlist(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x
۲ - ايجاد Vertex Buffer توسط تابع CreateVertexBuffer :
Set VBuffer = D3DDevice.CreateVertexBuffer(Len(Vlist(0)) * 8, 0, Lit_FVF, D3DPOOL_DEFAULT)x
D3DVertexBuffer8SetData VBuffer, 0, Len(Vlist(0)) * 8, 0, Vlist(0)x
۳ - توليد index ها :
front '
Ilist(0) = 0: Ilist(1) = 1: Ilist(2) = 2
Ilist(3) = 1: Ilist(4) = 3: Ilist(5) = 2
Right '
Ilist(6) = 2: Ilist(7) = 3: Ilist(8) = 6
Ilist(9) = 3: Ilist(10) = 7: Ilist(11) = 6
Back '
Ilist(12) = 6: Ilist(13) = 7: Ilist(14) = 4
Ilist(15) = 7: Ilist(16) = 5: Ilist(17) = 4
Left '
Ilist(18) = 4: Ilist(19) = 5: Ilist(20) = 0
Ilist(21) = 5: Ilist(22) = 1: Ilist(23) = 0
Top '
Ilist(24) = 1: Ilist(25) = 5: Ilist(26) = 3
Ilist(27) = 5: Ilist(28) = 7: Ilist(29) = 3
Bottom '
Ilist(30) = 2: Ilist(31) = 6: Ilist(32) = 0
Ilist(33) = 6: Ilist(34) = 4: Ilist(35) = 0
۴ - ايجاد Index Buffer توسط تابع CreateIndexBuffer :
Set IBuffer = D3DDevice.CreateIndexBuffer(Len(Ilist(0)) * 36, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT)x
D3DIndexBuffer8SetData IBuffer, 0, Len(Ilist(0)) * 36, 0, Ilist(0)x
-
تابع Render : براي رندر کردن اين مکعب دو روش وجود دارد :
۱ - استفاده از تابع DrawIndexedPrimitive : در اين روش از VBuffer و IBUffer و آرايه vertex ها استفاده مي شود :
Public Sub Render()x
D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, 0, 1#, 0
D3DDevice.BeginScene
D3DDevice.SetStreamSource 0, VBuffer, Len(Vlist(0))x
D3DDevice.SetIndices IBuffer, 0
D3DDevice.DrawIndexedPrimitive D3DPT_TRIANGLELIST, 0, 36, 0, 12
D3DDevice.EndScene
D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub
۲ - استفاده از تابع DrawIndexedPrimitiveUP : در اين روش از آرايه هاي vertex و index استفاده مي شود :
Public Sub Render()x
D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, 0, 1#, 0
D3DDevice.BeginScene
D3DDevice.DrawIndexedPrimitiveUP D3DPT_TRIANGLELIST, 0, 8, 12, Ilist(0), D3DFMT_INDEX16, Vlist(0), Len(Vlist(0))x
D3DDevice.EndScene
D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub
موضوع : Vertex/Mesh Animation
در اين درس در مورد روشهاي ساخت انيميشن در Direct3D صحبت خواهيم کرد . انيميشن در فضاي سه بعدي در حالتهاي مختلفي مي تواند ايجاد شود که بسته به engine گرافيکي شما و ابزارهايي که ايجاد کرده ايد ، دارد . سه روش اصلي ساخت انيميشن وجود دارد که عبارتند از :
- Tween سازي دستي / درون يابي خطي ( manual tweening/linear interpolation )
- درون بابي برداري ( vector interpolation )
- درون يابي بر اساس فريم کليدي ( keyframe interpolation )
1 – روش اول يکي از ساده ترين راههاي ساخت انيميشن است . اين روش در زمانيکه با مدلهاي پيچيده سر و کار داريد مناسب نيست – و يا مدلهايي با تعداد زيادي vertex – اين روش نوعي tween کردن است که از مزيت index buffer ها استفاده مي کند .
درون يابي ، چگونگي تغييرات شيي در طول يک زمان مشخص مي باشد . در درسهاي قبلي شما درون يابي رنگ را روي يک شي ديديد که در آن يک رنگ بطور ملايم به رنگ ديگري تبديل مي شد ( fadeشدن ( . درون يابي خطي نيز مشابه آن است . براي درون يابي خطي از موقعيت A به موقعيت B از فرمول زير استفاده مي شود :
(B*V)+A*(1-V)
که A و B مختصاتهاي مبدا و مقصد هستند و V ضريب درون يابي است که عددي بين صفر و يک مي باشد . اين فرمول مختصات نقطه tween را در هر لحظه مشخص مي کند .
همانطور که مي بينيد بکار بردن اين فرمول براي يک شي با تعداد زيادي vertex بسيار وقت گير بوده و fram rate را پايين مي آورد .
تابع زير دو vertex و يک مقدار ضريب درون يابي را مي گيرد تا نقطه tween را محاسبه کند :
Private Function TweenVertices(Source As LITVERTEX, Dest As LITVERTEX, TweenAmount As Single) As LITVERTEX
TweenVertices.X = (Dest.X * TweenAmount) + Source.X * (1# - TweenAmount)x
TweenVertices.Y = (Dest.Y * TweenAmount) + Source.Y * (1# - TweenAmount)x
TweenVertices.Z = (Dest.Z * TweenAmount) + Source.Z * (1# - TweenAmount)x
TweenVertices.color = Source.color
End Function
اگر شما از vertex هاي UNLIT استفاده کنيد – vertex هايي با بردار نرمال – در اينصورت بايد کد فوق را تغيير دهيد و بايد tween را از نرمال مبدا به نرمال مقصد نيز انجام دهيد .
همانطور که مي بينيد رنگ tween vertex نيز تنظيم شده است . در يک تابع tweening مناسبتر مي توانيد رنگها ، مختصات بافت و مقادير specular را نيز tween کنيد .
محدوديتي که اين روش دارد اينست که خطي است و براي مدل کردن حرکتهاي غير خطي درست کار نمي کند .
حال مي خواهيم از تابع tween استفاده کنيم تا يک مکعب را در يک انيميشن به يک هرم تبديل کنيم . ابتدا سه شي را بصورت زير تعريف مي کنيم :
در ابتداي انيميشن ، شي current cube همان source cube است’
CubeVertices(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x
CubeVertices(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x
CubeVertices(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x
CubeVertices(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x
CubeVertices(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x
CubeVertices(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x
CubeVertices(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x
CubeVertices(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x
مکعب اوليه’
CubeVerticesSource(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x
CubeVerticesSource(1) = CreateLitVertex(-1, 1, -1, &HFF00&, 0, 0, 0)x
CubeVerticesSource(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x
CubeVerticesSource(3) = CreateLitVertex(1, 1, -1, &HFF00FF, 0, 0, 0)x
CubeVerticesSource(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x
CubeVerticesSource(5) = CreateLitVertex(-1, 1, 1, &HFFFF, 0, 0, 0)x
CubeVerticesSource(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x
CubeVerticesSource(7) = CreateLitVertex(1, 1, 1, &HFFFFFF, 0, 0, 0)x
هرم مقصد’
CubeVerticesDest(0) = CreateLitVertex(-1, -1, -1, &HFF0000, 0, 0, 0)x
CubeVerticesDest(1) = CreateLitVertex(-0.1, 1, -0.1, &HFF00&, 0, 0, 0)x
CubeVerticesDest(2) = CreateLitVertex(1, -1, -1, &HFF&, 0, 0, 0)x
CubeVerticesDest(3) = CreateLitVertex(0.1, 1, -0.1, &HFF00FF, 0, 0, 0)x
CubeVerticesDest(4) = CreateLitVertex(-1, -1, 1, &HFFFF00, 0, 0, 0)x
CubeVerticesDest(5) = CreateLitVertex(-0.1, 1, 0.1, &HFFFF, 0, 0, 0)x
CubeVerticesDest(6) = CreateLitVertex(1, -1, 1, &HFFCC00, 0, 0, 0)x
CubeVerticesDest(7) = CreateLitVertex(0.1, 1, 0.1, &HFFFFFF, 0, 0, 0)x