( تعداد نمایش : 1686 )

مباحث پیشرفته Direct3D

موضوع : مروری بر مباحث قبلی – ساخت یک موتور گرافیکی سه بعدی

قبل از شروع مباحث جدید برنامه نویسی Direct3D ، با هم مروری بر مباحث قبلی خواهیم داشت . ( مباحث قبلی در آرشیو موجود می باشند ) .
در این درس با استفاده از مطالب قبلی یک Engine سه بعدی ساخته و از امکانات آن در یک برنامه نمونه استفاده خواهیم کرد .
این engine دارای دو کلاس است :
۱ – کلاس 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
روتین ها و توابع این کلاس عبارتند از :
۱ – InitD3D : این روتین ، اشیا D3D و D3Ddevice را می سازد و پارامترهای آنها را تنظیم می کند .
۲ – ApplyCameraChanges : روتین ایجاد ماتریس View
3 – SetupMatrices : روتین ایجاد ماتریس Projection
4 – StartRender : در این روتین عملیات لازم برای شروع عمل رندر صورت می گیرد .
۵ – RenderObject : این تابع ، یک شی سه بعدی از نوع کلاس D3Dobject را می گیرد و بردارهای مورد نیاز و نیز بافت شی را تنظیم می کند و در پایان شی را ترسیم می کند .
۶ – FinishRender : در این روتین به عملیات رندر پایان داده می شود .
۷ – Cleanup: روتین از بین بردن اشیا Direct3D
8 – CreateVector : تابع ساخت یک بردار سه بعدی
۹ – CreateTextures : روتین ساخت یک بافت جدید
۱۰ – InitTexture: تابع مقداردهی به یک بافت
در کلاس D3Dobject متغیرها و توابع لازم برای ایجاد یک شی سه بعدی و اختصاص بافت به آن موجود می باشد .
در این کلاس دو type عمومی تعریف شده است :
۱ – NormalVERTEX
2 – TeturedVERTEX
همچنین روتین ها و توابع این کلاس عبارتند از :
۱ – InitObject : تابعی که تنظیمات اولیه vertex ها و بافت شی را انجام می دهد .
۲ – Vertex : روتین ایجاد vertex های مورد نیاز
۳ – 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 های ۳D Studio Max در Direct3D
تا بحال ما هر شیی را که می خواستیم در Direct3D بسازیم خودمان بوسیله کد نویسی آنرا توصیف کرده ایم . ممکنست این سوال برایتان پیش آمده باشد که بازیهای تجاری برای تولید کاراکترهای و اشیا پیچیده سه بعدی چگونه عمل می کنند ؟
منطقی بنظر نمی رسد که اینگونه مدلهای پیچیده بصورت کد وارد برنامه شده اند زیرا نیاز به هزاران خط برنامه برای هر فریم خواهد بود . بجای اینکار ما object های خود را توسط برنامه های دیگری می سازیم و آنها را در برنامه خودمان load می کنیم سپس بافتها و material های مورد نظر را به آنها اختصاص داده و در پایان آنها را رندر می کنیم . مزیت دیگر اینکار اینست که شما می توانید براحتی فایل object خود را تغییر دهید و مدلهایی با جزئیات متفاوت برای برنامه خود قرار دهید .
مراحل ساخت چنین برنامه هایی بصورت زیر است :

۱ – ساخت object سه بعدی :
اولین چیزی که بایستی بدانید داشتن دانش پایه ای از چگونگی مدلسازی سه بعدی است . همچنین نیاز به یک نرم افزار مدلسازی مثل ۳D Studio Max دارید .

بعد از ساخت مدل خود در Max نیاز به یک Convertor دارید تا فایلهای Max را به فایلهای Direct3D که با فرمت “X.” هستند تبدیل کنید .
Convertor های زیادی برای تبدیل فایلهای نرم افزارهای مدلسازی به فایلهای “X.” وجود دارند که برخی از آنها عبارتند از :
- برنامه PolyTrans3D System Translation
- برنامه Deep Exploration 2.0
- برنامه Quick3D
- برنامه ۳DWin
- DirectX Explorer Plugin
- ابزارهای موجود در DirectX 8.0 SDK که عبارتند از :
برنامه Conv3DS برای تبدیل فایلهای ۳DS به فایلهای X
DX SDK Exporter Plugin برای تبدیل فایلهای ۳DS و 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,D3DDevice,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_DEFAULT,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 برای ذخیره سازی اشکال سه بعدی

مقدمه : مکعبی که در درسهای قبلی ساختیم را درنظر بگیرید . با دانشی که اکنون دارید ، دو راه برای ساخت یک مکعب داریم : ۱ – استفاده از ۳۶ عدد vertex برای تعریف face های مکعب ۲ – ساخت مکعب با استفاده از یک مدلساز و ذخیره آن با فرمت X
روش اول غیرکارامد است زیرا شما بایستی از تعداد زیادی vertex برای یک شکل بسیار ساده استفاده کنید . روش دوم مناسب است اما زمانیکه بخواهیم رنگها و بافتها را تغییر دهیم دچار مشکل خواهیم شد . روش جدیدی که امروز در مورد آن صحبت می کنم استفاده ار Index Buffer است .
Index Buffer شامل یکسری عدد integer است که این اعداد مرجعی برای vertex های ذخیره شده در یک Vertex Buffer هستند . برای مثال فرض کنید یک Vertex Buffer شامل ۸ عدد 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


حال باید در یک حلقه با استفاده از تابع twen پیکسلهای CubeVertices را update کنیم :

Private Sub UpdateAnimation()x
Dim I As Integer
به روز کردن پارامترهای زمان و جهت’
If AnimTweenDir = True Then
AnimTweenFactor = AnimTweenFactor + (((GetTickCount() – LastTimeTweened) / 1000)*1#)
LastTimeTweened = GetTickCount
If AnimTweenFactor >= 1# Then
AnimTweenFactor = 1#
AnimTweenDir = False
End If
Else
AnimTweenFactor = AnimTweenFactor – (((GetTickCount() – LastTimeTweened) / 1000)*1#)
LastTimeTweened = GetTickCount
If AnimTweenFactor <= 0# Then
AnimTweenFactor = 0#
AnimTweenDir = True
End If
End If
به روز کردن اطلاعات vertex ها ‘
For I = 0 To 7
CubeVertices(I) = TweenVertices(CubeVerticesSource(I), CubeVerticesDest(I), AnimTweenFactor)x
Next I
به روز کردن بافر vertex’
If D3DVertexBuffer8SetData(VBuffer, 0, Len(CubeVertices(0)) * 8, 0, CubeVertices(0)) = D3DERR_INVALIDCALL Then GoTo Error:
Exit Sub
Error:
Debug.Print “Error occured whilst updating the animation…”x
End Sub


زمان پایه انیمیشن توسط عبارت زیر تنظیم می شود :
(((GetTickCount() – LastTimeTweened) / 1000) * 1#)
همانطور که می دانید دو نوع انیمیشن وجود دارد : انیمیشن بر مبنای frame و انیمیشن بر مبنای زمان . در انیمیشن بر مبنای frame شماره فریم با یک مقدار ثابت در زمان افزایش می یابد اما اگر اینکار باعث می شود کیفیت انیمیشن در کامپیوترهای با سرعت متفاوت تغییر کند . بنابراین انیمیشن را بر مبنای زمان تولید کرده ایم . انیمیشن های بر مبنای زمان بجای ” ۱ فریم در هر سیکل ” ، ” ۳۰ فریم در هر ثانیه ” هستند .

۲ – روش دوم از توابع کتابخانه D3DX برای انجام عمل tweening استفاده می کند و بنابراین بهبودی در سرعت انیمیشن نسبت به روش بالا حاصل می شود . با استفاده از کتابخانه D3DX می توانیم عمل درون یابی خطی را برای تمام اجزا اصلی یک vertex انجام دهیم . لیست زیر توابعی را برای اینکار نشان می دهد :
- تابع D3DXVec3Lerp : انجام درون یابی برای موقعیت و نرمال :

D3DXVec3Lerp( VOut as D3DVECTOR, V1 as D3DVECTOR, V2 as D3DVECTOR, S as Single)x
- VOut = The result of the interpolation
- V1 = The source coordinates
- V2 = The destination coordinates
- S = The interpolation amount – between, but not limited to, 0.0 – 1.0 scale; where 0 is the source and 1 is the destination


- تابع D3DXColorLerp : انجام درون یابی برای رنگهای vertex :

D3DXColorLerp( COut as D3DCOLORVALUE, C1 as D3DCOLORVALUE, C2 as D3DCOLORVALUE, S as Single)x
- COut = The resulting colour
- C1 = The source colour
- C2 = The destination colour
- S = The interpolant,

on a 0.0 to 1.0 scale
- تابع D3DXVec2Lerp : انجام درون یابی برای مختصاتهای دوبعدی :


- VOut = The result of this interpolation
- V1 = The source coordinates
- V2 = The destination coordinates
- S = The interpolant on a 0.0 to 1.0 scale

- تابع D3DXVec3Hermite : تولید یک مسیر منحنی که از دو نقطه کنترل عبور می کند :

D3DXVec3Hermite( VOut as D3DVECTOR, V1 as D3DVECTOR, T1 as D3DVECTOR, V2 as D3DVECTOR, T2 as D3DVECTOR, S as Single)x
- VOut = The Result
- V1 = The Source Coordinate
- T1 = The Tangent at the Source coordinate, this is the direction and speed the line will leave the source point.
- V2 = The Destination Coordinate
- T2 = The Tangent at the Destination coordinate, this is the direction and speed the line will enter the destination point.
- S = The Interpolant Value

برای اینکه بتوانیم از کتابخانه D3DX استفاده کنیم باید توصیف vertex هایمان را تغییر دهیم و بایستی یکسری مقادیر ARGB اضافی را به ساختار vertex اضافه کنیم :

Private Type LITVERTEX
X As Single
Y As Single
Z As Single
color As Long
specular As Long
tu As Single
tv As Single
ColorEx As D3DCOLORVALUE
End Type


حال تابع tween را بصورت زیر می نویسیم :

Private Function TweenVertices(Source As LITVERTEX, Dest As LITVERTEX, TweenAmount As Single) As LITVERTEX
Dim vResult As D3DVECTOR
Dim vResult2 As D3DVECTOR2
Tween کردن موقعیت vertex ها ‘
D3DXVec3Lerp vResult, MakeVector(Source.X, Source.Y, Source.Z), MakeVector(Dest.X, Dest.Y, Dest.Z), TweenAmount
TweenVertices.X = vResult.X
TweenVertices.Y = vResult.Y
TweenVertices.Z = vResult.Z
Tween کردن اطلاعات texture ’
D3DXVec2Lerp vResult2, MakeVector2D(Source.tu, Source.tv), MakeVector2D(Dest.tu, Dest.tv), TweenAmount
TweenVertices.tu = vResult2.X
TweenVertices.tv = vResult2.Y
Tween کردن اطلاعات رنگ ‘
D3DXColorLerp TweenVertices.ColorEx, Source.ColorEx, Dest.ColorEx, TweenAmount
With TweenVertices.ColorEx
TweenVertices.color = RGB(.B * 255, .G * 255, .R * 255)x
End With
End Function

نکته ای که باید به آن توجه کنید اینست که در تابع فوق برای اشاره به vertex ، یک بردار ساخته شده است ( توسط تابع MakeVector ) .

3 – روش سوم پر استفاده ترین روش انیمیشن سازی است . اگر شما انیمیشن های پیچیده با تعداد زیادی شی در آن داشته باشید و اگر بخواهید تغییرات اشیا را در هر فریم ذخیره کنید ، به حجم بالایی از منابع ذخیره سازی نیاز است . بجای آن ما با استفاده از یکسری فریم کلیدی ، فریمهای میانی را پیش بینی می کنیم .
برای انجام درون یابی فریم کلیدی ، بایستی مقدار vertex را در هر فریم کلیدی بدانیم و نیز بدانیم هر فریم کلیدی در چه زمانی ظاهر می شود . بنابراین باید برای هر انیمیشن چند فایل را بعنوان فریم کلیدی ذخیره کنیم .
در این درس ما داده های کلیدی انیمیشن را از یکسری فایل load می کنیم بنابراین تمام ثابتهای زمان keyframe درون برنامه قرار داده می شود ( شما می توانید خودتان یک ماژول بنویسید که انیمیشن های عمومی تر را نیز مدیریت کند . این ماژول باید قادر باشد که یک فرمت استاندارد فایل را import کند ، اشیا و texture های مربوطه را load نماید و سپس خودش ساخت انیمیشن را بطور اتوماتیک انجام دهد و برنامه اصلی فقط روتین render و یا update را فراخوانی کند ) . پس از جمع آوری اطلاعات فریم های کلیدی ، باید در هر زمان محاسبه کنیم که چه مدتی از شروع انیمیشن گذشته است و بنابراین انیمیشن در چه موقعیتی قرار دارد . سپس محاسبه می کنیم که فریم کلیدی قبلی و فریم کلیدی بعدی چیست همچنین حساب می کنیم در چه فاصله زمانی از ایندو قرار داریم . سرانجام یک درون یابی نرمال را انجام می دهیم تا اطلاعات فریم جاری بدست آید و این اطلاعات را درون یک شی Mesh می گذاریم و آنرا رندر می کنیم .
در درسهای قبلی در مورد load کردن اشیا از یک فایل X صحبت کردم اما در مورد چگونگی گرفتن اطلاعات vertex از یک شی Mesh صحبت نشد . کتابخانه D3DX برای اینکار دو تابع دارد :
- تابع D3DXMeshVertexBuffer8GetData : اطلاعات یک شی D3DXMesh را گرفته و در یک آرایه از D3DVERTEX ذخیره می کند :

D3DXMeshVertexBuffer8GetData( D3DXMeshobj As Unknown, Offset As Long, Size As Long, Flags As Long, Data As Any) As Long
- D3DXMeshobj As Unknown = A D3DXMESH object that you want to extract the data from.
- Offset As Long = How far into the vertex buffer we want to start reading, 0 is the beginning
- Size As Long = Size of the vertex buffer, this will be Len(D3DVERTEX) * Mesh.GetNumVertices
- Flags As Long = A combination of the CONST_D3DLOCKFLAGS, leave as 0.
- Data As Any = The first element in the array that you want the data to be read into, should be an array of D3DVERTEX vertices
- Return Code As Long = Returns D3D_OK for success, or either of D3DERR_INVALIDCALL or E_INVALIDARG for an error


- تابع D3DXMeshVertexBuffer8SetData : اطلاعات یک بافر vertex را در یک شی D3DXMesh قرار می دهد :

D3DXMeshVertexBuffer8SetData( D3DXMeshobj As Unknown, Offset As Long, Size As Long, Flags As Long, Data As Any) As Long
- D3DXMeshobj As Unknown = The D3DXMESH object that defines where you want the data to be placed
- Offset As Long = How far into the Destination vertex buffer you want to place the data
- Size As Long = The Size of the buffer in bytes, this will be Len(D3DVERTEX) * Mesh.GetNumVertices
- Flags As Long = A Combination of the CONST_D3DLOCKFLAGS, leave as 0
- Data As Any = The first element in the array of data you want placed in the mesh’s vertex buffer
- Return Code As Long = D3D_OK for success or D3DERR_INVALIDCALL or E_INVALIDARG for failure


عملیات انجام انیمیشن فریم کلیدی بصورت زیر است :
- load کردن اشیا از فایلهای X به درون شی D3DXMesh
- استخراج اطلاعات vertex از این شی
- انجام درون یابی بین فریمهای کلیدی
- قرار دادن اطلاعات vertex های درون یابی در یک شی D3DXMesh
فرض می کنیم که انیمیشن ما همیشه از زمان صفر تا زمان n باشد – برحیب میلی ثانیه – بنابراین می توانیم از GetTickCount برای توابع زمانی خود استفاده کنیم . همچنین یک ساختار را برای هر فریم کلیدی بصورت زیر تعریف می کنیم :

Private Type KeyFrame
شی load شده از یک فایل’ Mesh As D3DXMesh
آرایه material برای هر شی’ MatList() As D3DMATERIAL8
آرایه Texture’ TexList() As Direct3DTexture8
تعداد material ها و texture هایی که استفاده می کنیم’ nMaterials As Long
داده های vertex برای این فریم کلیدی’ VertexList() As D3DVERTEX
موقعیت این فریم کلیدی در انیمیشن’ TimeIndex As Long
End Type


حال باید تابعی بنویسیم که اطلاعات را از یک فایل X استخراج کرده و درون فریم کلیدی قرار دهد :

Private Function CreateKeyFrameFromFile(Filename As String, TexturePrefix As String, Time As Long) As KeyFrame
نام فایل X برای شی سه بعدی: Filename ’
پوشه ای که اطلاعات texture این شی در آن قرار دارد : TexturePrefix ’
اندیس زمان برای این فریم کلیدی : Time ‘
Dim I As Long
Dim XBuffer As D3DXBuffer
Dim TextureFile As String
Dim hResult As Long
‘خواندن اطلاعات از فایل ورودی به حافظه
Set CreateKeyFrameFromFile.Mesh = D3DX.LoadMeshFromX(Filename, D3DXMESH_MANAGED, D3DDevice, Nothing, XBuffer, CreateKeyFrameFromFile.nMaterials)x
تولید material ها و texture ها ‘
ReDim CreateKeyFrameFromFile.MatList(CreateKeyFrameFromFile.nMaterials) As D3DMATERIAL8
ReDim CreateKeyFrameFromFile.TexList(CreateKeyFrameFromFile.nMaterials) As Direct3DTexture8
For I = 0 To CreateKeyFrameFromFile.nMaterials – 1
D3DX.BufferGetMaterial XBuffer, I, CreateKeyFrameFromFile.MatList(I)x
CreateKeyFrameFromFile.MatList(I).Ambient = CreateKeyFrameFromFile.MatList (I).diffuse
TextureFile = D3DX.BufferGetTextureName(XBuffer, I)x
If TextureFile <> “” Then
Set CreateKeyFrameFromFile.TexList(I) = D3DX.CreateTextureFromFileEx(D3DDevice, TexturePrefix & TextureFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED,
D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR, 0, ByVal 0, ByVal 0)x
End If
Next I
استخراج داده های vertex’
ReDim CreateKeyFrameFromFile.VertexList(CreateKeyFrameFromFile.Mesh.GetNumVertices) As D3DVERTEX
hResult = D3DXMeshVertexBuffer8GetData(CreateKeyFrameFromFile.Mesh, 0, Len(CreateKeyFrameFromFile.VertexList(0)) * reateKeyFrameFromFile.Mesh.GetNumVertices, 0, CreateKeyFrameFromFile.VertexList(0))
CreateKeyFrameFromFile.TimeIndex = Time
End Function


در تابع Initialize خطوط زیر را برای ساخت فریم های کلیدی اضافه می کنیم :

nKeyFrames = 4
kfAnimLength = 2500
AnimLastStartAt = GetTickCount()x
ReDim kfAnim(nKeyFrames – 1) As KeyFrame
kfAnim(0) = CreateKeyFrameFromFile(App.Path & “\frame0.x”, App.Path & “\”, 0)x
kfAnim(1) = CreateKeyFrameFromFile(App.Path & “\frame1.x”, App.Path & “\”, kfAnimLength * (1 / 3))x
kfAnim(2) = CreateKeyFrameFromFile(App.Path & “\frame2.x”, App.Path & “\”, kfAnimLength * (2 / 3))x
kfAnim(3) = CreateKeyFrameFromFile(App.Path & “\frame3.x”, App.Path & “\”, kfAnimLength)x
kfCurrent = CreateKeyFrameFromFile(App.Path & “\frame0.x”, App.Path & “\”, 0)


دقت کنید که از یک اندیس زمان برای ساخت فریم های کلیدی استفاده شده است .
حال باید کدی برای نمایش دادن انیمیشن بنویسیم . ابتدا باید به روشی تغییرات فریمها را کنترل کنیم :

For I = 0 To nKeyFrames – 2
If CurrentTimeIndex >= kfAnim(I).TimeIndex Then
PrevFrame = I
NextFrame = I + 1
End If
Next I


سپس باید با توجه به زمان index دو فریم کلیدی و زمان جاری ، پارامتر درون یابی را محاسبه کنیم :

sTime = kfAnim(PrevFrame).TimeIndex
eTime = kfAnim(NextFrame).TimeIndex
cTime = CurrentTimeIndex
eTime = eTime – sTime
cTime = cTime – sTime
sTime = sTime – sTime
InterpolateAmount = cTime / eTime


سپس باید بر اساس این پارامتر عمل درون یابی را روی داده های vertex انجام دهیم :

For I = 0 To kfCurrent.Mesh.GetNumVertices
‘درون یابی مختصاتها
D3DXVec3Lerp vTemp3D, MakeVector(kfAnim(PrevFrame).VertexList(I).X, kfAnim(PrevFrame).VertexList(I).Y, _
kfAnim(PrevFrame).VertexList(I).Z), MakeVector(kfAnim(NextFrame).VertexList(I).X, kfAnim(NextFrame).VertexList(I).Y, _
kfAnim(NextFrame).VertexList(I).Z), InterpolateAmount
kfCurrent.VertexList(I).X = vTemp3D.X
kfCurrent.VertexList(I).Y = vTemp3D.Y
kfCurrent.VertexList(I).Z = vTemp3D.Z

‘درون یابی نرمالها
D3DXVec3Lerp vTemp3D, MakeVector(kfAnim(PrevFrame).VertexList(I).nx, kfAnim(PrevFrame).VertexList(I).ny, _
kfAnim(PrevFrame).VertexList(I).nz), MakeVector(kfAnim(NextFrame).VertexList(I).nx, kfAnim(NextFrame).VertexList(I).ny, _
kfAnim(NextFrame).VertexList(I).nz), InterpolateAmount
kfCurrent.VertexList(I).nx = vTemp3D.X
kfCurrent.VertexList(I).ny = vTemp3D.Y
kfCurrent.VertexList(I).nz = vTemp3D.Z

‘درون یابی اطلاعات بافت
D3DXVec2Lerp vTemp2D, MakeVector2D(kfAnim(PrevFrame).VertexList(I).tu, kfAnim(PrevFrame).VertexList(I).tv), _
MakeVector2D(kfAnim(NextFrame).VertexList(I).tu, kfAnim(NextFrame).VertexList(I).tv), InterpolateAmount
kfCurrent.VertexList(I).tu = vTemp2D.X
kfCurrent.VertexList(I).tv = vTemp2D.Y
Next I


حال باید داده تولید شده را به فرمت Mesh برگردانیم :

hResult = D3DXMeshVertexBuffer8SetData(kfCurrent.Mesh, 0, Len(kfCurrent.VertexList(0)) * kfCurrent.Mesh.GetNumVertices, 0, kfCurrent.VertexList(0))x


با استفاده از روش فوق می توانید هر تعداد فریم کلیدی را به انیمیشنتان اضافه کنید . اشکالی که روش فوق دارد اینست که اطلاعات texture برای تمام فریمهای کلیدی جداگانه ذخیره شده است در حالیکه texture در تمام فریمها ثابت است . در درسهای بعدی از روشی بنام texture pooling استفاده می کنیم تا تنها یک کپی از texture ها نگهداری کنیم .

دیدگاه خود را بیان کنید.

باید وارد سایت شده باشید برای دیدگاه دادن