由于原来从未做过3D方面的开发,所以在这方面吃了不少苦头!只有借助网络力量,搜罗一番改进如下:
public class Camera
{
public enum RotateType
{
AboutCamera,
AboutTarget
}
public Camera()
{
this.aspectRatio = (float)4.0f / 3.0f;
this.fieldOfView = MathHelper.PiOver4;
this.m_Target = Vector3.Zero;
this.nearPlane = 0.1f;
this.farPlane = 1000f;
}
private void ReCreateViewMatrix()
{
Vector3 dir = Vector3.Normalize(m_Target - m_Position);
float dp = Vector3.Dot(dir, Vector3.UnitZ);
if (Math.Abs(dp) < 0.9999f)
viewMatrix = Matrix.CreateLookAt(m_Position, m_Target, Vector3.UnitZ);
else
viewMatrix = Matrix.CreateLookAt(m_Position, m_Target, -Vector3.UnitY);
viewMatrixDirty = false;
}
private void ReCreateProjectionMatrix()
{
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearPlane, farPlane);
projectionMatrixDirty = false;
}
public void SetAspectRatio(float aspectRatio)
{
this.AspectRatio = aspectRatio;
}
public void Zoom(float d)
{
float newDistance = CurrentDistance - d;
if (newDistance < 1.0f)
newDistance = 1.0f;
Vector3 dir = Vector3.Normalize(m_Position - m_Target);
Position = m_Target + (dir * newDistance);
}
public void Move(Vector3 csOffset)
{
csOffset *= Math.Max(1.0f, CurrentDistance / 10.0f);
Vector3 dir = Vector3.Normalize(m_Target - m_Position);
float dp = Vector3.Dot(dir, Vector3.UnitZ);
Vector3 up;
if (Math.Abs(dp) < 0.9999f)
up = Vector3.UnitZ;
else
up = -Vector3.UnitY;
Vector3 right = Vector3.Cross(dir, up);
right.Normalize();
up = Vector3.Cross(right, dir);
up.Normalize();
Vector3 offset = (dir * csOffset.Z) +
(right * csOffset.X) +
(up * csOffset.Y);
Position += offset;
Target += offset;
}
public void Rotate(RotateType type, float azimuth, float inclination)
{
if (type == RotateType.AboutTarget)
{
//移动相机位置
Quaternion rotLocal = Quaternion.CreateFromYawPitchRoll(azimuth, inclination, 0);
Quaternion rotWorld = Quaternion.CreateFromRotationMatrix(ViewMatrix);
Quaternion rotInvWorld = Quaternion.Inverse(rotWorld);
Quaternion rot = Quaternion.Multiply(rotInvWorld, Quaternion.Multiply(rotLocal, rotWorld));
Vector3 offset = m_Position - m_Target;
Vector3 rotatedOffset = Vector3.Transform(offset, rot);
Position = m_Target + new Vector3(rotatedOffset.X, rotatedOffset.Y, rotatedOffset.Z);
}
else if (type == RotateType.AboutCamera)
{
//移动目标
Quaternion rotLocal = Quaternion.CreateFromYawPitchRoll(azimuth, inclination, 0);
Quaternion rotWorld = Quaternion.CreateFromRotationMatrix(ViewMatrix);
Quaternion rotInvWorld = Quaternion.Inverse(rotWorld);
Quaternion rot = Quaternion.Multiply(rotInvWorld, Quaternion.Multiply(rotLocal, rotWorld));
Vector3 offset = m_Target - m_Position;
Vector3 rotatedOffset = Vector3.Transform(offset, rot);
Target = m_Position + new Vector3(rotatedOffset.X, rotatedOffset.Y, rotatedOffset.Z);
}
}
private bool viewMatrixDirty = true;//是否需要更新视图矩阵
private bool projectionMatrixDirty = true;//是否需要更新投影矩阵
/// <summary>
/// 获取当前相机的位置与目标的距离
/// </summary>
public float CurrentDistance
{
get { return (m_Position - m_Target).Length(); }
}
private float fieldOfView;
/// <summary>
/// 获取或设置相机的可视区域弧度
/// </summary>
public float FieldOfView
{
get { return fieldOfView; }
set
{
if (fieldOfView != value)
{
projectionMatrixDirty = true;
fieldOfView = value;
}
}
}
private float aspectRatio;
/// <summary>
/// 获取或设置相机的长宽比(通常为[float]屏幕宽度除以[float]屏幕高度)
/// </summary>
public float AspectRatio
{
get { return aspectRatio; }
set
{
if (aspectRatio != value)
{
projectionMatrixDirty = true;
aspectRatio = value;
}
}
}
private float nearPlane;
/// <summary>
/// 获取或设置相机近裁面
/// </summary>
public float NearPlane
{
get { return nearPlane; }
set
{
if (nearPlane != value)
{
projectionMatrixDirty = true;
nearPlane = value;
}
}
}
private float farPlane;
/// <summary>
/// 获取或设置相机的远裁面
/// </summary>
public float FarPlane
{
get { return farPlane; }
set
{
if (farPlane != value)
{
projectionMatrixDirty = true;
farPlane = value;
}
}
}
private Vector3 m_Position;
/// <summary>
/// 获取或设置当前相机的位置
/// </summary>
public Vector3 Position
{
get { return m_Position; }
set
{
if (m_Position != value)
{
viewMatrixDirty = true;
m_Position = value;
}
}
}
private Vector3 m_Target;
/// <summary>
/// 获取或设置当前相机观察的目标
/// </summary>
public Vector3 Target
{
get { return m_Target; }
set
{
if (m_Target != value)
{
m_Target = value;
viewMatrixDirty = true;
}
}
}
/// <summary>
/// 获取视图矩阵与投影矩阵的乘积
/// </summary>
public Matrix ViewProjectionMatrix
{
get { return ViewMatrix * ProjectionMatrix; }
}
private Matrix viewMatrix;
/// <summary>
/// 获取当前相机的视图矩阵
/// </summary>
public Matrix ViewMatrix
{
get
{
if (viewMatrixDirty)
ReCreateViewMatrix();
return viewMatrix;
}
}
private Matrix projectionMatrix;
/// <summary>
/// 获取当前相机的投影矩阵
/// </summary>
public Matrix ProjectionMatrix
{
get
{
if (projectionMatrixDirty)
ReCreateProjectionMatrix();
return projectionMatrix;
}
}
}
操作:左键平移相机、鼠标滚轮缩放相机、右键旋转相机。
目前存在的问题:当相机进行俯仰查看顶部或底部时(且角度与屏幕垂直时)会出现闪烁的问题。杯具的人啊。。。不知道如何控制俯仰的范围。。。
期待各位高手能不吝赐教,谢谢!