之前介绍了几何阶段坐标变换的原理,接下来使用C++来进行实现。
Transform
首先定义一个Transform类,它存储了之前介绍的3个重要的变换矩阵和屏幕的宽高,负责处理顶点的变换,剔除
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类初始化的方式有两种,一种是先创建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;
}
另一种是传入一个摄像机对象和世界变换矩阵,利用摄像机来生成视图变换和透视变换矩阵,初始化一个完整的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;
}
当用户自行修改了变换矩阵后需要调用UpdateTransform函数更新Transform类
void Transform::UpdateTransform()
{
_transform = _worldTransform * _viewTransform * _perspectiveTransform;
}
ApplyTransform函数负责对顶点进行变换
Vector4f Transform::ApplyTransform(const Vector4f& v) const
{
return v * _transform;
}
CheckCVV函数负责检查顶点变换后是否在规则长方体内
_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负责将进行CVV检查后的顶点进行其次除法,并通过一个简单的线性变换将顶点的归一化坐标映射变换为屏幕坐标
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
视图变换矩阵和透视变换矩阵的生成都和摄像机相关,所以还需要定义一个摄像机类Camera,相机类中包含了相机的朝向(position、direction、up和horizontal向量),屏幕的宽高比,视锥体的视角,近裁剪平面和远裁剪平面到摄像机的距离
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在构造的时候只需要传入摄像机正对的方向和摄像机的正上方即可,摄像机正对的方向可以通过摄像机目标点的左边减去摄像机本身的位置得到,在构造函数中会通过叉乘来计算摄像机水平方向的向量
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函数根据前面的介绍的试图变换矩阵的原理构造出视图变换矩阵
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函数根据前面的介绍的试图透视矩阵的原理构造出视图变换矩阵
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;
}