您当前的位置:五五电子网电子知识单片机-工控设备综合-其它利用c++面向对象的方法来实现光线追踪 正文
利用c++面向对象的方法来实现光线追踪

利用c++面向对象的方法来实现光线追踪

点击数:7161 次   录入时间:03-04 11:46:43   整理:http://www.55dianzi.com   综合-其它

    if( obj->isIntersected(ray, distance) != MISS) // 判断是否有交点

    {

    Intersection = ray.getPoint(distance); //如果相交,求出交点保存到Intersection

    }

    }

    为了计算方便,这里就以球为例,创建一个CSPhere的类,该类继承于CGObject。

    作为球,只需要提供球心Center和半径Radius就可以决定它的几何性质。所以CSphere类只有两个私有成员变量。在所有成员函数中,我们重点来看看isIntersected()方法。

    INTERSECTION_TYPE CSphere::isIntersected(CRay _ray, double& _dist)

    {

    GVector3 v = _ray.getOrigin() - m_Center;

    double b = -(v * _ray.getDirection());

    double det = (b * b) - v*v + m_Radius;

    INTERSECTION_TYPE retval = MISS;

    if (det > 0){

    det = sqrt(det);

    double t1 = b - det;

    double t2 = b + det;

    if (t2 > 0){

    if (t1 < 0) {

    if (t2 < _dist) {

    _dist = t2;

    retval = INTERSECTED_IN;

    }

    }

    else{

    if (t1 < _dist){

    _dist = t1;

    retval = INTERSECTED;

    }

    }

    }

    }

    return retval;

    }

    如果射线和球有交点,那么交点肯定在球面上。球面上的点P都满足下面的关系,

    | P – C | = R

    很明显球面上的点和球心的差向量的大小等于球的半径。然后将射线的参数方程带入上面的公式,再利用求根公式判断解的情况。具体的方法这里就不详述了,有兴趣的同学可以参考另一篇文章“利用OpenGL实现RayPICking”,这篇文章详细讲解了射线和球交点的计算过程。

    现在我们实现了射线CRay,球体CSphere,还差一个重要的角色——光源。光源也是物体的一种,完全可以从我们的基类CGObject类继承。这里做一点区别,我们单独创建一个所有光源的基类CLightSource,然后从它在派生出不同的光源种类,比如平行光源DirectionalLight,点光源CPointLight和聚光源CSpotLight。本文中只详细讲解平行光源的情况,其他两种光源有兴趣的同学可以自己实现。

    类CLightSource的成员变量有四个,分别表示光源的位置,光源的环境光成分,漫反射成分和镜面反射成分。同样地,所有的set和get方法都为该类的子类提供相同的功能。最后也有三个虚成员函数,EvalAmbient(),EvalDiffuse()和EvalSpecular(),它们名字分别说明它们的功能,并且都返回GVector3类型的值——颜色。由于对于不同种类的光源,计算方法可能不同,于是将它们设置为虚函数为以后的扩展做准备。笔者这里将光照计算放在了光源类里面,当然你也可以放在物体类CGObject里,也可以单独写一个方法,将光源和物体作为参数传入,计算出颜色后最为返回值返回。具体使用哪一种好还是要根据具体情况具体分析。

    上面的平行光源类CDirectionalLight是CLightSource的子类,它继承了父类三个虚函数方法。下面来看看这三个函数的具体实现。

    环境光的计算是最简单的,将物体材质环境反射系数和光源的环境光成分相乘即可。

    ambient = Ia•Ka

    计算环境光的代码如下

    GVector3 CDirectionalLight::EvalAmbient(const GVector3& _material_Ka)



www.55dianzi.com

    {

    return GVector3(m_Ka[0]*_material_Ka[0],

    m_Ka[1]*_material_Ka[1],

    m_Ka[2]*_material_Ka[2]);

    }

    漫反射的计算稍微比环境光复杂,漫反射的计算公式为

    diffuse = Id•Kd• (N•L)

    其中,Id是光源的漫反射成分,Kd是物体的漫反射系数,N是法线,L是入射光向量。

    GVector3 CDirectionalLight::EvalDiffuse(const GVector3& _N, const GVector3& _L, constGVector3& _material_Kd)

    {

    GVector3 IdKd = GVector3( m_Kd[0]*_material_Kd[0],

    m_Kd[1]*_material_Kd[1],

    m_Kd[2]*_material_Kd[2]);

    double NdotL = MAX(_N*_L, 0.0);

    return IdKd*NdotL;

    }

    镜面反射的计算又比环境光要复杂,镜面反射的计算公式为

    specular = Is•Ks• (V·R)n

    其中

    R = 2(L•N) •N-L

    Is是光源镜面反射成分,Ks是物体的镜面反射系数,V是相机方向向量,R是反射向量,­n­就反射强度Shininess。为了提高计算效率,也可以利用HalfVector H来计算镜面反射。

    specular = Is•Ks• (N•H)n

    其中

    H=(L+V)/2

    计算H要比计算反射向量R要快得多。

    GVector3 CDirectionalLight::EvalSpecluar(const GVector3& _N, const GVector3& _L, constGVector3& _V,

    const GVector3& _material_Ks,const double& _shininess)

    {

    GVector3 IsKs = GVector3( m_Ks[0]*_material_Ks[0],

    m_Ks[1]*_material_Ks[1],

    m_Ks[2]*_material_Ks[2]);

    GVector3 H = (_L+_V).Normalize();

    double NdotL = MAX(_N*_L, 0.0);

    double NdotH = pow(MAX(_N*H, 0.0), _shininess);

    if(NdotL<=0.0)

    NdotH = 0.0;

    return IsKs*NdotH;

    }

    分别计算出射线和物体交点处的环境光,漫反射和镜面反射后,那么该射线对应像素的颜色c为

    C = ambient + diffuse + specular

    于是,我们可以在代码中添加一个方法叫Tracer(),该方法就是遍历场景中的每个物体,判断射线和物体的交点,然后计算交点的颜色。

    GVector3 Tracer(CRay R)

    {

    GVector3 color;

    for(/*遍历每一个物体*/)

    {

    if(/*如果有交点*/)

    {

    GVector3 p = R.getPoint(dist);

    GVector3 N = m_pObj[k]->getNormal(p);

    N.Normalize();

    for(/*遍历每一个光源*/)

    {

    GVector3 ambient = m_pLight[m]->EvalAmbient(m_pObj[k]->getKa());

    GVector3 L = m_pLight[m]->getPosition()-p;

    L.Normalize();

    GVector3 diffuse = m_pLight[m]->EvalDiffuse(N, L, m_pObj[k]->getKd());

    GVector3 V = m_CameraPosition - p;

    V.Normalize();

    GVector3 specular = m_pLight[m]->EvalSpecluar(N, L, V, m_pObj[k]->getKs(), m_pObj[k]->getShininess());

    color = ambient + diffuse + specular;

    }

    }

    }

    }

    如果要渲染可以反射周围环境的物体,就需要稍微修改上面的Tracer()方法,因为反射是一个递归的过程,一但一条射线被物体反射,那么同样的Tracer()方法就要被执行一次来计算被反射光线和其他物体是否还有交点。于是,在Tracer()方法中再传入一个代表递归迭代深度的参数depth,它表示射线与物体相交后反射的次数,如果为1,说明射线与物体相交后不反射,为2表示射线反射一次,以此类推。

    Tracer(CRay R, int depth)

    {

上一页  [1] [2] [3]  下一页


本文关键字:暂无联系方式综合-其它单片机-工控设备 - 综合-其它