OpenGL学习5——变换

变换(Transformations)

1. 矢量(Vector)

  • 一个矢量拥有方向(direction)大小(magnitude)也称为长度(length)

  • 矢量表示
    \vec v = \left( \begin{matrix} \color{red}{ x} \\ \color{green}{y} \\ \color{blue}{z} \end{matrix} \right)

1.1 矢量与标量的运算

  • 一个标量(scalar)就是一个数字。矢量与标量的加减乘除(下面的+,可以是+, -, .或÷)。
    \left( \begin{matrix} \color{red} 1 \\ \color{green} 2 \\ \color{blue} 3 \end{matrix} \right) + x \rightarrow \left( \begin{matrix} \color{red} 1 \\ \color{green} 2 \\ \color{blue} 3 \end{matrix} \right) + \left( \begin{matrix} x \\ x \\ x \end{matrix} \right) = \left( \begin{matrix} \color{red}1+x \\ \color{green}2+x \\ \color{blue}3+x \end{matrix} \right)

1.2 矢量的负运算

  • 一个矢量的负就是反方向的矢量。
    -\vec v = -\left( \begin{matrix} \color{red} {v_x} \\ \color{green}{ v_y} \\ \color{blue}{ v_z} \end{matrix} \right) = \left( \begin{matrix} -\color{red}{v_x} \\ -\color{green}{v_y} \\ -\color{blue}{v_z} \end{matrix} \right)

1.3 矢量的加减运算

  • 两个矢量相加被定义为矢量之间对应元素之间进行相加,下面展示两个矢量进行相加,相减与此类似:
    \vec v = \left( \begin{matrix} \color{red} 1 \\ \color{green} 2 \\ \color{blue} 3 \end{matrix} \right), \vec k = \left( \begin{matrix} \color{red}4 \\ \color{green}5 \\ \color{blue}6 \end{matrix} \right) \rightarrow \vec v+\vec k= \left( \begin{matrix} \color{red}1+\color{red}4 \\ \color{green}2+\color{green}5 \\ \color{blue}3+\color{blue}6 \end{matrix} \right)= \left( \begin{matrix} \color{red} 5 \\ \color{green} 7 \\ \color{blue} 9 \end{matrix} \right)

1.4 矢量的长度

  • 获取一个矢量的大小或长度我们使用毕达哥拉斯原理(Pythagoras theorem)
    ||\vec v|| = \sqrt{\color{green}x^2 + \color{blue}y^2}
    ||\vec v||表示向量\vec v的长度。
  • 有一个特殊类型的矢量,我们称为单位矢量(unit vector)。单位矢量的长度为1。普通矢量转换为一个单位矢量称为正则化(normalizing)
    \hat{n} = \frac{\vec v} {||\vec v||}

1.5 矢量的乘运算

  • 矢量乘法有两种:

    • 点积(dot product),表示为\vec v\cdot \vec k
    • 叉积(cross product),表示为\vec v\times \vec k
  • 点积运算公式(其中\theta表示两个矢量之间的角度):
    \vec v \cdot \vec k = ||\vec v|| \cdot ||\vec{k}|| \cdot \cos{\theta}
    对于单位矢量
    \vec v\cdot\vec k = 1\cdot 1\cdot \cos{\theta} = \cos{\theta} \\ \cos(90) = 0,\cos(0) = 1.
    点积的计算
    \left( \begin{matrix} \color{red}{0.6} \\ -\color{green}{0.8} \\ \color{blue} 0 \end{matrix} \right) \cdot \left( \begin{matrix} \color{red} 0 \\ \color{green} 1 \\ \color{blue} 0 \end{matrix} \right) = (\color{red}{0.6} * \color{red}{0}) + ( -\color{green}{0.8}*\color{green}1) + (\color{blue}0*\color{blue}0) = -0.8

  • 叉积只定义在3D空间,且需两个非平行的矢量作为输入,结果是与两个输入矢量正交的矢量。如果两个输入矢量之间彼此正交,则叉积将产生三个彼此正交的矢量。(图片取自书中

    叉积

  • 叉积的计算
    \left( \begin{matrix} \color{red}{A_x} \\ \color{green}{A_y} \\ \color{blue}{A_z} \end{matrix} \right) \times \left( \begin{matrix} \color{red}{B_x} \\ \color{green}{B_y} \\ \color{blue}{B_z} \end{matrix} \right) = \left( \begin{matrix} \color{green}{A_y}\cdot\color{blue}{B_z}-\color{blue}{A_z}\cdot\color{green}{B_y} \\ \color{blue}{A_z}\cdot\color{red}{B_x}-\color{red}{A_x}\cdot\color{blue}{B_z} \\ \color{red}{A_x}\cdot\color{green}{B_y}-\color{green}{A_y}\cdot\color{red}{B_x} \end{matrix} \right)

2. 矩阵(Matrices)

  • 一个矩阵(matrix)就是一个数字、符号和/或数学表达式的矩形数组。矩阵中的每个项称为矩阵的元素(element)。一个2X3的矩阵示例:
    \left[ \begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6\end{matrix} \right]
  • 矩阵由(i, j)进行索引,其中i是行,j是列。而3行和2列也称为矩阵的维(dimensions)。

2.1 矩阵的加减

  • 两个矩阵之间的加减是在每个元素上完成的,这意味着只能是维度相同的两个矩阵才可以进行加减运算。下面是一个2X2的矩阵相加示例(减法运算相似):
    \left[ \begin{matrix} \color{red}1 & \color{red}2 \\ \color{green}3 & \color{green}4 \end{matrix} \right] + \left[ \begin{matrix} \color{red}5 & \color{red}6 \\ \color{green}7 & \color{green}8 \end{matrix} \right] = \left[ \begin{matrix} \color{red}{1+5} & \color{red}{2+6} \\ \color{green}{3+7} & \color{green}{4+8} \end{matrix} \right] = \left[ \begin{matrix} \color{red}6 & \color{red}8 \\ \color{green}{10} & \color{green}{12}\end{matrix} \right]

2.1 矩阵与标量的乘积

  • 矩阵与标量乘积相当于矩阵每个元素都乘以标量。
    \color{green}2 \color{black}\cdot \left[ \begin{matrix} 1 & 2 \\ 3 & 4 \end{matrix} \right] = \left[ \begin{matrix} \color{green}2 \color{black}\cdot 1 & \color{green}{2}\color{black}\cdot2 \\ \color{green}2\color{black}\cdot3 & \color{green}2\color{black}\cdot4 \end{matrix} \right] = \left[ \begin{matrix} 2 & 4 \\ 6 & 8 \end{matrix} \right]

2.3 矩阵之间的乘积

  • 矩阵之间相乘的限制:
    • 左侧矩阵的列数与右侧矩阵的行数相等;
    • 矩阵相乘是不可交换(commutative)A\cdot B\neq B\cdot A
      \left[ \begin{matrix} \color{red}1 & \color{red}2 \\ \color{green}3 & \color{green}4 \end{matrix} \right] \cdot \left[ \begin{matrix} \color{blue}5 & \color{purple}6 \\ \color{blue}7 & \color{purple}8 \end{matrix} \right] = \left[ \begin{matrix} \color{red}1\cdot \color{blue}5 + \color{red}2\cdot \color{blue}7 & \color{red}1\cdot \color{purple}6 + \color{red}2\cdot \color{purple}8 \\ \color{green}3\cdot \color{blue}5 + \color{green}4\cdot \color{blue}7 & \color{green}3\cdot \color{purple}6 + \color{green}4\cdot \color{purple}7 \end{matrix} \right] = \left[ \begin{matrix} 19 & 22 \\ 43 & 50\end{matrix} \right]

2.4 矩阵与矢量乘积

  • 矢量实际上是一个N\times 1矩阵,其中N是矢量分部的数量(也称为N维矢量)。

2.5 单位矩阵

  • 单位矩阵(identity matrix)是一个除了对角线元素为1其他元素都是0的N\times N矩阵。下面是一个单位矩阵与矢量相乘:
    \left[ \begin{matrix} \color{red}1 & \color{red}0 & \color{red}0 & \color{red}0 \\ \color{green}0 & \color{green}1 & \color{green}0 & \color{green}0 \\ \color{blue}0 & \color{blue}0 & \color{blue}1 & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right] \cdot \left( \begin{matrix} 1 \\ 2 \\ 3 \\ 4 \end{matrix} \right) = \left( \begin{matrix} \color{red}1 \color{black}\cdot 1 \\ \color{green}1 \color{black}\cdot 2 \\ \color{blue}1 \color{black}\cdot 3 \\ \color{purple}1 \color{black}\cdot 4 \end{matrix} \right) = \left( \begin{matrix} 1 \\ 2 \\ 3 \\ 4 \end{matrix} \right)

2.6 缩放(Scaling)

  • 没个坐标轴的缩放因此不一样称为不成比例缩放(non-uniform scale),如果所有坐标轴的缩放因子相同则是成比例缩放(uniform scale)。矢量缩放可通过与矩阵相乘实现,下面运算中左侧矩阵称为缩放矩阵
    \left[ \begin{matrix} \color{red}{S_1} & \color{red}0 & \color{red}0 & \color{red}0 \\ \color{green}0 & \color{green}{S_2} & \color{green}0 & \color{green}0 \\ \color{blue}0 & \color{blue}0 & \color{blue}{S_3} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right] \cdot \left( \begin{matrix} x \\ y \\ z \\ 1 \end{matrix} \right) = \left( \begin{matrix} \color{red}{S_1} \color{black}\cdot x \\ \color{green}{S_2} \color{black}\cdot y \\ \color{blue}{S_3} \color{black}\cdot z \\ \color{purple}{1} \end{matrix} \right)

2.7 平移(Translation)

  • 平移就是原来矢量加上另一个矢量产生一个指向不同位置的矢量,相当于基于一个平移矢量移动矢量。矢量平移可通过平移矩阵与矢量相乘实现:
    \left[ \begin{matrix} \color{red}1 & \color{red}0 & \color{red}0 & \color{red}{T_x} \\ \color{green}0 & \color{green}1 & \color{green}0 & \color{green}{T_y} \\ \color{blue}0 & \color{blue}0 & \color{blue}1 & \color{blue}{T_z} \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}{1} \end{matrix} \right] \cdot \left( \begin{matrix} x \\ y \\ z \\ 1 \end{matrix} \right) = \left( \begin{matrix} x+\color{red}{T_x} \\ y+\color{green}{T_y} \\ z+\color{blue}{T_z} \\ \color{purple}1 \end{matrix} \right)
  • 矢量的w分部也称为齐次坐标(homogeneous coordinate)。要将一个齐次坐标矢量转换为3D坐标矢量,我只需将x,y和z坐标除以w坐标。使用齐次坐标的好处是:允许我们对3D矢量做矩阵平移,而且下一章我们将使用w值创建3D透视。同时,齐次坐标等于0的矢量也称为方向矢量(direction vector)

2.8 旋转(Rotation)

  • 角度与弧度的转换

  • 3D坐标空间的旋转需要指定一个角度和一个旋转轴(rotation axis)

  • 3D空间中针对不同单位坐标轴的旋转矩阵如下所示,其中角度由\theta表示:

    • 绕x轴旋转
      \left[ \begin{matrix} \color{red}1 & \color{red}0 & \color{red}0 & \color{red}0 \\ \color{green}0 & \color{green}{\cos\theta} & \color{green}{-\sin\theta} & \color{green}0 \\ \color{blue}0 & \color{blue}{\sin\theta} & \color{blue}{\cos\theta} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right] \cdot \left( \begin{matrix} x \\ y \\ z \\ 1 \end{matrix} \right) = \left( \begin{matrix} x \\ \color{green}{\cos\theta}\color{black}\cdot y-\color{green}{\sin\theta}\color{black}\cdot z \\ \color{blue}{\sin\theta}\color{black}{\cdot y}+\color{blue}{\cos\theta}\color{black}\cdot z \\ 1 \end{matrix} \right)
    • 绕y轴旋转
      \left[ \begin{matrix} \color{red}{\cos\theta} & \color{red}0 & \color{red}{\sin\theta} & \color{red}0 \\ \color{green}0 & \color{green}1 & \color{green}0 & \color{green}0 \\ \color{blue}{-\sin\theta} & \color{blue}0 & \color{blue}{\cos\theta} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right] \cdot \left( \begin{matrix} x \\ y \\ z \\ 1 \end{matrix} \right) = \left( \begin{matrix} \color{red}{\cos\theta}\color{black}\cdot x+\color{red}{\sin\theta}\color{black}\cdot z \\ y \\ \color{blue}{-\sin\theta}\color{black}\cdot x+\color{blue}{\cos\theta}\color{black}\cdot z \\ 1 \end{matrix} \right)
    • 绕z轴旋转
      \left[ \begin{matrix} \color{red}{\cos\theta} &\color{red}{-\sin\theta} & \color{red}0 & \color{red}0 \\ \color{green}{\sin\theta} &\color{green}{\cos\theta} &\color{green}0 &\color{green}0 \\ \color{blue}0 & \color{blue}0 & \color{blue}1 & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right] \cdot \left( \begin{matrix} x \\ y \\ z \\ 1 \end{matrix} \right) = \left( \begin{matrix} \color{red}{\cos\theta}\color{black}\cdot x-\color{red}{\sin\theta}\color{black}\cdot y \\ \color{green}{\sin\theta}\color{black}\cdot x+\color{green}{\cos\theta}\color{black}\cdot y \\ z \\ 1 \end{matrix} \right)
  • 要绕任意3D空间坐标轴旋转,我们可以通过结合上述3个旋转矩阵来实现,如先绕x轴旋转,然后y轴然后是z轴。但是,这样会引入一个叫做万向锁(Gimbal lock) 的问题。一个更好的解决方案是直接绕任意单位坐标轴进行旋转而不是通过组合旋转矩阵。任意单位坐标轴(\color{red}{R_x},\color{green}{R_y},\color{blue}{R_z})的旋转矩阵如下所示:
    \left[ \begin{matrix} cos\theta+\color{red}{R_x}^2(\color{black}1-\cos\theta) &\color{red}{R_x}\color{green}{R_y}(\color{black}1-\cos\theta)-\color{blue}{R_z}\color{black}\sin\theta &\color{red}{R_x}\color{blue}{R_z}(\color{black}1-\cos\theta)+\color{green}{R_y}\color{black}\sin\theta & 0 \\ \color{green}{R_y}\color{red}{R_x}(\color{black}1-\cos\theta)+\color{blue}{R_z}\color{black}\sin\theta &\cos\theta+\color{green}{R_y}^2(\color{black}1-\cos\theta) &\color{green}{R_y}\color{blue}{R_z}(\color{black}1-\cos\theta)-\color{red}{R_x}\color{black}\sin\theta & 0 \\ \color{blue}{R_z}\color{red}{R_x}(\color{black}1-\cos\theta)-\color{green}{R_y}\color{black}\sin\theta &\color{blue}{R_z}\color{green}{R_y}(\color{black}1-\cos\theta)+\color{red}{R_x}\color{black}\sin\theta &\cos\theta+\color{blue}{R_z}^2(\color{black}1-\cos\theta) & 0 \\ 0 & 0 & 0 & 1 \end{matrix} \right]

  • 注意:上面的旋转矩阵也无法完全解决万向锁问题,要完全解决万向锁问题我们需要使用四元数(quaternions) 来表示旋转。

2.9 组合变换矩阵

  • 矩阵用于变换的真正威力在于我们可以将多个变换操作组合到一个矩阵中。下面的示例展示将矢量缩放2和平移(1,2,3)
    Trans.Scale= \left[ \begin{matrix} \color{red}1 & \color{red}0 & \color{red}0 & \color{red}1 \\ \color{green}0 & \color{green}1 & \color{green}0 & \color{green}2 \\ \color{blue}0 & \color{blue}0 & \color{blue}1 & \color{blue}3 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right] \cdot \left[ \begin{matrix} \color{red}2 & \color{red}0 & \color{red}0 & \color{red}0 \\ \color{green}0 & \color{green}2 & \color{green}0 & \color{green}0 \\ \color{blue}0 & \color{blue}0 & \color{blue}2 & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right] = \left[ \begin{matrix} \color{red}2 & \color{red}0 & \color{red}0 & \color{red}1 \\ \color{green}0 & \color{green}2 & \color{green}0 & \color{green}2 \\ \color{blue}0 & \color{blue}0 & \color{blue}2 & \color{blue}3 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right]
    组合矩阵与矢量相乘
    \left[ \begin{matrix} \color{red}2 & \color{red}0 & \color{red}0 & \color{red}1 \\ \color{green}0 & \color{green}2 & \color{green}0 & \color{green}2 \\ \color{blue}0 & \color{blue}0 & \color{blue}2 & \color{blue}3 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{matrix} \right] \cdot \left[ \begin{matrix} x \\ y \\ z \\ 1 \end{matrix} \right] = \left[ \begin{matrix} \color{red}2\color{black}x +\color{red}1\\ \color{green}2\color{black}y +\color{green}2 \\ \color{blue}2\color{black}z +\color{blue}3 \\ 1 \end{matrix} \right]
  • 当进行矩阵相乘时,最右的矩阵最先与矢量相乘,所以我们应该从右往左看矩阵相乘。在组合矩阵时建议先进行缩放操作,然后旋转最后是平移,不然它们之间可能彼此影响。

3. GLM

  • GLM代表OpenGL Mathematics,是一个header-only类库,这意味着只需包含合适的头文件,无需链接和编译即可应用类库。
  • GLM类库的下载
    Gitee 极速下载/glm
  • GLM解压内容


    GLM内容
  • GLM类库使用:直接将glm文件夹拷贝到项目目录中。
  • 我们需要的大部分GLM功能可以在下面3个头文件找到:
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
  • 使用GLM类库将矢量(1,0,0)平移(1,1,0)(注意代码中将矢量定义为glm::vec4,齐次坐标设置为1.0):
glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
glm::mat4 trans = glm::mat4(1.0f);    // 单位矩阵
// 通过平移单位矩阵获取平移矩阵
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
vec = trans * vec;
std::cout << vec.x << vec.y << vec.z << std::endl;
  • 下面是一个使用GLM进行缩放和旋转的例子:
glm::mat4 trans = glm::mat4(1.0f);
// 以z轴旋转90度
trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
  • 注意:旋转应该绕单位矢量进行,如果不是绕x,y或z轴需要先将矢量正则化。
  • 修改顶点着色器,添加mat4的uniform变量:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

uniform mat4 transform;

void main()
{
    gl_Position = transform * vec4(aPos, 1.0f);
    TexCoord = aTexCoord;
}
  • 在代码中为顶点着色器的uniform变量设置值:
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
  • 渲染效果


    缩放+旋转
  • 让图形随时间进行旋转然后平移(下面代码需在渲染循环中执行):
glm::mat4 trans = glm::mat4(1.0f);
// 平移
trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));
// 随时间旋转
trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0, 0.0, 1.0));
  • 渲染效果


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

推荐阅读更多精彩内容