本文主要对smallpt的光线跟踪函数进行详细的解释

1. 光线跟踪详细解释

计算法向量,计算折射光线,反射光线等,可以查阅《3D数学基础,图形与游戏开发》

1.1 计算部分

Vec radiance(const Ray &r, int depth, unsigned short *Xi) {
    double t;                               // 与射线相交物体的距离
    int id = 0;                               // 与射线相交物体的id
    if (!intersect(r, t, id)) 
        return Vec(); // 如果都没有相交,则返回emisson(0, 0, 0)
    const Sphere &obj = spheres[id];        // the hit object 
    Vec x = r.o + r.d*t, n = (x - obj.position).norm(); // 计算n,球面法向量
    Vec nl = n.dot(r.d) < 0 ? n : n*-1, f = obj.color;
    double p = f.x>f.y && f.x>f.z ? f.x : f.y>f.z ? f.y : f.z; // 找出最大的值
    if (++depth>5||!p) 
        if (erand48(Xi)<p) 
            f = f*(1 / p); //假设f为(0.5, 0.2, 0.2),那么作者假定这个表面的反射率为f*(1/0.5),也就是颜色值最大的反射率最高(1, 0.4, 0.4)
        else  
            return obj.emission; 
    // ...
}

1.2 漫反射

if (obj.refl == DIFF) {                  // 如果物体的反射属性是漫反射 
    double r1 = 2 * M_PI*erand48(Xi), r2 = erand48(Xi), r2s = sqrt(r2); //取随机数
    Vec w = nl, u = ((fabs(w.x)>.1 ? Vec(0, 1) : Vec(1)).cross(w)).norm(), v = w.cross(u);  //w,v,u为正交基
    Vec d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1 - r2)).norm(); //求得一个随机的漫反射光线,继续迭代
    return obj.emission + f.mult(radiance(Ray(x, d), depth, Xi));
}

1.3 镜面反射光

else if (obj.refl == SPEC)            // 如果物体的反射属性是漫反射 
    return obj.emission + f.mult(radiance(Ray(x, r.d - n * 2 * n.dot(r.d)), depth, Xi)); //这个比较简单,只要求得反射光的角度即可

1.4 折射加反射

else {
    Ray reflRay(x, r.d - n * 2 * n.dot(r.d));     // 由平行四边形的方法求得反射光的direction
    bool into = n.dot(nl)>0;                // 判断光线是否是进入球体
    double nc = 1, nt = 1.5, nnt = into ? nc / nt : nt / nc, ddn = r.d.dot(nl), cos2t;
    if ((cos2t = 1 - nnt*nnt*(1 - ddn*ddn))<0)    // 如果求得cos2t<0,就没必要进行折射了。
        return obj.emission + f.mult(radiance(reflRay, depth, Xi));
    Vec tdir = (r.d*nnt - n*((into ? 1 : -1)*(ddn*nnt + sqrt(cos2t)))).norm(); //折射角的角度
    double a = nt - nc, b = nt + nc, R0 = a*a / (b*b), c = 1 - (into ? -ddn : tdir.dot(n));
    double Re = R0 + (1 - R0)*c*c*c*c*c, Tr = 1 - Re, P = .25 + .5*Re, RP = Re / P, TP = Tr / (1 - P);
    return obj.emission + f.mult(depth>2 ? (erand48(Xi)<P ?   // 轮盘赌
        radiance(reflRay, depth, Xi)*RP : radiance(Ray(x, tdir), depth, Xi)*TP) :
        radiance(reflRay, depth, Xi)*Re + radiance(Ray(x, tdir), depth, Xi)*Tr);
}

2 加烟雾效果

学习完smallpt之后,看到图形学的书上提到了烟雾效果。
这里写图片描述
编程实现

#define E_MATH       2.71828
Vector frog_color(0.7, 0.7, 0.7);

inline double f_atmo(double distance) {
    return pow(E_MATH, -(0.01 * 0.01* distance * distance));
}
// 然后修改光线反射,折射中的代码
// 在这个实现中,我只在第一层的递归加了烟雾效果
// 修改漫反射的代码
if (depth <= 1) {
    double fTemp = f_atmo(distance);
    return obj._emission + (radiance(Ray(hit_point, direct), depth)) * fTemp + frog_color*(1 - fTemp);
}
else 
    return obj._emission + f.mult(radiance(Ray(hit_point, direct), depth));

// 修改反射
if (depth <= 1) {
    double fTemp = f_atmo(distance);
        return obj._emission + (radiance(Ray(hit_point, direct_refl), depth)) * fTemp + frog_color*(1 - fTemp);
    }
else
    return obj._emission + radiance(Ray(hit_point, direct_refl), depth);

// 修改折射
double fTemp = f_atmo(distance);
    return ((radiance(ray_refl, depth) * Fe
                + radiance(ray_refr, depth) * Fr))*fTemp + frog_color*(1 - fTemp);

效果图1,无烟雾

这里写图片描述

效果图2,加烟雾,可以看出后面那个大球的轮廓。感觉像现在中国最流行的雾霾。 :D
这里写图片描述