XNA – 追蹤攝影機(上)

嗯…
明明是個很簡單的東西,可是卻一直無法透徹了解
這是書本上的範例,邊打邊複習,順便撐篇數(歐飛)
文章中參雜書本直接的翻譯和我自己的話混合而成,所以可能看起來會怪怪的…
大部分是因為不知道怎樣翻比較順,就自己隨意補上了~

 


在這個 ThirdPersonCamera 類別裡,你可以創造 SetChaseParameters 方法,用來設定一些不常改變的追蹤參數:追蹤距離和速度。
另外可放一些經常更新的位置和方向參數~

// 追蹤參數
float desiredChaseDistance;
float minChaseDistance;
float maxChaseDistance;
float chaseSpeed;
Vector3 chasePosition;
public Vector3 ChasePosition
{
get { return chasePosition; }
set { chasePosition = value; }
}
Vector3 chaseDirection;
public Vector3 ChaseDirection
{
get { return chaseDirection; }
set { chaseDirection = value; }
}
public void SetChaseParameters(float chaseSpeed,
float desiredChaseDistance, float minChaseDistance, float maxChaseDistance){
this.chaseSpeed = chaseSpeed;
this.desiredChaseDistance = desiredChaseDistance;
this.minChaseDistance = minChaseDistance;
this.maxChaseDistance = maxChaseDistance;
}

更新攝影機的位置

每次攝影機在更新時,都應該要重新計算位置。攝影機的位置應該等同於…追蹤點 – 追蹤方向 * 追蹤距離(攝影機到追蹤點的距離)。
如同示意圖,攝影機的期望位置就應該是最終位置。


為了讓攝影機平滑地在最遠距離(maxChaseDistance)和最短距離(minChaseDistance)之間移動,需要利用線性內插的方式計算新位置。

Vector3 targetPosition = chasePosition;
Vector3 desiredCameraPosition = chasePosition chaseDirection * desiredChaseDistance;
 

float interpolatedSpeed = MathHelper.Clamp(chaseSpeed *
elapsedTimeSeconds, 0.0f, 1.0f);

desiredCameraPosition = Vector3.Lerp(position, desiredCameraPosition,
interpolatedSpeed);


線性權重
計算是由上一次更新的時間差乘上攝影機的速度,權重需要壓縮到 0.0 ~ 1.0 之間,接著再用 Vector3 的 Lerp() 計算線性內插值。
創造一個 UpdateFollowPosition 方法,用來更新被追蹤的位置。

private void UpdateFollowPosition(float elapsedTimeSeconds, bool interpolate)
{
Vector3 targetPosition = chasePosition;
Vector3 desiredCameraPosition = chasePositionchaseDirection * desiredChaseDistance;
if (interpolate)
{
float interpolatedSpeed = MathHelper.Clamp(
chaseSpeed * elapsedTimeSeconds, 0.0f, 1.0f); 

desiredCameraPosition = Vector3.Lerp(position,
desiredCameraPosition,
interpolatedSpeed);

// 限制距離
Vector3 targetVector = desiredCameraPositiontargetPosition;
float targetLength = targetVector.Length();
targetVector /= targetLength;
if (targetLength < minChaseDistance)
{
desiredCameraPosition = targetPosition + targetVector * minChaseDistance;
}
else if (targetLength > maxChaseDistance)
{
desiredCameraPosition = targetPosition + targetVector * maxChaseDistance;
}
}
//  需要重新計算向量
SetLookAt(desiredCameraPosition, targetPosition, upVec);
}

這個 UpdateFollowPositionmethod 方法裡有個 interpolate 參數,是用來決定是否使用線性內插計算攝影機的位置。
如果攝影機第一次追蹤目標,你需要將 interpolate 設為 false ,將攝影機放到定位。
當你使用內插計算出攝影機的位置後,接著要確保位置介於最短距離和最大距離之內。
這個檢測可以避免在被追蹤的物體移動過快時,還能準確追蹤著。

使攝影機環繞物件

為了讓我們的攝影機可以沿著物件環繞。所以,要在加入兩個參數給 ThirdPersonCamera 類別。

// 可允許的最大旋轉角度
public static float MAX_ROTATE = 30.0f; // 目前在攝影機軸上的選轉角度
Vector3 eyeRotate; 

// 在攝影機軸上的旋轉速度
Vector3 eyeRotateVelocity;

public Vector3 EyeRotateVelocity
{
get { return eyeRotateVelocity; }
set { eyeRotateVelocity = value; }
}

攝影機允許的旋轉角度介於 -MAX_ROTATE 和 MAX_ROTATE 之間。
eyeRotate 向量儲存目前旋轉的角度,X、Y 和 Z 分別代表在攝影機三軸上的旋轉角度。
最後,在攝影機旋轉角度更新,儲存旋轉速度到 eyeRotateVelocity 裡面。

接著,為了計算攝影機的檢視矩陣(viewmatrix),需要覆寫 BaseCamera 的 UpdateView 方法。
記住,UpdateView 是在屬性 View 被 get 時,而且需要更新時才會呼叫的。

protected override void UpdateView()
{
Vector3 newPosition = PositionTarget; 

// 計算攝影機的新位置,以及依照各自的軸旋轉
newPosition = Vector3.Transform(newPosition,
Matrix.CreateFromAxisAngle(UpVector,
MathHelper.ToRadians(eyeRotate.Y)) *
Matrix.CreateFromAxisAngle(StrafeVector,
MathHelper.ToRadians(eyeRotate.X)) *
Matrix.CreateFromAxisAngle(HeadingVector,
MathHelper.ToRadians(eyeRotate.Z))
);

viewMatrix = Matrix.CreateLookAt(newPosition + Target, Target, UpVector);
needUpdateView = false;
needUpdateFrustum = true;
}

在覆寫 UpdateView 時就要考慮到攝影機的旋轉去計算它的位置。
攝影機的旋轉角度記錄在 eyeRotation 裡面。
要讓攝影機依照自己的任一軸旋轉時,需要使用 CreateFromAxisAngle 產生一個旋轉矩陣。
然後,將 X、Y 和 Z 三軸的旋轉矩陣組合,計算出最終矩陣。

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

Google+ photo

You are commenting using your Google+ account. Log Out / 變更 )

連結到 %s