Path Tracing介紹

Ray Tracing實(shí)際上是一種渲染框架雷逆,通過迭代計(jì)算尾组,得到鏡面反射效果和折射效果壤追,在上一篇文章中磕道,我使用了最基本的方法,當(dāng)光線和物體相交時(shí)行冰,根據(jù)交點(diǎn)和法線方向計(jì)算唯一的反射光線和折射光線溺蕉,進(jìn)而迭代處理。但是現(xiàn)實(shí)情況往往并非如此悼做,反射光線方向不一定是唯一的疯特,因此Path Tracing作為Ray Tracing的一個(gè)具體實(shí)現(xiàn),可以實(shí)現(xiàn)更加真實(shí)的效果肛走。Path Tracing使用了蒙特卡洛方法漓雅,利用BRDF隨機(jī)跟蹤多條反射光線,隨后根據(jù)這些光線的貢獻(xiàn)計(jì)算該點(diǎn)的顏色值朽色。因此邻吞,可想而知,Path Tracing相比于上一篇文章的結(jié)果葫男,非常耗時(shí)抱冷,但是結(jié)果也更加真實(shí)。當(dāng)然BRDF里有很多模型梢褐,目標(biāo)也是解決不同場景下出現(xiàn)的問題旺遮,因此這是一個(gè)深坑,我也沒有爬出來orz盈咳。
當(dāng)然某些同學(xué)還會(huì)遇到一類渲染方法叫做Ray Casting耿眉,事實(shí)上這個(gè)渲染方法就是Ray Tracing的第一步,發(fā)射光線鱼响,與物體相交跷敬,這個(gè)方法可以實(shí)現(xiàn)陰影的效果,但是沒有包括后續(xù)的反射效果和折射效果的計(jì)算热押。


result_25k.png

上面這張圖片是Path Tracing的一個(gè)渲染例子,可以看到效果確實(shí)很不錯(cuò)斤寇,但是代價(jià)是渲染速度非常慢桶癣,另外在這里需要強(qiáng)調(diào)的一點(diǎn)是,右邊的球是透明的娘锁,因此具有折射屬性牙寞,所以可以看到,光源的光線穿過了球體,在下面形成了圓形间雀,還有折射和鏡面反射是密不可分的悔详,存在折射就會(huì)存在鏡面反射,這也是為什么右邊的球存在鏡面反射惹挟。
以下是代碼部分:

#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));
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茄螃,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子连锯,更是在濱河造成了極大的恐慌归苍,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件运怖,死亡現(xiàn)場離奇詭異拼弃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)摇展,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門吻氧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人咏连,你說我怎么就攤上這事盯孙。” “怎么了捻勉?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵镀梭,是天一觀的道長。 經(jīng)常有香客問我踱启,道長报账,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任埠偿,我火速辦了婚禮透罢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冠蒋。我一直安慰自己羽圃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布抖剿。 她就那樣靜靜地躺著朽寞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斩郎。 梳的紋絲不亂的頭發(fā)上脑融,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音缩宜,去河邊找鬼肘迎。 笑死甥温,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妓布。 我是一名探鬼主播姻蚓,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼匣沼!你這毒婦竟也來了狰挡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤肛著,失蹤者是張志新(化名)和其女友劉穎圆兵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枢贿,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡殉农,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了局荚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片超凳。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖耀态,靈堂內(nèi)的尸體忽然破棺而出轮傍,到底是詐尸還是另有隱情,我是刑警寧澤首装,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布创夜,位于F島的核電站,受9級(jí)特大地震影響仙逻,放射性物質(zhì)發(fā)生泄漏驰吓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一系奉、第九天 我趴在偏房一處隱蔽的房頂上張望檬贰。 院中可真熱鬧,春花似錦缺亮、人聲如沸翁涤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽葵礼。三九已至,卻和暖如春并鸵,著一層夾襖步出監(jiān)牢的瞬間章咧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工能真, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓粉铐,卻偏偏與公主長得像疼约,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蝙泼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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