গতি মসৃণভাবে প্রদর্শিত হতে দুটি জিনিস গুরুত্বপূর্ণ, প্রথমটি স্পষ্টতই আপনি যখন রেন্ডারটি প্রত্যাশিত অবস্থার সাথে মিলিত হওয়া প্রয়োজন যখন ফ্রেমটি ব্যবহারকারীর সামনে উপস্থাপন করা হয়, দ্বিতীয়টি আপনাকে ব্যবহারকারীর সামনে ফ্রেম উপস্থাপন করতে হবে অপেক্ষাকৃত নির্ধারিত বিরতিতে। T + 10ms এ একটি ফ্রেম উপস্থাপন করা হবে, তারপরে অন্যটি টি + 30 মিমি, তারপরে অন্য টি + 40 মিমিতে, ব্যবহারকারীকে বিচার করবে বলে উপস্থিত হবে, এমনকি সেই সময়ের জন্য যা প্রদর্শিত হবে তা সিমুলেশন অনুসারে সঠিক হয়।
আপনি কেবল নিয়মিত বিরতিতে রেন্ডার হন তা নিশ্চিত করার জন্য আপনার প্রধান লুপটিতে কোনও গ্যাটিং মেকানিজম নেই। সুতরাং কখনও কখনও আপনি রেন্ডারগুলির মধ্যে 3 আপডেট করতে পারেন, কখনও কখনও আপনি 4ও করতে পারেন Bas মূলত আপনার লুপটি যতবার সম্ভব সম্ভব রেন্ডার হবে, যত তাড়াতাড়ি আপনি বর্তমান সময়ের সামনে সিমুলেশন রাষ্ট্রকে ধাক্কা দেওয়ার জন্য যথেষ্ট সময় সিমুলেট করেছেন, আপনি তারপরে সেই রাজ্যটি প্রেরণ করুন। তবে আপডেট বা রেন্ডার করতে কতক্ষণ সময় লাগে তার কোনও পরিবর্তনশীলতা এবং ফ্রেমের মধ্যে অন্তর পাশাপাশি পৃথক হবে। আপনার সিমুলেশনটির জন্য একটি স্থির টাইমস্টেপ পেয়েছেন তবে আপনার রেন্ডারিংয়ের জন্য একটি পরিবর্তনশীল টাইমস্টেপ।
আপনার সম্ভবত যা দরকার তা হ'ল আপনার রেন্ডারের ঠিক আগে অপেক্ষা, এটি নিশ্চিত করে যে আপনি কেবল রেন্ডার বিরতি শুরু করার সাথে সাথে রেন্ডারিং শুরু করে। আদর্শভাবে এটি অভিযোজিত হওয়া উচিত: যদি আপনি আপডেট / রেন্ডার করতে খুব বেশি সময় নিয়ে থাকেন এবং ইতিমধ্যে বিরতিটি শুরু হয়ে গেছে, আপনার অবিলম্বে রেন্ডার করা উচিত, তবে অন্তরালের দৈর্ঘ্যও বাড়িয়ে দেওয়া উচিত, যতক্ষণ না আপনি ধারাবাহিকভাবে রেন্ডার এবং আপডেট করতে পারবেন এবং এখনও পৌঁছাতে পারবেন না অন্তর শেষ হওয়ার আগে পরবর্তী রেন্ডার করুন। আপনার যদি অতিরিক্ত সময় দেওয়ার মতো সময় থাকে তবে আপনি আবার দ্রুত রেন্ডার করতে আস্তে আস্তে বিরতি হ্রাস করতে পারবেন (অর্থাত ফ্রেমের হার বৃদ্ধি করুন)।
তবে, এবং এখানে কিকার, যদি সিমুলেশন রাষ্ট্রটি "এখন" -তে আপডেট করা হয়েছে তা সনাক্ত করার পরে আপনি যদি ফ্রেমটি রেন্ডার না করেন তবে আপনি অস্থায়ী আলিয়াসিং প্রবর্তন করেন। ব্যবহারকারীর সামনে উপস্থাপন করা ফ্রেমটি সামান্য ভুল সময়ে উপস্থাপন করা হচ্ছে এবং এটি নিজেই তোতলা অনুভব করবে।
আপনার পড়া নিবন্ধগুলিতে উল্লিখিত "আংশিক টাইমস্টেপ" এর কারণ এটি। এটি এখানে একটি ভাল কারণেই রয়েছে এবং এটি কারণ কারণ আপনি যদি না আপনার পদার্থবিজ্ঞানের টাইমস্টেপটি নির্দিষ্ট স্থির রেন্ডারিং টাইমস্টেপের কিছু স্থির অবিচ্ছেদ্য একাধিকের সাথে ঠিক করেন, আপনি কেবল সঠিক সময়ে ফ্রেমগুলি উপস্থাপন করতে পারবেন না। আপনি এগুলি হয় খুব তাড়াতাড়ি, অথবা খুব দেরীতে উপস্থাপন করে। স্থিরভাবে রেন্ডারিং রেট পাওয়ার এবং শারীরিকভাবে সঠিক এমন কিছু উপস্থাপনের একমাত্র উপায় হ'ল গ্রহণযোগ্যতা যে রেন্ডারিং অন্তরকালটি প্রায় সময় আসে তখন আপনি সম্ভবত আপনার দুটি স্থির পদার্থবিজ্ঞানের টাইমস্টেপের মাঝখানে হয়ে যান। তবে এর অর্থ এই নয় যে উপস্থাপনাগুলি রেন্ডারিংয়ের সময় সংশোধিত হয়, কেবল যে রেন্ডারিংকে অস্থায়ীভাবে স্থাপন করতে হবে যেখানে বস্তুগুলি যাতে এটি তাদের আগে যেখানে ছিল এবং যেখানে আপডেটের পরে রয়েছে সেগুলির মধ্যে কোথাও এটি রেন্ডার করতে পারে। এটি গুরুত্বপূর্ণ - রেন্ডারিংয়ের জন্য বিশ্ব রাষ্ট্রকে কখনই পরিবর্তন করবেন না, কেবলমাত্র আপডেটগুলি বিশ্ব রাষ্ট্র পরিবর্তন করে।
সুতরাং এটি সিউডোকোড লুপে রাখতে আমার মনে হয় আপনার আরও কিছু এর মতো দরকার:
InitialiseWorldState();
previousTime = currentTime = 0.0;
renderInterval = 1.0 / 60.0; //A nice high starting interval
subFrameProportion = 1.0; //100% currentFrame, 0% previousFrame
while (true)
{
frameStart = ActualTime();
//Render the world state as if it was some proportion
// between previousTime and currentTime
// E.g. if subFrameProportion is 0.5, previousTime is 0.1 and
// currentTime is 0.2, then we actually want to render the state
// as it would be at time 0.15. We'd do that by interpolating
// between movingObject.previousPosition and movingObject.currentPosition
// with a lerp parameter of 0.5
Render(subFrameProportion);
//Check we've not taken too long and missed our render interval
frameTime = ActualTime() - frameStart;
if (frameTime > renderInterval)
{
renderInterval = frameTime * 1.2f; //Give us a more reasonable render interval that we actually have a chance of hitting
}
expectedFrameEnd = frameStart + renderInterval;
//Loop until it's time to render the next frame
while (ActualTime() < expectedFrameEnd)
{
//step the simulation forward until it has moved just beyond the frame end
if (previousTime < expectedFrameEnd) &&
currentTime >= expectedFrameEnd)
{
previousTime = currentTime;
Update();
currentTime += fixedTimeStep;
//After the update, all objects will be in the position they should be for
// currentTime, **but** they also need to remember where they were before,
// so that the rendering can draw them somewhere between previousTime and
// currentTime
//Check again we've not taken too long and missed our render interval
frameTime = ActualTime() - frameStart;
if (frameTime > renderInterval)
{
renderInterval = frameTime * 1.2f; //Give us a more reasonable render interval that we actually have a chance of hitting
expectedFrameEnd = frameStart + renderInterval
}
}
else
{
//We've brought the simulation to just after the next time
// we expect to render, so we just want to wait.
// Ideally sleep or spin in a tight loop while waiting.
timeTillFrameEnd = expectedFrameEnd - ActualTime();
sleep(timeTillFrameEnd);
}
}
//How far between update timesteps (i.e. previousTime and currentTime)
// will we be at the end of the frame when we start the next render?
subFrameProportion = (expectedFrameEnd - previousTime) / (currentTime - previousTime);
}
এটির জন্য সমস্ত অবজেক্টের কাজ করার জন্য তাদের আগে কোথায় ছিল এবং এখন তারা কোথায় ছিল সে সম্পর্কে জ্ঞান সংরক্ষণ করা দরকার, যাতে রেন্ডারিংটি অবজেক্টটি কোথায় তা তার জ্ঞান ব্যবহার করতে পারে।
class MovingObject
{
Vector velocity;
Vector previousPosition;
Vector currentPosition;
Initialise(startPosition, startVelocity)
{
currentPosition = startPosition; // position at time 0
velocity = startVelocity;
//ignore previousPosition because we should never render before time 0
}
Update()
{
previousPosition = currentPosition;
currentPosition += velocity * fixedTimeStep;
}
Render(subFrameProportion)
{
Vector actualPosition =
Lerp(previousPosition, currentPosition, subFrameProportion);
RenderAt(actualPosition);
}
}
এবং আসুন মিলসেকেন্ডে একটি টাইমলাইন রাখি, বলে যে রেন্ডারিংটি সম্পূর্ণ হতে 3 মিমি সময় নেয়, আপডেট করাতে 1 মিমি লাগে, আপনার আপডেটের সময়-পদক্ষেপটি 5 এমএসে স্থির করা হয়, এবং আপনার রেন্ডার টাইমস্টেপ শুরু হয় (এবং অবশেষে) 16 মিমি [60Hz] এ।
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
R0 U5 U10 U15 U20 W16 R16 U25 U30 U35 W32 R32
- প্রথমে আমরা 0 সময় সূচনা (তাই বর্তমান সময় = 0)
- আমরা 1.0 (100% কারেন্টটাইম) এর অনুপাতের সাথে রেন্ডার করি, যা 0 সময়ে বিশ্বকে আঁকবে
- যখন এটি শেষ হয়, আসল সময় 3 হয় এবং আমরা 16 অবধি ফ্রেমটির সমাপ্তি আশা করি না, তাই আমাদের কিছু আপডেট চালানো দরকার
- টি + 3: আমরা 0 থেকে 5 পর্যন্ত আপডেট করি (তারপরে কারেন্টটাইম = 5, পূর্ববর্তী সময় = 0)
- টি + 4: ফ্রেম শেষ হওয়ার আগে এখনও, তাই আমরা 5 থেকে 10 এ আপডেট করব
- টি + 5: ফ্রেম শেষ হওয়ার আগে এখনও, তাই আমরা 10 থেকে 15 পর্যন্ত আপডেট করি
- টি + 6: ফ্রেম শেষ হওয়ার আগে এখনও, তাই আমরা 15 থেকে 20 পর্যন্ত আপডেট করি
- টি + 7: ফ্রেম শেষ হওয়ার আগে এখনও, তবে কারেন্টটাইম ফ্রেমের শেষের বাইরে। আমরা আর কোনও সিমুলেট করতে চাই না কারণ এটি করার ফলে আমরা পরবর্তী সময়টি রেন্ডার করতে চাইলে ছাড়িয়ে যায়। পরিবর্তে আমরা পরবর্তী রেন্ডার ব্যবধানের জন্য চুপচাপ অপেক্ষা করি (16)
- টি + 16: এটি আবার রেন্ডার করার সময়। পূর্ববর্তী সময়টি 15, কারেন্টটাইম 20 হয় So তাই আমরা যদি টি + 16 রেন্ডার করতে চাই, আমরা 5 মিমি দীর্ঘ টাইমস্টেপের মধ্য দিয়ে 1 মিমি। সুতরাং আমরা ফ্রেমের মাধ্যমে 20% পথ (অনুপাত = 0.2)। যখন আমরা রেন্ডার করি, আমরা তাদের পূর্ববর্তী অবস্থান এবং তাদের বর্তমান অবস্থানের মধ্যে অবজেক্টগুলিকে 20% আঁকি।
- 3 এ ফিরে লুপ করুন এবং অনির্দিষ্টকালের জন্য চালিয়ে যান।
সময়ের অনেক আগেই সিমুলেট করার বিষয়ে এখানে আরও একটি উপকার রয়েছে, অর্থাত ফ্রেমটি রেন্ডার হওয়ার আগেই ঘটেছে তবুও ব্যবহারকারীর ইনপুটগুলি উপেক্ষা করা যেতে পারে, তবে লুপটি সহজেই সিমুলেট করছে না এমনটা নিশ্চিত না হওয়া পর্যন্ত এই বিষয়ে চিন্তা করবেন না।