一個簡單光柵器的實(shí)現(xiàn)(四) 幾何階段的坐標(biāo)變換的C++實(shí)現(xiàn)

之前介紹了幾何階段坐標(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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容