美文网首页图形渲染
Path Tracing介绍

Path Tracing介绍

作者: Manster | 来源:发表于2016-05-29 14:35 被阅读0次

    Ray Tracing实际上是一种渲染框架,通过迭代计算,得到镜面反射效果和折射效果,在上一篇文章中,我使用了最基本的方法,当光线和物体相交时,根据交点和法线方向计算唯一的反射光线和折射光线,进而迭代处理。但是现实情况往往并非如此,反射光线方向不一定是唯一的,因此Path Tracing作为Ray Tracing的一个具体实现,可以实现更加真实的效果。Path Tracing使用了蒙特卡洛方法,利用BRDF随机跟踪多条反射光线,随后根据这些光线的贡献计算该点的颜色值。因此,可想而知,Path Tracing相比于上一篇文章的结果,非常耗时,但是结果也更加真实。当然BRDF里有很多模型,目标也是解决不同场景下出现的问题,因此这是一个深坑,我也没有爬出来orz。
    当然某些同学还会遇到一类渲染方法叫做Ray Casting,事实上这个渲染方法就是Ray Tracing的第一步,发射光线,与物体相交,这个方法可以实现阴影的效果,但是没有包括后续的反射效果和折射效果的计算。


    result_25k.png

    上面这张图片是Path Tracing的一个渲染例子,可以看到效果确实很不错,但是代价是渲染速度非常慢,另外在这里需要强调的一点是,右边的球是透明的,因此具有折射属性,所以可以看到,光源的光线穿过了球体,在下面形成了圆形,还有折射和镜面反射是密不可分的,存在折射就会存在镜面反射,这也是为什么右边的球存在镜面反射。
    以下是代码部分:

    #include <math.h>   // cl -o pathtracing.exe pathtracing.cpp under windows
    #include <stdlib.h> // g++ -o pathtracing.out pathtracing.cpp under mac
    #include <stdio.h>          
    
    #define M_PI 3.141592653589793
    
    unsigned RAND48_SEED_0  (0x330e);
    unsigned RAND48_SEED_1  (0xabcd);
    unsigned RAND48_SEED_2  (0x1234);
    unsigned RAND48_MULT_0  (0xe66d);
    unsigned RAND48_MULT_1  (0xdeec);
    unsigned RAND48_MULT_2  (0x0005);
    unsigned RAND48_ADD     (0x000b);
    
    //Generate random number using seed.
    
    unsigned short _rand48_seed[3] = {
         RAND48_SEED_0,
         RAND48_SEED_1,
         RAND48_SEED_2
    };
    unsigned short _rand48_mult[3] = {
         RAND48_MULT_0,
         RAND48_MULT_1,
         RAND48_MULT_2
    };
    
    void dorand48(unsigned short xseed[3])
    {
         unsigned accu;
         unsigned short temp[2];
    
         accu = RAND48_MULT_0 * xseed[0] + RAND48_ADD;
         temp[0] = (unsigned short) accu;
         accu >>= 16;
         accu += RAND48_MULT_0 * xseed[1] + RAND48_MULT_1 * xseed[0];
         temp[1] = (unsigned short) accu;
         accu >>= 16;
         accu += RAND48_MULT_0 * xseed[2] + RAND48_MULT_1 * xseed[1] + RAND48_MULT_2 * xseed[0];
         xseed[0] = temp[0];
         xseed[1] = temp[1];
         xseed[2] = (unsigned short) accu;
    }
    
    double erand48(unsigned short xseed[3])
    {
         dorand48(xseed);
         return ldexp((double) xseed[0], -48) +
                ldexp((double) xseed[1], -32) +
                ldexp((double) xseed[2], -16);
    }
    
    float erand24(unsigned short xseed[3])
    {
         dorand48(xseed);
         return ldexp((float) xseed[1], -32) + ldexp((float) xseed[2], -16);
    }
    
    
    //Define the struct vec
    struct Vec
    {
         float x, y, z;
         /*Vec(float x_ = 0)
         {
             x = x_;
             y = y_;
             z = z_;
         }*/
         
         Vec(float x_ = 0, float y_ = 0, float z_ = 0)
         {
              x = x_;
              y = y_;
              z = z_;
         }
         
         Vec operator+(const Vec &b) const
         {
              return Vec(x + b.x, y + b.y, z + b.z);
         }
         
         Vec operator-(const Vec &b) const
         {
              return Vec(x - b.x, y - b.y, z - b.z);
         }
    
         Vec operator-() const
         {
             return Vec(-x, -y, -z);
         }
    
         Vec operator*(float b) const 
         {
              return Vec(x * b, y * b, z * b);
         }
         
         Vec mult(const Vec &b) const 
         {
              return Vec(x * b.x, y * b.y, z * b.z);
         }
         
         Vec& norm()
         {
              return *this = *this * (1 / sqrt(x * x + y * y + z * z));
         }
         
         float dot(const Vec &b) const 
         {
              return x * b.x + y * b.y + z * b.z;   
         }
         
         Vec operator%(Vec&b) 
         {
              return Vec(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x);
         }
    };
    
    //Define the struct Ray, including start and orientation P(t) = O + tD
    struct Ray
    {
         Vec o, d;
         Ray(Vec o_, Vec d_) : o(o_), d(d_) {}
    };
    
    // three material types, diffuse, specular and refractive
    enum Refl_t { DIFF, SPEC, REFR };  
    
    struct Sphere
    {
        float rad;       // radius of sphere
        Vec p, e, c;      // position, emission, color
        Refl_t refl;      // reflection type including diffuse, specular and refractive 
    
        Sphere(float rad_, Vec p_, Vec e_, Vec c_, Refl_t refl_) : rad(rad_), p(p_), e(e_), c(c_), refl(refl_) { }
    
        float intersect(const Ray &r) const // returns distance, 0 if nohit
        {  
            Vec op = p - r.o; // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0
            double t, eps = 1e-4, b = op.dot(r.d), det = b * b - op.dot(op) + rad * rad;
            if (det < 0) return 0;
            else det = sqrt(det);
            return (t = b - det) > eps ? t : ((t = b + det) > eps ? t : 0);
            /*Vec l = p - r.o;
            double eps = 1e-4;
            double tca = l.dot(r.d);
            if(tca < 1e-4) return 0;
            double dist = l.dot(l) - tca*tca;
            double rad2 = rad * rad;
            if (dist > rad2) return 0;
            double thc = sqrt(rad2 - dist);
            double t0 = tca - thc;
            double t1 = tca - thc;
            return (t0) > eps ? t0 : (t1 > eps ? t1: 0);*/
        }
    };
    
    Sphere spheres[] = {//Scene: radius, position, emission, color, material
         Sphere(1e5, Vec( 1e5 + 1, 40.8, 81.6), Vec(), Vec(.75, .25, .25), DIFF), //Left
         Sphere(1e5, Vec(-1e5 + 99, 40.8, 81.6), Vec(), Vec(.25, .25, .75), DIFF), //Rght
         Sphere(1e5, Vec(50, 40.8, 1e5),     Vec(), Vec(.75, .75, .75), DIFF), //Back
         Sphere(1e5, Vec(50, 40.8, -1e5 + 170), Vec(), Vec(),           DIFF), //Frnt
         Sphere(1e5, Vec(50, 1e5, 81.6),    Vec(), Vec(.75, .75, .75), DIFF), //Botm
         Sphere(1e5, Vec(50, -1e5 + 81.6, 81.6), Vec(), Vec(.75, .75, .75), DIFF), //Top
         Sphere(16.5, Vec(27, 16.5, 47),       Vec(), Vec(1, 1, 1)*.999, SPEC), //Mirr
         Sphere(16.5, Vec(73, 16.5, 78),       Vec(), Vec(1, 1, 1)*.999, SPEC), //Glas
         Sphere(600, Vec(50, 681.6 - .27, 81.6), Vec(12, 12, 12),  Vec(), DIFF) //Light
    };
    
    /*Sphere spheres[] = {//Scene: radius, position, emission, color, material
      // center 50 40.8 62
      // floor 0
      // back  0
      //     rad       pos                   emis           col     refl
    //  Sphere(1e3,   Vec(1,1,-2)*1e4,    Vec(1,1,1)*5e2,     Vec(), DIFF), // moon
    //  Sphere(3e2,   Vec(.6,.2,-2)*1e4,    Vec(1,1,1)*5e3,     Vec(), DIFF), //
    //  moon
    
      Sphere(2.5e3,   Vec(.82,.92,-2)*1e4,    Vec(1,1,1)*.8e2,     Vec(), DIFF), // moon
    
    //  Sphere(2.5e4, Vec(50, 0, 0),     Vec(1,1,1)*1e-3,    Vec(.2,.2,1)*0.0075, DIFF), // sky
    //  Sphere(2.5e4, Vec(50, 0, 0),  Vec(0.114, 0.133, 0.212)*1e-2,  Vec(.216,.384,1)*0.0007, DIFF), // sky
    
      Sphere(2.5e4, Vec(50, 0, 0),  Vec(0.114, 0.133, 0.212)*1e-2,  Vec(.216,.384,1)*0.003, DIFF), // sky
    
      Sphere(5e0,   Vec(-.2,0.16,-1)*1e4, Vec(1.00, 0.843, 0.698)*1e2,   Vec(), DIFF),  // star
      Sphere(5e0,   Vec(0,  0.18,-1)*1e4, Vec(1.00, 0.851, 0.710)*1e2,  Vec(), DIFF),  // star
      Sphere(5e0,   Vec(.3, 0.15,-1)*1e4, Vec(0.671, 0.780, 1.00)*1e2,   Vec(), DIFF),  // star
      Sphere(3.5e4,   Vec(600,-3.5e4+1, 300), Vec(),   Vec(.6,.8,1)*.01,  REFR),   //pool
      Sphere(5e4,   Vec(-500,-5e4+0, 0),   Vec(),      Vec(1,1,1)*.35,  DIFF),    //hill
      Sphere(16.5,  Vec(27,0,47),         Vec(),              Vec(1,1,1)*.33, DIFF), //hut
      Sphere(7,     Vec(27+8*sqrt(2),0,47+8*sqrt(2)),Vec(),  Vec(1,1,1)*.33,  DIFF), //door
      Sphere(500,   Vec(-1e3,-300,-3e3), Vec(),  Vec(1,1,1)*.351,    DIFF),  //mnt
      Sphere(830,   Vec(0,   -500,-3e3), Vec(),  Vec(1,1,1)*.354,    DIFF),  //mnt
      Sphere(490,  Vec(1e3,  -300,-3e3), Vec(),  Vec(1,1,1)*.352,    DIFF),  //mnt
    };*/
    
    
    //Convert unbounded color range to [0,1]
    inline float clamp(float x)
    {
         return x < 0 ? 0 : x > 1 ? 1 : x;
    }
    
    //Convert [0,1] to [0,255]
    inline int toInt(float x)
    {
         return int(pow(clamp(x), 1 / 2.2f) * 255 + .5f);
    }
    
    //Compute the intersection between ray and objects in scene, and find the closest object
    bool intersect(const Ray& r, double& t, int& id)
    {
        double n = sizeof(spheres) / sizeof(Sphere);
        double inf = t = 1e20;
        for(int i = int(n); i--;)
        {
            double d = spheres[i].intersect(r);
            if (d && d < t) {
                t = d;
                id = i;
            }
        }
        return t < inf;
    }
    
    /*struct RadState
    {
        Vec e;
        Vec f;
    };*/
    
    
    Vec radiance(Ray r, int depth, unsigned short *Xi)
    {
        Vec resE(0, 0, 0);
        Vec resF(1, 1, 1);
    
        for (int depth = 0; depth != 10; ++depth)
        {
            double t;                               // distance to intersection
            int id = 0;                               // id of intersected object
    
            if (!intersect(r, t, id))
            {
                resF = Vec();
                continue;
            }
    
            const Sphere &obj = spheres[id];        // the hit object
    
            Vec x = r.o + r.d * t;
            Vec n = (x - obj.p).norm();
            Vec nl = n.dot(r.d) < 0 ? n : -n;
            Vec f = obj.c;
    
            double p = f.x > f.y && f.x > f.z ? f.x : f.y > f.z ? f.y : f.z; // max refl
    
            if (obj.refl == DIFF)
            {                 // Ideal DIFFUSE reflection
                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)) % w).norm(), v = w % u;
                Vec d = (u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1 - r2)).norm();
    
                r = Ray(x + d * 0.1, d);
                resE = resE + obj.e.mult(resF);
                resF = resF.mult(f);
                continue;
            }
            else if (obj.refl == SPEC)
            {// Ideal SPECULAR reflection
                r = Ray(x, r.d - n * 2 * n.dot(r.d));
                resE = resE + obj.e.mult(resF);
                resF = resF.mult(f);
                continue;
            }
            else { 
                resF = Vec();
                continue;
            }
    
            Ray reflRay(x, r.d - n * 2 * n.dot(r.d)); // Ideal dielectric REFRACTION
            bool into = n.dot(nl) > 0;              // Ray from outside going in?
            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 && depth < 5) // Total internal reflection
            {
                r = reflRay;
                resE = resE + obj.e.mult(resF);
                resF = resF.mult(f);
                continue;
            }
    
            Vec tdir = (r.d * nnt - n * ((into ? 1 : -1) * (ddn * nnt + sqrt(cos2t)))).norm();
            double a = nt - nc;
            double b = nt + nc;
            double R0 = a * a / (b * b);
            double c = 1 - (into ? -ddn : tdir.dot(n));
            double Re = R0 + (1 - R0) * c * c * c * c * c;
            double Tr = 1 - Re;
            double P = .25 + .5 * Re;
            double RP = Re / P;
            double TP = Tr / (1 - P);
        
            if (erand48(Xi) < P)
            {
                r = reflRay;
                resE = resE + obj.e.mult(resF) * RP;
                resF = resF.mult(f) * RP;
            }
            else
            {
                r = Ray(x, tdir);
                resE = resE + obj.e.mult(resF) * TP;
                resF = resF.mult(f) * TP;
            }
        }
    
        return resE;
    }
    
     /*Vec radiance(const Ray &r, int depth, unsigned short *Xi){ 
       double t;                               // distance to intersection 
       int id=0;                               // id of intersected object 
       if (!intersect(r, t, id)) return Vec(); // if miss, return black 
       const Sphere &obj = spheres[id];        // the hit object 
       Vec x=r.o+r.d*t, n=(x-obj.p).norm(), nl=n.dot(r.d)<0?n:n*-1, f=obj.c; 
       double p = f.x>f.y && f.x>f.z ? f.x : f.y>f.z ? f.y : f.z; // max refl 
       if (++depth>5) if (erand48(Xi)<p) f=f*(1/p); else return obj.e; //R.R. 
       if (obj.refl == DIFF){                  // Ideal DIFFUSE reflection 
         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))%w).norm(), v=w%u; 
         Vec d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1-r2)).norm(); 
         return obj.e + f.mult(radiance(Ray(x,d),depth,Xi)); 
       } else if (obj.refl == SPEC)            // Ideal SPECULAR reflection 
         return obj.e + f.mult(radiance(Ray(x,r.d-n*2*n.dot(r.d)),depth,Xi)); 
       Ray reflRay(x, r.d-n*2*n.dot(r.d));     // Ideal dielectric REFRACTION 
       bool into = n.dot(nl)>0;                // Ray from outside going in? 
       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)    // Total internal reflection 
         return obj.e + 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.e + f.mult(
         (erand48(Xi)<P ?   // Russian roulette 
            radiance(reflRay,depth,Xi)*RP:
                radiance(Ray(x,tdir),depth,Xi)*TP)); 
     }*/
     
    int main(int argc, char *argv[])
    {
         int w = 320, h = 240;//Set image size
         int samps = 50; // set samples
         //Ray cam(Vec(50, 52, 295.6), Vec(0, -0.042612, -1).norm()); // cam pos, dir
         Ray cam(Vec(50, 52, 295.6), Vec(0, -0.14, -1).norm()); // set camera's position and direction
         Vec cx = Vec(w * .5135 / h);
         Vec cy = (cx % cam.d).norm() * .5135;
         Vec r;
         Vec *c = new Vec[w*h];
    //#pragma omp parallel for schedule(dynamic, 1) private(r)       // OpenMP 
         for (int y = 0; y < h; y++) {                  // Loop over image row s
              fprintf(stderr, "\rRendering (%d spp) %5.2f%%", samps * 4, 100.*y / (h - 1));
              unsigned short Xi[3] = {0, 0, y*y*y};
              for (unsigned short x = 0; x < w; x++) // Loop cols
                   for (int sy = 0, i = (h - y - 1) * w + x; sy < 2; sy++) // 2x2 subpixel rows
                        for (int sx = 0; sx < 2; sx++, r = Vec()) { // 2x2 subpixel cols
                             for (int s = 0; s < samps; s++) {
                                  double r1 = 2 * erand48(Xi), dx = r1 < 1 ? sqrt(r1) - 1 : 1 - sqrt(2 - r1);
                                  double r2 = 2 * erand48(Xi), dy = r2 < 1 ? sqrt(r2) - 1 : 1 - sqrt(2 - r2);
                                  Vec d = cx * ( ( (sx + .5 + dx) / 2 + x) / w - .5) +
                                          cy * ( ( (sy + .5 + dy) / 2 + y) / h - .5) + cam.d;
                                  r = r + radiance(Ray(cam.o + d * 140, d.norm()), 0, Xi) * (1. / samps);
                             } // Camera rays are pushed ^^^^^ forward to start in interior
                             c[i] = c[i] + Vec(clamp(r.x), clamp(r.y), clamp(r.z)) * .25;
                        }
         }
         FILE *f = fopen("image.ppm", "w");         // Write image to PPM file.
         fprintf(f, "P3\n%d %d\n%d\n", w, h, 255);
         for (int i = 0; i < w * h; i++)
              fprintf(f, "%d %d %d ", toInt(c[i].x), toInt(c[i].y), toInt(c[i].z));
    }
    

    相关文章

      网友评论

        本文标题:Path Tracing介绍

        本文链接:https://www.haomeiwen.com/subject/ngrqdttx.html