OpenGL光照

预备知识

材料

材料设置


void glMaterialfv(

    GLenum face, //设置材质的面片,是前面,后面,还是双面
    GLenum pname, //材质的类型,是环境光材质,漫反射,反射,高光,辐射光等
    GLfloat *param //参数,跟光的参数一样,表示对应的值。
); 

  • face参数

指出将材质应用于物体的哪面


GL_FRONT

GL_BACK

GL_FRONT_AND_BACK

  • pname参数可选项

GL_AMBIENT//环境光颜色

GL_DIFFUSE//漫反射颜色 

GL_SPECULAR//镜面反射颜色 

GL_SHININESS//镜面反射指数高光 

GL_EMISSION//辐射光颜色 

  • param

颜色参数表示对该种色光的反射程度

1表示完全反射,0表示完全吸收

材质状态

材质会被保存在状态里面,所以如果不改变材质的话,默认会用最近用过的材质。

在使用某种材质的前文中需要设定好材质属性,再进行绘制。

这一点和之前所学的glTranslatefglScalefglRotatef有一定相似之处

思考

发现在给物体设置材质的时候,在本次实验里面设定GL_FRONT还是GL_FRONT_AND_BACK没有差别,猜想是因为这次的物体在查看的时候显示出来的全是FRONT面

光照

光照设置


void glLightfv(

    GLenum light, //指定光源
    GLenum pname, //材质的类型,是环境光材质,漫反射,反射,高光,辐射光等
    const GLfloat *param //参数,跟光的参数一样,表示对应的值。
);

  • light参数

OpenGL里面最多可设置八个光源,GL_LIGHT0、GL_LIGHT1、……、GL_LIGHT7

light参数便是指定对某一光源进行属性设置

  • pname参数可选项

GL_AMBIENT//环境光颜色,默认值(0,0,0,1)

GL_DIFFUSE//漫反射颜色,默认值(1,1,1,1) 

GL_SPECULAR//镜面反射颜色,默认值(1,1,1,1)

GL_POSITION//光源位置,默认值(0,0,1,0)

GL_SPOT_CUTOFF//角度,对于聚光灯设定

GL_SPOT_DIRECTION//光照方向,对于聚光灯设定 

GL_SPOT_EXPONENT//聚集度,对于聚光灯设定 

  • param

设置pname项的具体参数

其中GL_POSITION的第四个参数w

若w=0,表示定向光源,也即光源位于无穷远处,照在物体上时可当成平行光

若w=1,表示定点光源,光源位于特定的某个点处

光源开启

光源和材质不一样,材质只需要直接指定即可,但是光源需要“开启”,才能显示出作用

在设置光源之前,需要开启光照模式


glEnable(GL_LIGHTING);

设置完某一光源后,想要在结果里面显示出有光源的效果,需要像打开灯的开关一样打开它


glEnable(GL_LIGHT0);

实验要求

使用Visual Studio C++编译已有项目工程。

模型尺寸不做具体要求。要求修改代码达到以下要求:

1.通过设置材料使得桌面和四条腿的颜色各不相同,分别为:(1, 0, 0), (0, 1, 0), (1, 1, 0), (0, 1, 1), (0, 0, 1);

2.通过设置材料使得茶壶为金黄色;

3.添加按键处理,移动场景中的光源,并能改变光源的颜色(在两种颜色间切换,颜色自己定义);

4.修改茶壶的镜面反射系数,使之对光源呈现高光;

5.在场景中添加一个聚光光源,其照射区域正好覆盖茶壶,并能调整改聚光光源的照射角度和朝向。

具体做法

一)设置材质

根据实验要求对茶壶,桌面,四条桌腿分别设置不同的材质

//设置材质参数
    GLfloat specular[] = { 0.6f,0.6f,0.6f,1 };
    GLfloat diffuse0[] = { 0.85f,0.65f,0.2f,1 };
    GLfloat diffuse1[] = { 1,0,0,1 };
    GLfloat diffuse2[] = { 0,1,0,1 };
    GLfloat diffuse3[] = { 1,1,0,1 };
    GLfloat diffuse4[] = { 0,1,1,1 };
    GLfloat diffuse5[] = { 0,0,1,1 };

茶壶的镜面反射与漫反射设置不同的颜色

//茶壶
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);//正反面的镜面反射颜色
    glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 50);//镜面反射系数
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse0);//正反面的漫反射颜色

桌面与每条桌腿自身的镜面反射与漫反射设置成相同颜色,不考虑高光

//桌面
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse1);//正反面的镜面反射颜色
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse1);//正反面的漫反射颜色

二)设置光照

设定点光源与聚光灯的一系列参数

/* 点光源 */
GLfloat light0_pos[] = { 5,5,5,1 };//Position
GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };//color
GLfloat purple[] = { 1.0, 0, 1.0, 1.0 };//color
/*聚光灯*/
GLfloat light1_pos[] = { 0.0f, 5.0f, 0.0f, 1.0f };
GLfloat light_dir[] = { 0,-1,0 };
GLfloat angle = 5.0f;

配置光源并打开

  • 点光源
/* 点光源 */
    glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);//位置
    //环境光可在白光和紫光之间切换
    if (isWhite) {
        glLightfv(GL_LIGHT0, GL_AMBIENT, white);//环境光属性
    }
    else {
        glLightfv(GL_LIGHT0, GL_AMBIENT, purple);//环境光属性
    }
    glLightfv(GL_LIGHT0, GL_DIFFUSE, white);//漫反射属性
    glLightfv(GL_LIGHT0, GL_SPECULAR, white);//镜面反射属性
    glEnable(GL_LIGHT0);
    
  • 聚光灯
/* 聚光灯 */
    glLightfv(GL_LIGHT1, GL_POSITION, light1_pos);//位置
    if (isWhite) {
        glLightfv(GL_LIGHT1, GL_AMBIENT, white);//环境光属性
    }
    else {
        glLightfv(GL_LIGHT1, GL_AMBIENT, purple);//环境光属性
    }
    glLightfv(GL_LIGHT1, GL_DIFFUSE, white);//漫反射属性
    glLightfv(GL_LIGHT1, GL_SPECULAR, white);//镜面反射属性
    glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, angle);//设置聚光灯角度
    glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir);//设置聚光灯方向
    glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2);//设置聚光灯聚集度
    glEnable(GL_LIGHT1);

三)设置按键功能

光照颜色切换

使用l按键来切换,isWhite是事先定义好的一个全局变量

//切换光照颜色,白色or紫色
case 'l': {
    isWhite = !isWhite;
    break;
}

光源位置调整

考虑到按键数量有限,若是给点光源位置,聚光灯位置,聚光灯朝向分别设置6个按键分别控制上下前后左右的调整,按键数可能不够也非常不好管理。所以想让这6个按键彼此共用,一方面需要管理的按键数量减少,另一方面能够尽可能保证按键与其功能有一定意义上的关联,便于记忆与理解。
共用的话就需要做好切换,因此引入两个全局变量 isLight0isPos调整

  • isLight0在true与false间切换,true代表当前控制的是点光源,false表示聚光灯
  • isPos在true与false间切换,true代表当前控制的是position,false表示direction(由于只有聚光灯有direction,所以只有当isLight0=false;时才会进行对isPos的判断

按键5与按键0分别进行isLight0isPos的控制

//移动控制,移动环境光or聚光灯
case '5': {
    isLight0 = !isLight0;
    break;
}
//移动控制,改变聚光灯角度or位置
case '0': {
    isPos = !isPos;
    break;
}

数字键盘上的按键824613分别表示上下左右前后

//光照左移
    case '4': {
        if (isLight0) //移动环境光
        {
            light0_pos[0] -= 0.2f;
        }
        else //移动聚光灯
        {
            if (isPos) {
                light1_pos[0] -= 0.2f;
            }
            else
            {
                light_dir[0] -= 0.05f;
            }
        }
        break;
    }          

聚光灯角度调整

聚光灯角度需要设计一个范围,只能在该范围内放大减小
angle是预先定义好的全局变量
用按键+-控制角度变大变小

//增大聚光灯角度
    case '+': {
        if (angle <= 89.8) {
            angle += 0.2f;
        }
        break;
    }
    //减小聚光灯角度
    case '-': {
        if (angle >= 0.2) {
            angle -= 0.2f;
        }
        break;
    }
    }

实验结果






附源码参考

// glutEx1.cpp : 定义控制台应用程序的入口点。
//


#include <stdlib.h>
#include "glut.h"

float fTranslate;
float fRotate;
float fScale     = 1.0f;    // set inital scale value to 1.0f

bool bPersp = false;
bool bAnim = false;
bool bWire = false;
bool isWhite = true;
bool isLight0 = true;
bool isPos = true;

int wHeight = 0;
int wWidth = 0;

/* 点光源 */
GLfloat light0_pos[] = { 5,5,5,1 };//Position
GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };//color
GLfloat purple[] = { 1.0, 0, 1.0, 1.0 };//color

/*聚光灯*/
GLfloat light1_pos[] = { 0.0f, 5.0f, 0.0f, 1.0f };
GLfloat light_dir[] = { 0,-1,0 };
GLfloat angle = 5.0f;

void Draw_Leg();

void Draw_Triangle() // This function draws a triangle with RGB colors
{
    //设置材质参数
    GLfloat specular[] = { 0.6f,0.6f,0.6f,1 };
    GLfloat diffuse0[] = { 0.85f,0.65f,0.2f,1 };
    GLfloat diffuse1[] = { 1,0,0,1 };
    GLfloat diffuse2[] = { 0,1,0,1 };
    GLfloat diffuse3[] = { 1,1,0,1 };
    GLfloat diffuse4[] = { 0,1,1,1 };
    GLfloat diffuse5[] = { 0,0,1,1 };

    //茶壶
    glPushMatrix();
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);//正反面的镜面反射颜色
    glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 50);//镜面反射系数
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse0);//正反面的漫反射颜色
    glTranslatef(0, 0, 4+1);
    glRotatef(90, 1, 0, 0);
    glutSolidTeapot(1);
    glPopMatrix();

    //桌面
    glPushMatrix();
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse1);//正反面的镜面反射颜色
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse1);//正反面的漫反射颜色
    glTranslatef(0, 0, 3.5);
    glScalef(5, 4, 1);
    glutSolidCube(1.0);
    glPopMatrix();

    //桌腿1
    glPushMatrix();
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse2);//正反面的镜面反射颜色
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse2);//正反面的漫反射颜色
    //glMaterialfv(GL_BACK, GL_SPECULAR, diffuse2);//正反面的镜面反射颜色
    //glMaterialfv(GL_BACK, GL_DIFFUSE, diffuse2);//正反面的漫反射颜色
    glTranslatef(1.5, 1, 1.5);
    Draw_Leg();
    glPopMatrix();

    //桌腿2
    glPushMatrix();
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse3);//正反面的镜面反射颜色
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse3);//正反面的漫反射颜色
    glTranslatef(-1.5, 1, 1.5);
    Draw_Leg();
    glPopMatrix();

    //桌腿3
    glPushMatrix();
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse4);//正反面的镜面反射颜色
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse4);//正反面的漫反射颜色
    glTranslatef(1.5, -1, 1.5);
    Draw_Leg();
    glPopMatrix();

    //桌腿4
    glPushMatrix();
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse5);//正反面的镜面反射颜色
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse5);//正反面的漫反射颜色
    glTranslatef(-1.5, -1, 1.5);
    Draw_Leg();
    glPopMatrix();

}

void Draw_Leg()
{
    glScalef(1, 1, 3);
    glutSolidCube(1.0);
}

void updateView(int width, int height)
{
    glViewport(0,0,width,height);                       // Reset The Current Viewport

    glMatrixMode(GL_PROJECTION);                        // Select The Projection Matrix
    glLoadIdentity();                                   // Reset The Projection Matrix

    float whRatio = (GLfloat)width/(GLfloat)height;
    if (bPersp) {
        gluPerspective(45.0f, whRatio,0.1f,100.0f);
        //glFrustum(-3, 3, -3, 3, 3,100);
    } else {
        glOrtho(-3 ,3, -3, 3,-100,100);
    }

    glMatrixMode(GL_MODELVIEW);                         // Select The Modelview Matrix
}

void reshape(int width, int height)
{
    if (height==0)                                      // Prevent A Divide By Zero By
    {
        height=1;                                       // Making Height Equal One
    }

    wHeight = height;
    wWidth = width;

    updateView(wHeight, wWidth);
}

void idle()
{
    glutPostRedisplay();
}

float eye[] = {0, 0, 8};
float center[] = {0, 0, 0};

void key(unsigned char k, int x, int y)
{
    switch(k)
    {
    case 27:
    case 'q': {exit(0); break; }
    //切换投影模式
    case 'p': {bPersp = !bPersp; break; }
    //旋转
    case ' ': {bAnim = !bAnim; break;}
    //切换线框模式
    case 'o': {bWire = !bWire; break;}
    //整体左移
    case 'a': {
        eye[0] += 0.2f;
        center[0] += 0.2f;
        break;
              }
    //整体右移
    case 'd': {
        eye[0] -= 0.2f;
        center[0] -= 0.2f;
        break;
              }
    //整体上移
    case 'w': {
        eye[1] -= 0.2f;
        center[1] -= 0.2f;
        break;
              }
    //整体下移
    case 's': {
        eye[1] += 0.2f;
        center[1] += 0.2f;
        break;
              }
    //整体前移
    case 'z': {
        eye[2] -= 0.2f;
        center[2] -= 0.2f;
        break;
              }
    //整体后移
    case 'c': {
        eye[2] += 0.2f;
        center[2] += 0.2f;
        break;
              }
    //切换光照颜色,白色or紫色
    case 'l': {
        isWhite = !isWhite;
        break;
        }
    //移动控制,移动环境光or聚光灯
    case '5': {
        isLight0 = !isLight0;
        break;
    }
    //移动控制,改变聚光灯角度or位置
    case '0': {
        isPos = !isPos;
        break;
    }
    //光照左移
    case '4': {
        if (isLight0) //移动环境光
        {
            light0_pos[0] -= 0.2f;
        }
        else //移动聚光灯
        {
            if (isPos) {
                light1_pos[0] -= 0.2f;
            }
            else
            {
                light_dir[0] -= 0.05f;
            }
        }
        break;
    }         
    //光照右移
    case '6': {
        if (isLight0) //移动环境光
        {
            light0_pos[0] += 0.2f;
        }
        else //移动聚光灯
        {
            if (isPos) {
                light1_pos[0] += 0.2f;
            }
            else
            {
                light_dir[0] += 0.05f;
            }
        }
        break;
    }
    //光照上移
    case '8': {
        if (isLight0) //移动环境光
        {
            light0_pos[1] += 0.2f;
        }
        else //移动聚光灯
        {
            if (isPos) {
                light1_pos[1] += 0.2f;
            }
            else
            {
                light_dir[1] += 0.05f;
            }
        }
        break;
    }
    //光照下移
    case '2': {
        if (isLight0) //移动环境光
        {
            light0_pos[1] -= 0.2f;
        }
        else //移动聚光灯
        {
            if (isPos) {
                light1_pos[1] -= 0.2f;
            }
            else
            {
                light_dir[1] -= 0.05f;
            }
        }
        break;
    }
    //光照前移
    case '1': {
        if (isLight0) //移动环境光
        {
            light0_pos[2] += 0.2f;
        }
        else //移动聚光灯
        {
            if (isPos) {
                light1_pos[2] += 0.2f;
            }
            else
            {
                light_dir[2] += 0.05f;
            }
        }
        break;
    }
    //光照后移
    case '3': {
        if (isLight0) //移动环境光
        {
            light0_pos[2] -= 0.2f;
        }
        else //移动聚光灯
        {
            if (isPos) {
                light1_pos[2] -= 0.2f;
            }
            else
            {
                light_dir[2] -= 0.05f;
            }
        }
        break;
    }
    //增大聚光灯角度
    case '+': {
        if (angle <= 89.8) {
            angle += 0.2f;
        }
        break;
    }
    //减小聚光灯角度
    case '-': {
        if (angle >= 0.2) {
            angle -= 0.2f;
        }
        break;
    }
    }
    updateView(wHeight, wWidth);
}


void redraw()
{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();                                   // Reset The Current Modelview Matrix

    gluLookAt(eye[0], eye[1], eye[2],
        center[0], center[1], center[2],
        0, 1, 0);               // 场景(0,0,0)的视点中心 (0,5,50),Y轴向上

    if (bWire) {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }
    else {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);

    /* 点光源 */
    glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);//位置
    //环境光可在白光和紫光之间切换
    if (isWhite) {
        glLightfv(GL_LIGHT0, GL_AMBIENT, white);//环境光属性
    }
    else {
        glLightfv(GL_LIGHT0, GL_AMBIENT, purple);//环境光属性
    }
    glLightfv(GL_LIGHT0, GL_DIFFUSE, white);//漫反射属性
    glLightfv(GL_LIGHT0, GL_SPECULAR, white);//镜面反射属性
    glEnable(GL_LIGHT0);

    /* 聚光灯 */
    glLightfv(GL_LIGHT1, GL_POSITION, light1_pos);//位置
    if (isWhite) {
        glLightfv(GL_LIGHT1, GL_AMBIENT, white);//环境光属性
    }
    else {
        glLightfv(GL_LIGHT1, GL_AMBIENT, purple);//环境光属性
    }
    glLightfv(GL_LIGHT1, GL_DIFFUSE, white);//漫反射属性
    glLightfv(GL_LIGHT1, GL_SPECULAR, white);//镜面反射属性

    glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, angle);//设置聚光灯角度
    glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir);//设置聚光灯方向
    glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2);//设置聚光灯聚集度
    glEnable(GL_LIGHT1);

    //  glTranslatef(0.0f, 0.0f,-6.0f);         // Place the triangle at Center
    glRotatef(fRotate, 0, 1.0f, 0);         // Rotate around Y axis
    glRotatef(-90, 1, 0, 0);
    glScalef(0.2, 0.2, 0.2);
    Draw_Triangle();                        // Draw triangle

    if (bAnim) fRotate    += 0.5f;
    glutSwapBuffers();
}

int main (int argc,  char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
    glutInitWindowSize(480,480);
    int windowHandle = glutCreateWindow("Simple GLUT App");

    glutDisplayFunc(redraw);
    glutReshapeFunc(reshape);   
    glutKeyboardFunc(key);
    glutIdleFunc(idle);

    glutMainLoop();
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,312评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,578评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,337评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,134评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,161评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,303评论 1 280
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,761评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,421评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,609评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,450评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,504评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,194评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,760评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,836评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,066评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,612评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,178评论 2 341