در این درس در مورد روشهای ساخت انیمیشن در 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 شماره فریم با یک مقدار ثابت در زمان افزایش می یابد اما اگر اینکار باعث می شود کیفیت انیمیشن در کامپیوترهای با سرعت متفاوت تغییر کند . بنابراین انیمیشن را بر مبنای زمان تولید کرده ایم . انیمیشن های بر مبنای زمان بجای " 1 فریم در هر سیکل " ، " 30 فریم در هر ثانیه " هستند . 2 – روش دوم از توابع کتابخانه 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 می گذاریم و آنرا رندر می کنیم .