之前介紹了幾何階段坐標(biāo)變換的原理,接下來使用C++來進(jìn)行實(shí)現(xiàn)。
Transform
首先定義一個Transform類,它存儲了之前介紹的3個重要的變換矩陣和屏幕的寬高,負(fù)責(zé)處理頂點(diǎn)的變換,剔除
class Transform
{
public:
Matrix4f _worldTransform, _viewTransform, _perspectiveTransform, _transform;
_INT32 _width, _height;
public:
Transform(){}
Transform(Matrix4f worldTransform, Matrix4f viewTransform, Matrix4f perspectiveTransform,
_INT32 width, _INT32 height);
Transform(const Transform& other);
public:
void Init(_INT32 width, _INT32 height);
void Init(Camera* camera, _INT32 width, _INT32 height, Matrix4f wordTransform);
Vector4f ApplyTransform(const Vector4f& v) const;
_INT32 CheckCVV(const Vector4f& v) const;
Vector4f Homogenize(const Vector4f& v) const;
void UpdateTransform();
};
Transform::Transform(Matrix4f worldTransform, Matrix4f viewTransform, Matrix4f perspectiveTransform,
_INT32 width, _INT32 height)
{
_worldTransform = worldTransform;
_viewTransform = viewTransform;
_perspectiveTransform = perspectiveTransform;
_transform = _worldTransform * _viewTransform * _perspectiveTransform;
_width = width;
_height = height;
}
Transform::Transform(const Transform& other)
{
_worldTransform = other._worldTransform;
_viewTransform = other._viewTransform;
_perspectiveTransform = other._perspectiveTransform;
_width = other._width;
_height = other._height;
}
Transform類初始化的方式有兩種,一種是先創(chuàng)建Transform類,之后再自行修改其變換矩陣
void Transform::Init(_INT32 width, _INT32 height)
{
_FLOAT aspect = (_FLOAT)width / (_FLOAT)height;
_width = width;
_height = height;
_worldTransform.SetIdentity();
_viewTransform.SetIdentity();
_perspectiveTransform.SetIdentity();
_transform = _worldTransform * _viewTransform * _perspectiveTransform;
}
另一種是傳入一個攝像機(jī)對象和世界變換矩陣,利用攝像機(jī)來生成視圖變換和透視變換矩陣,初始化一個完整的Transform對象
void Transform::Init(Camera* camera, _INT32 width, _INT32 height, Matrix4f worldTransform)
{
_FLOAT aspect = (_FLOAT)width / (_FLOAT)height;
_width = width;
_height = height;
_worldTransform = worldTransform;
_viewTransform = camera->GetViewTransformMatrix();
_perspectiveTransform = camera->GetPerspectiveTransformMarix();
_transform = _worldTransform * _viewTransform * _perspectiveTransform;
}
當(dāng)用戶自行修改了變換矩陣后需要調(diào)用UpdateTransform函數(shù)更新Transform類
void Transform::UpdateTransform()
{
_transform = _worldTransform * _viewTransform * _perspectiveTransform;
}
ApplyTransform函數(shù)負(fù)責(zé)對頂點(diǎn)進(jìn)行變換
Vector4f Transform::ApplyTransform(const Vector4f& v) const
{
return v * _transform;
}
CheckCVV函數(shù)負(fù)責(zé)檢查頂點(diǎn)變換后是否在規(guī)則長方體內(nèi)
_INT32 Transform::CheckCVV(const Vector4f& v) const
{
_FLOAT w = v._w;
_INT32 check = 0;
if (v._z < 0)
{
check |= 0x01;
}
if (v._z > w)
{
check |= 0x02;
}
if (v._x < -w)
{
check |= 0x04;
}
if (v._x > w)
{
check |= 0x08;
}
if (v._y < -w)
{
check |= 0x10;
}
if (v._y > w)
{
check |= 0x20;
}
return check;
}
Homogenize負(fù)責(zé)將進(jìn)行CVV檢查后的頂點(diǎn)進(jìn)行其次除法,并通過一個簡單的線性變換將頂點(diǎn)的歸一化坐標(biāo)映射變換為屏幕坐標(biāo)
Vector4f Transform::Homogenize(const Vector4f& v) const
{
_FLOAT inverse = 1.0f / v._w;
return Vector4f(
(v._x * inverse + 1.0f) * _width * 0.5f,
(1.0f - v._y * inverse) * _height * 0.5f,
v._z * inverse,
1.0f
);
}
Camera
視圖變換矩陣和透視變換矩陣的生成都和攝像機(jī)相關(guān),所以還需要定義一個攝像機(jī)類Camera,相機(jī)類中包含了相機(jī)的朝向(position、direction、up和horizontal向量),屏幕的寬高比,視錐體的視角,近裁剪平面和遠(yuǎn)裁剪平面到攝像機(jī)的距離
class Camera
{
public:
Vector3f _position, _direction, _up, _horizontal;
_FLOAT _aspect, _angle, _near, _far;
public:
Camera(){}
Camera(Vector3f position, Vector3f direction, Vector3f up,
_FLOAT aspect, _FLOAT angle, _FLOAT near, _FLOAT far);
Camera(Camera& other);
public:
Matrix4f GetViewTransformMatrix();
static Matrix4f GetViewTransformMatrix(Vector4f position, Vector4f direction, Vector4f up);
Matrix4f GetPerspectiveTransformMarix();
static Matrix4f GetPerspectiveTransformMarix(_FLOAT angle, _FLOAT aspect, _FLOAT near, _FLOAT far);
};
Camera在構(gòu)造的時候只需要傳入攝像機(jī)正對的方向和攝像機(jī)的正上方即可,攝像機(jī)正對的方向可以通過攝像機(jī)目標(biāo)點(diǎn)的左邊減去攝像機(jī)本身的位置得到,在構(gòu)造函數(shù)中會通過叉乘來計算攝像機(jī)水平方向的向量
Camera::Camera(Vector3f position, Vector3f direction, Vector3f up,
_FLOAT aspect, _FLOAT angle, _FLOAT near, _FLOAT far)
{
_position = position;
_direction = direction.Normalize();
_up = up.Normalize();
_horizontal = _up.Cross(_direction).Normalize();
_aspect = aspect;
_angle = angle;
_near = near;
_far = far;
}
Camera::Camera(Camera& other)
{
_position = other._position;
_direction = other._direction.Normalize();
_up = other._up.Normalize();
_horizontal = _up.Cross(_direction).Normalize();
_aspect = other._aspect;
_angle = other._angle;
_near = other._near;
_far = other._far;
}
GetViewTransformMatrix函數(shù)根據(jù)前面的介紹的試圖變換矩陣的原理構(gòu)造出視圖變換矩陣
Matrix4f Camera::GetViewTransformMatrix()
{
Matrix4f viewTransform = Matrix4f();
viewTransform(0, 0) = _horizontal._x;
viewTransform(1, 0) = _horizontal._y;
viewTransform(2, 0) = _horizontal._z;
viewTransform(3, 0) = -_position.Dot(_horizontal);
viewTransform(0, 1) = _up._x;
viewTransform(1, 1) = _up._y;
viewTransform(2, 1) = _up._z;
viewTransform(3, 1) = -_position.Dot(_up);
viewTransform(0, 2) = _direction._x;
viewTransform(1, 2) = _direction._y;
viewTransform(2, 2) = _direction._z;
viewTransform(3, 2) = -_position.Dot(_direction);
viewTransform(0, 3) = 0.0f;
viewTransform(1, 3) = 0.0f;
viewTransform(2, 3) = 0.0f;
viewTransform(3, 3) = 1.0f;
return viewTransform;
}
Matrix4f Camera::GetViewTransformMatrix(Vector4f position, Vector4f direction, Vector4f up)
{
direction = direction.Normalize();
up = up.Normalize();
Vector4f horizontal = up.Cross(direction).Normalize();
Matrix4f viewTransform = Matrix4f();
viewTransform(0, 0) = horizontal._x;
viewTransform(1, 0) = horizontal._y;
viewTransform(2, 0) = horizontal._z;
viewTransform(3, 0) = -position.Dot(horizontal);
viewTransform(0, 1) = up._x;
viewTransform(1, 1) = up._y;
viewTransform(2, 1) = up._z;
viewTransform(3, 1) = -position.Dot(up);
viewTransform(0, 2) = direction._x;
viewTransform(1, 2) = direction._y;
viewTransform(2, 2) = direction._z;
viewTransform(3, 2) = -position.Dot(direction);
viewTransform(0, 3) = 0.0f;
viewTransform(1, 3) = 0.0f;
viewTransform(2, 3) = 0.0f;
viewTransform(3, 3) = 1.0f;
return viewTransform;
}
GetPerspectiveTransformMarix函數(shù)根據(jù)前面的介紹的試圖透視矩陣的原理構(gòu)造出視圖變換矩陣
Matrix4f Camera::GetPerspectiveTransformMarix()
{
Matrix4f perspectiveTransform = Matrix4f();
perspectiveTransform.SetZero();
float cotHalfAngle = 1.0f / tanf(_angle * 0.5f);
perspectiveTransform(0, 0) = cotHalfAngle / _aspect;
perspectiveTransform(1, 1) = cotHalfAngle;
perspectiveTransform(2, 2) = _far / (_far - _near);
perspectiveTransform(2, 3) = 1.0f;
perspectiveTransform(3, 2) = _far * _near / (_near - _far);
return perspectiveTransform;
}
Matrix4f Camera::GetPerspectiveTransformMarix(_FLOAT angle, _FLOAT aspect, _FLOAT near, _FLOAT far)
{
Matrix4f perspectiveTransform = Matrix4f();
perspectiveTransform.SetZero();
float cotHalfAngle = 1.0f / tanf(angle * 0.5f);
perspectiveTransform(0, 0) = cotHalfAngle / aspect;
perspectiveTransform(1, 1) = cotHalfAngle;
perspectiveTransform(2, 2) = far / (far - near);
perspectiveTransform(2, 3) = 1.0f;
perspectiveTransform(3, 2) = far * near / (near - far);
return perspectiveTransform;
}