还是在读书的时候帮外专业朋友做作业,用GDI实现三维空间的立方体绘制和旋转的操作,那个时候自己根据《线性代数与空间解析几何》以及《计算机图形学》等课程的相关知识写了一个三维向量类。后来做了些二维和三维的东西发现用向量操作会使很多问题变得简单直观,尤其是在OpenGL编程的时候这种感觉尤为明显。经过这么长时间的使用现在这个类已经比较完善了,早些时候将它上传到了CSDN,近来比较闲索性写个介绍吧,希望对从事三维图形开发的朋友有所帮助,也期待着与大家的交流中会有所提高。

      先把代码贴上,当然有兴趣的朋友也可以在我的资源中下载。

      类的定义是这样的

class CVector  
{ 
public: 
    float    m_fVectorX, m_fVectorY, m_fVectorZ,    //向量的空间坐标 
            m_fAngleX, m_fAngleY, m_fAngleZ,    //向量与坐标轴的夹角 
            m_fMod;                                //向量的模

public: 
    CVector(); 
    CVector(float fVectorX, float fVectorY, float fVectorZ); 
    virtual ~CVector();

    void    CalculateAngles(); 
    void    SetVector(float fStartX, float fStartY, float fStartZ, float fEndX, float fEndY, float fEndZ); 
    void    SetVector(float fVectorX, float fVectorY, float fVectorZ); 
    float    GetMod(){return m_fMod;} 
    void    Invert(); 
    void    MakeUnitVector(); 
    void    Rotate(float fAngle, CVector vtrAxis); 
    void    operator=(CVector vectorOther); 
    CVector    operator+(CVector vectorOther); 
    CVector    operator*(float fNum); 
    float    operator^(CVector vectorOther); 
    CVector    operator*(CVector vectorOther); 
};

      大家应该都知道所谓向量其实就是一组数,在笛卡尔坐标系中一个向量就是起点在原点的有向线段,虽然向量的起点可以在任意位置,但是标记向量的有序数却还是当起点在原点时终点的坐标。所以m_fVectorX, m_fVectorY, m_fVectorZ就唯一确定了一个向量。当然向量还有一些其他属性,比如模(也就是向量的长度),比如其和各个坐标轴的夹角,所以我做了另外四个变量m_fMod、m_fAngleX、m_fAngleY、m_fAngleZ来保存这些属性。

      生成变量有很多方法,比如直接设置终点坐标、比如通过起点和终点做差,所以我也实现了两个版本的SetVector。当然也有通过模和角度来生成变量的,但这种方法极其不常用,尤其是在三维条件下这个角度其实很不容易计算,所以我也就没有实现相应的方法。获取向量的模是常用的向量操作,我自然也对其进行了实现,GetMod就是返回模的函数。和矢量有关的操作中反向也是比较常用的一个,我也用Invert实现了这个操作。具体应用时,我们可能经常会用到单位向量,MakeUnitVector的功能就是将当前向量变成单位向量。最值得一提的就是Rotate函数,有过三维制作经验的人都应该知道在三维空间中旋转操作是最让人头疼的了,需要进行繁琐的矩阵运算。根据实际使用的经验,我实现了简单易用的Rotate函数,第一个参数fAngle用于指定旋转角度,而第二个参数vtrAxis用于指定旋转轴。这样通过调用这个函数我们就可以轻松实现任意的旋转操作,也可以初始向量到一个特殊方向,通过旋转达到指定方向,这样也省掉了自行计算复杂向量初始参数的麻烦。当然作为一个数学操作单元重载操作符是必须的,加、减、赋值不必多说,向量乘法有两种,叉积和内积,我通过“*”实现了叉积运算而通过“^”号实现了内积运算。各个向量操作主要都是通过坐标实现的,而某些操作实现之后需要相应的修正其他参数,CalculateAngles函数就是重新计算新向量的角度参数的,使用在各个函数的内部。以下是这个类的实现代码:

CVector::CVector() 
{ 
    //参数初始化 
    m_fVectorX    = 0; 
    m_fVectorY    = 0; 
    m_fVectorZ    = 0;

    m_fAngleX    = 0; 
    m_fAngleY    = 0; 
    m_fAngleZ    = 0;

    m_fMod        = 0; 
}

CVector::CVector(float fVectorX, float fVectorY, float fVectorZ) 
{ 
    SetVector(fVectorX, fVectorY, fVectorZ); 
}

CVector::~CVector() 
{ 
}

void CVector::CalculateAngles() 
{ 
    //计算与坐标轴的夹角 
    m_fAngleX    = (float)acos(m_fVectorX/m_fMod); 
    m_fAngleY    = (float)acos(m_fVectorY/m_fMod); 
    m_fAngleZ    = (float)acos(m_fVectorZ/m_fMod); 
}

void CVector::SetVector(float fStartX, float fStartY, float fStartZ, float fEndX, float fEndY, float fEndZ) 
{ 
    //设置空间坐标 
    m_fVectorX        = fEndX-fStartX; 
    m_fVectorY        = fEndY-fStartY; 
    m_fVectorZ        = fEndZ-fStartZ;

    //计算模 
    m_fMod        = (float)sqrt(m_fVectorX*m_fVectorX+m_fVectorY*m_fVectorY+m_fVectorZ*m_fVectorZ);

    //计算与坐标轴的夹角 
    CalculateAngles(); 
}

void CVector::SetVector(float fVectorX, float fVectorY, float fVectorZ) 
{ 
    //设置空间坐标 
    m_fVectorX        = fVectorX; 
    m_fVectorY        = fVectorY; 
    m_fVectorZ        = fVectorZ;

    //计算模 
    m_fMod        = (float)sqrt(m_fVectorX*m_fVectorX+m_fVectorY*m_fVectorY+m_fVectorZ*m_fVectorZ);

    //计算与坐标轴的夹角 
    CalculateAngles(); 
}

void CVector::Invert() 
{ 
    //设置空间坐标 
    m_fVectorX    = -m_fVectorX; 
    m_fVectorY    = -m_fVectorY; 
    m_fVectorZ    = -m_fVectorZ;

    //计算与坐标轴的夹角 
    CalculateAngles(); 
}

void CVector::MakeUnitVector() 
{ 
    //模归一化 
    m_fMod        = 1;

    //重新设置向量坐标 
    m_fVectorX    = (float)cos(m_fAngleX); 
    m_fVectorY    = (float)cos(m_fAngleY); 
    m_fVectorZ    = (float)cos(m_fAngleZ); 
}

void CVector::Rotate(float fAngle, CVector vtrAxis) 
{ 
    float    fResaultX, fResaultY, fResaultZ;

    //计算角度的cos和sin值 
    float    fCosValue    = (float)cos(fAngle); 
    float    fSinValue    = (float)sin(fAngle);

    //计算旋转后的向量坐标 
    fResaultX     = (fCosValue+(1-fCosValue)*vtrAxis.m_fVectorX*vtrAxis.m_fVectorX)*m_fVectorX; 
    fResaultX    += ((1-fCosValue)*vtrAxis.m_fVectorX*vtrAxis.m_fVectorY-vtrAxis.m_fVectorZ*fSinValue)*m_fVectorY; 
    fResaultX    += ((1-fCosValue)*vtrAxis.m_fVectorX*vtrAxis.m_fVectorZ+vtrAxis.m_fVectorY*fSinValue)*m_fVectorZ;

    fResaultY     = ((1-fCosValue)*vtrAxis.m_fVectorX*vtrAxis.m_fVectorY+vtrAxis.m_fVectorZ*fSinValue)*m_fVectorX; 
    fResaultY    += (fCosValue+(1-fCosValue)*vtrAxis.m_fVectorY*vtrAxis.m_fVectorY)*m_fVectorY; 
    fResaultY    += ((1-fCosValue)*vtrAxis.m_fVectorY*vtrAxis.m_fVectorZ-vtrAxis.m_fVectorX*fSinValue)*m_fVectorZ;

    fResaultZ     = ((1-fCosValue)*vtrAxis.m_fVectorX*vtrAxis.m_fVectorZ-vtrAxis.m_fVectorY*fSinValue)*m_fVectorX; 
    fResaultZ    += ((1-fCosValue)*vtrAxis.m_fVectorY*vtrAxis.m_fVectorZ+vtrAxis.m_fVectorX*fSinValue)*m_fVectorY; 
    fResaultZ    += (fCosValue+(1-fCosValue)*vtrAxis.m_fVectorZ*vtrAxis.m_fVectorZ)*m_fVectorZ;

    //更新向量坐标 
    m_fVectorX     = fResaultX; 
    m_fVectorY     = fResaultY; 
    m_fVectorZ     = fResaultZ;

    //更新向量与坐标的夹角 
    CalculateAngles(); 
}

void CVector::operator=(CVector vectorOther) 
{ 
    CVector vectorResult;

    //参数赋值 
    m_fVectorX    = vectorOther.m_fVectorX; 
    m_fVectorY    = vectorOther.m_fVectorY; 
    m_fVectorZ    = vectorOther.m_fVectorZ;

    m_fAngleX    = vectorOther.m_fAngleX; 
    m_fAngleY    = vectorOther.m_fAngleY; 
    m_fAngleZ    = vectorOther.m_fAngleZ;

    m_fMod        = vectorOther.m_fMod; 
}

CVector CVector::operator+(CVector vectorOther) 
{ 
    CVector vectorResult;

    vectorResult.m_fVectorX    = m_fVectorX+vectorOther.m_fVectorX; 
    vectorResult.m_fVectorY    = m_fVectorY+vectorOther.m_fVectorY; 
    vectorResult.m_fVectorZ    = m_fVectorZ+vectorOther.m_fVectorZ;

    vectorResult.m_fMod        = m_fMod+vectorOther.m_fMod; 
    //计算角度 
    vectorResult.CalculateAngles();

    return vectorResult; 
}

CVector CVector::operator*(float fNum) 
{ 
    CVector vectorResult;

    vectorResult.m_fVectorX    = m_fVectorX*fNum; 
    vectorResult.m_fVectorY    = m_fVectorY*fNum; 
    vectorResult.m_fVectorZ    = m_fVectorZ*fNum;

    vectorResult.m_fMod        = m_fMod*fNum;

    vectorResult.CalculateAngles();

    return vectorResult; 
}

float CVector::operator^(CVector vectorOther) 
{ 
    float    fResult;

    fResult    = m_fVectorX*vectorOther.m_fVectorX+m_fVectorY*vectorOther.m_fVectorY+m_fVectorZ*vectorOther.m_fVectorZ;

    return fResult; 
}

CVector CVector::operator*(CVector vectorOther) 
{ 
    CVector vectorResult;

    //计算新向量 
    vectorResult.m_fVectorX = ((m_fVectorY*vectorOther.m_fVectorZ)-(m_fVectorZ*vectorOther.m_fVectorY)); 
    vectorResult.m_fVectorY = ((m_fVectorZ*vectorOther.m_fVectorX)-(m_fVectorX*vectorOther.m_fVectorZ)); 
    vectorResult.m_fVectorZ = ((m_fVectorX*vectorOther.m_fVectorY)-(m_fVectorY*vectorOther.m_fVectorX)); 
    //计算新向量的模 
    vectorResult.m_fMod        = (float)sqrt(vectorResult.m_fVectorX*vectorResult.m_fVectorX+vectorResult.m_fVectorY*vectorResult.m_fVectorY+vectorResult.m_fVectorZ*vectorResult.m_fVectorZ); 
    //计算新向量的角度 
    vectorResult.CalculateAngles();

    return vectorResult; 
}

      我在网上也看过一些向量类,多半只是有坐标参数,没有相应操作。这个类体现出了向量的所有属性,而且封装了我能想到的向量的所有常用操作。尤其是旋转操作功能比较全面,在我实际的三维编程应用中也有很好的效果。这次拿出来和大家分享,希望大家多提宝贵意见。