3D變換:模型还棱,視圖,投影與Viewport

[OpenGL ES 03]3D變換:模型惭等,視圖珍手,投影與Viewport

羅朝輝 (http://www.cnblogs.com/kesalin/)

本文遵循“署名-非商業(yè)用途-保持一致”創(chuàng)作公用協(xié)議

前言

本來打算直接寫教程 04 的,但是想到3D 變換涉及的數(shù)學知識較多辞做,往往是很多初學者的攔路虎(比如我自己)琳要。再加上OpenGL ES 2.0 不再提供OpenGL ES 1.0中 3D 變換相關(guān)的一些重量級函數(shù),如 glMatrixMode(GL_PROJECTION); glMatrixMode(GL_MODELVIEW); glLoadMatrixf; glMultMatrix 等秤茅,這些函數(shù)在 OpenGL ES 2.0 中均需要我們自己去實現(xiàn)稚补。 如果不對線性代數(shù)與幾何知識作一些簡單介紹,恐怕不少人難以理解文中的一些步驟為什么要那么做嫂伞。因此今天這一篇文章將放棄原定計劃孔厉,先來介紹一些 3D 數(shù)學以及 3D 變換相關(guān)的知識。BTW帖努,原定計劃的代碼示例已經(jīng)寫好了,有興趣的同學可以先行瀏覽粪般,代碼放在這里拼余,運行效果如下:

OpenGL ES 04 示例

一,3D數(shù)學歷史

我們都學過幾何學亩歹,應該都知道歐幾里得(公元前3世紀希臘數(shù)學家)這位幾何學鼻祖匙监,正是這位大牛創(chuàng)建了歐幾里得幾何學,他提出了基于 X小作,Y亭姥,Z 三軸的三維空間概念。到了17世紀顾稀,又出了位大牛笛卡爾达罗,我們通常所說的笛卡爾坐標就是他的創(chuàng)造,笛卡爾坐標非常完美地將歐幾里得幾何學理論與代數(shù)學聯(lián)系到一塊静秆。正是因為有了笛卡爾坐標粮揉,我們才能夠用簡單的矩陣(Matrix)來表示三維變換。但用矩陣來表示三維變換操作有一個無法解決的問題-萬向節(jié)鎖 抚笔。什么是萬向節(jié)鎖呢扶认?簡單地說就是兩個軸旋轉(zhuǎn)到同一個方向上去了,這兩個軸平行了殊橙,因此就比原來少了一維(詳情可參考這里)辐宾。過了一百多年狱从,漢密爾頓(Sir William Rowan Hamilton)創(chuàng)建了四元數(shù)(quaternion)解決了因為旋轉(zhuǎn)而導致萬向節(jié)鎖的問題,然后四元數(shù)還有其他用處叠纹,但在3D數(shù)學里主要是用來處理旋轉(zhuǎn)問題季研。

好吧,或許你看得一頭霧水吊洼,不要緊训貌,你只要知道:用矩陣來表示3D變換,但矩陣在表示旋轉(zhuǎn)時可能會導致萬向節(jié)鎖的問題冒窍,而使用四元數(shù)可以避免萬向節(jié)鎖就可以了递沪。

二,矩陣變換

在前面提到可使用 Matrix 來表示三維變換操作综液,那么變換又是如何通過 Matrix 實現(xiàn)的呢款慨?下面就來講這個。在這里我推薦一本3D數(shù)學入門書籍:《3D數(shù)學基礎(chǔ):圖形與游戲開發(fā)

通常我們使用 4 維向量 (x, y, z, w) 表示在3D空間中的一個點谬莹,最后一維 w 表示齊次坐標檩奠。齊次坐標的含義是兩條平行線在投影平面的無窮遠處相交于一點,但在 Matrix 中沒有表示無窮大附帽,所以增加了齊次坐標這一維埠戳。你可以想象下,火車軌道的兩條邊在無限遠處看起來就相交于一點蕉扮,齊次坐標詳細的介紹可以參考這篇文章整胃。

矩陣運算規(guī)則:

  1. 若矩陣 A 和 B 不是互矩陣,則不滿足乘法交換律喳钟,即 A × B 不等于 B × A屁使;
  2. M × N 階的矩陣只能和 N × O 階的矩陣相乘,即 N 的階數(shù)相等奔则,結(jié)果為 M × O 階的矩陣蛮寂;
  3. 矩陣 A × B 的運算過程是 A 的每一行依次乘以 B 的每一列作為結(jié)果矩陣中的一行;
  4. 矩陣 A 的逆矩陣 B 滿足 A × B = B × A = 單位矩陣易茬。
  5. 單位矩陣是對角線上的值為1酬蹋,其余均為 0 的矩陣。單位矩陣不影響坐標變換(你可以將下面的3D變換矩陣換成單位矩陣來思考下)疾呻。

3D空間的物體投影到2D平面上時除嘹,就需要使用到齊次坐標,因此我們需要使用 4 × 4 的 Matrix 來表示變換岸蜗。在編程語言中尉咕,這樣的 Matrix 可用大小為 16 的一維數(shù)組或4 × 4 的二維數(shù)組來表示。由于矩陣乘法不滿足乘法交換律璃岳,用數(shù)組表示 Matrix 又分為兩種形式:行主序和列主序年缎,它們在本質(zhì)上是等價的悔捶,只不過是一個是右乘(行主序,矩陣放右邊)和一個是左乘(列主序单芜,矩陣放左邊)蜕该。OpenGL 使用列主序矩陣,即列矩陣洲鸠,因此我們總是倒過來算的(左乘矩陣堂淡,變換效果是按從右向左的順序進行): 投影矩陣 × 視圖矩陣 × 模型矩陣 × 3D位置。

4× 4列矩陣的數(shù)組表示:數(shù)字表示數(shù)組下標對應的行列位置:

那么

平移矩陣可表示為:

平移矩陣 × 列矩陣(a, b, c, 1) = 列矩陣(a + x, b + y, c + z, 1)扒腕。

縮放矩陣可表示為:

縮放矩陣 × 列矩陣(a, b, c, 1) = 列矩陣(a × sx, b × sy, c × sz, 1)绢淀。

繞 X 軸旋轉(zhuǎn)的旋轉(zhuǎn)矩陣可表示為:

繞 X 軸旋轉(zhuǎn)的旋轉(zhuǎn)矩陣 × 列矩陣(a, b, c, 1) = 列矩陣(a, b × cos(θ) - c × sin(θ), b × -sin(θ) + c × cos(θ), 1)。

繞 Y 軸旋轉(zhuǎn)的旋轉(zhuǎn)矩陣可表示為:

繞 Y 軸旋轉(zhuǎn)的旋轉(zhuǎn)矩陣 × 列矩陣(a, b, c, 1) = 列矩陣(a × cos(θ) - c × sin(θ), b , a × -sin(θ) + c × cos(θ), 1)瘾腰。

繞 Z 軸旋轉(zhuǎn)的旋轉(zhuǎn)矩陣可表示為:

繞 Z 軸旋轉(zhuǎn)的旋轉(zhuǎn)矩陣 × 列矩陣(a, b, c, 1) = 列矩陣(a × cos(θ) - b × sin(θ), a × -sin(θ) + b × cos(θ), c, 1)皆的。

三,OpenGL 中的實現(xiàn)

OpenGL 使用右手規(guī)則進行旋轉(zhuǎn)蹋盆,因此逆時針方向的選擇是正角度的费薄,而順時針方向的旋轉(zhuǎn)是負角度的。還記得中學學物理時候的右手規(guī)則么栖雾?忘記了的話楞抡,看下圖:

注意:

前面說到矩陣乘法不滿足乘法交換律,因此你對一個3D坐標先進行旋轉(zhuǎn)析藕,然后進行平移(平移矩陣 × 旋轉(zhuǎn)矩陣 × 3D坐標)拌倍;與先進行平移,然后進行旋轉(zhuǎn)(旋轉(zhuǎn)矩陣 × 平移矩陣 × 3D坐標)得到的效果是大為迥異的噪径。如下圖所示:

在第一種情況下,我們通常稱旋轉(zhuǎn)是在 local space 中進行数初,因為它是繞著物體自己的中心點進行的找爱,而在后一種情況下的旋轉(zhuǎn)通常稱為是在 world space 中進行的。我們知道點是可以在坐標空間之間相互轉(zhuǎn)換的泡孩,這是一個很重要的概念车摄。OpenGL 中物體最初是在本地坐標空間中,然后轉(zhuǎn)換到世界坐標空間仑鸥,再到 camera 視圖空間吮播,再到投影空間,這一系列轉(zhuǎn)換都是靠 matrix 計算來實現(xiàn)眼俊。

上面的這個過程在 OpenGL 及 OpenGL ES 1.0 中意狠,對應的代碼類似于:

   glViewport (0, 0, (GLsizei) w, (GLsizei) h);    a)
   glMatrixMode (GL_PROJECTION);           b)
   glLoadIdentity ();
   glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);    c)
   glMatrixMode (GL_MODELVIEW);             d)

   glClear (GL_COLOR_BUFFER_BIT);
   glColor3f (1.0, 1.0, 1.0);
   glLoadIdentity ();             /* clear the matrix */
   /* viewing transformation  */
   gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);    e)
   glScalef (1.0, 2.0, 1.0);      /* modeling transformation */  f)
   glutWireCube (1.0);                          g)
   glFlush ();

說明:

a) 是用于viewport(視口)變換,viewport 變換發(fā)生在投影到2D 投影平面之后疮胖,該變換是將投影之后歸一化的點映射到屏幕上一塊區(qū)域內(nèi)的坐標环戈。視口變換的目的是指定投影之后圖像在屏幕上顯示的區(qū)域闷板。如下示意圖所示:

image

視口變換 glViewport(x, y, width, height); x,y 是投影平面描繪在屏幕或窗口上的起始位置(注意屏幕坐標以左上方為原點),width和height是以像素為單位院塞,指投影平面在屏幕上描繪的區(qū)域大小遮晚。如果投影平面的寬高比,與width/height比不相同(如上面的右圖)拦止,那么描繪的場景就會扭曲县遣。

從裁剪到屏幕的整個過程如下圖所示,w 就是前面提到的齊次坐標那一維汹族,從 Clip Space 到 Normalized Device Space 就是投影規(guī)范化的過程萧求,從 Normalized Device Space 到 Window Space 就是 viewport 變換過程。

投影鞠抑,視口變換過程

該轉(zhuǎn)換內(nèi)部計算公式為:

image

(xw, yw)是屏幕坐標饭聚,(x, y, width, height)是傳入的參數(shù),(xnd, ynd)是投影之后經(jīng)歸一化之后的點(上圖中 Normalized Device Space 空間的點)搁拙。因此 viewport 變換就是將投影之后歸一化的點轉(zhuǎn)換為真正可用于在屏幕上進行渲染的屏幕坐標秒梳;

b) 是說明下面的 matrix 是用于投影變換的,在本例中箕速,是通過語句 c) glFrustum 來設(shè)置透視投影變換的酪碘。投影變換有兩種:正交投影和透視投影,后面會有詳細介紹盐茎;

d) 是說明下面的 matrix 是用于模型視圖變換兴垦,注意,OpenGL 和 OpenGL ES 都將模型變換與視圖變換結(jié)合在一起字柠,而不是分開為兩個探越,這是因為模型變換等價于視圖變換的逆變換。視圖變換是將物體轉(zhuǎn)換到觀察者(一般稱之為 camera)的視線空間中窑业。你可以想象一下钦幔,照相時,你可以:A)照相機不懂常柄,旋轉(zhuǎn)自己的頭找個側(cè)面像鲤氢,也可以B)自己不動,照相機旋轉(zhuǎn)一定的角度來達到同樣的效果西潘。下面的兩幅圖分別描述了情形A)和情形B):

情形A):旋轉(zhuǎn)物體卷玉,相機不動

情形A):旋轉(zhuǎn)物體,相機不動

情形B):旋轉(zhuǎn)相機喷市,物體不動

情形B):旋轉(zhuǎn)相機相种,物體不動

在 OpenGL 中,我們在設(shè)置場景(scene)的時候通常是采取情形B)的做法东抹,因此在語句 e) 處蚂子,我們設(shè)置相機的位置和朝向沃测,來設(shè)定視圖變換,之后的語句 f) glScale 是設(shè)定在模型變換的食茎,最后語句 g) 在本地空間描繪物體蒂破。

注意

寫 OpenGL 代碼時從前到后的順序依次是:設(shè)定 viewport(視口變換),設(shè)定投影變換别渔,設(shè)定視圖變換附迷,設(shè)定模型變換,在本地坐標空間描繪物體哎媚。而在前面為了便于理解做介紹時喇伯,說的順序是OpenGL 中物體最初是在本地坐標空間中,然后轉(zhuǎn)換到世界坐標空間拨与,再到 camera 視圖空間稻据,再到投影空間。由于模型變換包括了本地空間變換到世界坐標空間买喧,所以我們理解3D 變換是一個順序捻悯,而真正寫代碼時則是以相反的順序進行的,如果從左乘矩陣這點上去理解就很容易明白為什么會是反序的淤毛。

有了上面 3D 變換的整體概念今缚,下面來詳細說說投影變換與視圖變換。

四低淡,投影變換

投影變換的目的是確定 3D 空間的物體如何投影到 2D 平面上姓言,從而形成2D圖像,這些 2D 圖像再經(jīng)視口變換就被渲染到屏幕上蔗蹋。前面提到投影變換有兩種:正交投影和透視投影何荚。透視投影用的比較廣泛,它與真實世界更相近:近處的物體看起來要比遠處的物體大猪杭;而正交投影沒有這個效果兽泣,正交投影通常用于CAD或建筑設(shè)計。下面是正交投影與透視投影效果示意圖:

| 正交投影 | 透視投影 |

|
正交投影

|
image

|

透視投影可以通過兩種方式來表述胁孙,OpenGL 及 OpenGL ES 1.0 提供其中一種: glFrustum,而 glut 輔助庫提供了另外一種:gluPerspective称鳞。它們本質(zhì)上是相同的涮较,只不過是不同的表述而已:

視錐體/視景體

image

glFrustum(left, right, bottom, top, zNear, zFar);

left,right冈止, bootom狂票,top 定義了 near 裁剪面大小,而 zNear 和 zFar 定義了從 Camera/Viewer 到遠近兩個裁剪面的距離(注意這兩個距離都是正值)熙暴。由這六個參數(shù)可以定義出六個裁剪面構(gòu)成的錐體闺属,這個錐體通常被稱之為視錐體或視景體慌盯。只有在這個錐體內(nèi)的物體才是可以見的,不在這個錐體內(nèi)的物體就相當于不再視線范圍內(nèi)掂器,因而會被裁減掉亚皂,OpenGL 不會這些物體進行渲染。

由于 OpenGL ES 2.0 不提供此函數(shù)国瓮,因此我們需要自己實現(xiàn)該函數(shù)灭必。其計算公式如下:

假設(shè):l = left, r = right, b = bottom, t = top, n = zNear, f = zFar,有

image

透視圖

image

gluPerspective(fovy, aspect, zNear, zFar);

fovy 定義了 camera 在 y 方向上的視線角度(介于 0 ~ 180 之間)乃摹,aspect 定義了近裁剪面的寬高比 aspect = w/h禁漓,而 zNear 和 zFar 定義了從 Camera/Viewer 到遠近兩個裁剪面的距離(注意這兩個距離都是正值)。這四個參數(shù)同樣也定義了一個視錐體孵睬。

在 OpenGL ES 2.0 中播歼,我們也需要自己實現(xiàn)該函數(shù)。我們可以通過三角公式 tan(fovy/2) = (h / 2)/zNear 計算出 h 掰读,然后再根據(jù) w = h * aspect 計算出 w秘狞,這樣就可以得到 left, right, top, bottom, zNear, zFar 六個參數(shù),代入在介紹視錐體時提到的公式即可磷支。

正交投影在 OpenGL 及 OpenGL ES 1.0 中是由 glOrtho 來提供的谒撼,我們可以把正交投影看成是透視投影的特殊形式:即近裁剪面與遠裁剪面除了Z 位置外完全相同,因此物體始終保持一致的大小雾狈,即便是在遠處看上去也不會變小廓潜。

image

glOrtho(left, right, bottom, top, zNear, zFar);

left,right善榛, bootom辩蛋,top 定義了 near 裁剪面大小,而 zNear 和 zFar 定義了從 Camera/Viewer 到遠近兩個裁剪面的距離(注意這兩個距離都是正值)移盆。

假設(shè):xmax = right, xmin = left, ymax = top, ymin = bottom, zmax = far, zmin = near悼院,正交投影的計算可分為兩步:首先平移到視錐體的中心,然后縮放咒循。

平移矩陣:(圖中的2min 應為 zmin)

image

縮放矩陣:

image

正交投影矩陣 R = S × T:

image

五据途,視圖變換

視圖變換的目的是為了讓我們能觀察到某個角度的場景(從觀察者的角度來說)或者說是為了將物體從世界坐標轉(zhuǎn)換到相機視線所在視圖空間中來(從3D物體角度來說)。這可以通過設(shè)定觀察者的位置和朝向來實現(xiàn)的或?qū)ξ矬w進行3D變換來實現(xiàn)叙甸,通常前面一種方式來實現(xiàn)(即設(shè)定觀察者的位置與朝向)颖医。如下圖所示,xyz坐標軸表示的是世界坐標裆蒸,藍白色區(qū)域為視圖空間熔萧,視圖變換就是要將長方體從世界空間中轉(zhuǎn)換到視圖空間的坐標體系中去,然后再投影規(guī)范化,然后再經(jīng) viewport 轉(zhuǎn)換映射到屏幕上渲染出來佛致。

視圖變換

在 OpenGL 中贮缕,我們可以通過工具庫提供的 gluLookAt 這個函數(shù)來實現(xiàn)此功能。該函數(shù)的原型為:

gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz);

eye 表示 camera/viewer 的位置俺榆, center 表示相機或眼睛的焦點(它與 eye 共同來決定 eye 的朝向)感昼,而 up 表示 eye 的正上方向,注意 up 只表示方向肋演,與大小無關(guān)抑诸。通過調(diào)用此函數(shù),就能夠設(shè)定觀察的場景爹殊,在這個場景中的物體就會被 OpenGL 處理蜕乡。在 OpenGL 中,eye 的默認位置是在原點梗夸,指向 Z 軸的負方向(屏幕往里)层玲,up 方向為 Y 軸的正方向。在接下來的教程 04 中反症,使用的就是這個默認設(shè)置辛块。

OpenGL ES 2.0 也沒有提供該函數(shù),glulookat 的內(nèi)部實現(xiàn)其實就是先旋轉(zhuǎn)到與觀察者視線相同的方向铅碍,然后再平移到觀察者所在的位置润绵。其實現(xiàn)偽碼如下:


Matrix4 GetLookAtMatrix(Vector3 eye, Vector3 at, Vector3 up){

Vector3 forward, side;

forward = at - eye;

normalize(forward);

side = cross(forward, up);

normalize(side);

up = cross(side, forward);

Matrix4 res = Matrix4(

side.x, up.x, -forward.x, 0,

side.y, up.y, -forward.y, 0,

side.z, up.z, -forward.z, 0,

0, 0, 0, 1);

translate(res, Vector3(0 - eye));

return res;

}

上面代碼中的 cross 是叉積,normalize 是規(guī)范化胞谈,Matrix4 是列主序尘盼,translate 是平移。

六烦绳,后記

3D 變換是對初學者來說是比較困難的卿捎,我盡量寫得明白點,但效果如何就不得而知了径密。寫這一篇花了我不少時間午阵,但對四元數(shù)和萬向節(jié)鎖也只是提及而已,未詳細介紹享扔,以后再單獨介紹吧底桂。Nate Robin 寫了一個3D 變換的可視化教程工具,對于理解投影惧眠,視圖戚啥,模型變換非常有幫助,強烈建議下載運行該程序锉试,并調(diào)整相關(guān)參數(shù)看看效果。下面?zhèn)鲝埥貓D以誘惑你去下載:點此進入下載頁面(Windows 和 Mac 版本都有)

image

七,引用

1呆盖,《OpenGL 編程指南

2拖云,《3D數(shù)學基礎(chǔ):圖形與游戲開發(fā)

3,http://cse.csusb.edu/tong/courses/cs420/notes/viewing2.php

4应又,http://www.mesa3d.org/

5宙项,http://db-in.com/blog/2011/04/cameras-on-opengl-es-2-x/

分類: 3D技術(shù),Cocoa開發(fā)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市株扛,隨后出現(xiàn)的幾起案子尤筐,更是在濱河造成了極大的恐慌,老刑警劉巖洞就,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盆繁,死亡現(xiàn)場離奇詭異,居然都是意外死亡旬蟋,警方通過查閱死者的電腦和手機油昂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倾贰,“玉大人冕碟,你說我怎么就攤上這事〈艺悖” “怎么了安寺?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長首尼。 經(jīng)常有香客問我挑庶,道長,這世上最難降的妖魔是什么饰恕? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任挠羔,我火速辦了婚禮,結(jié)果婚禮上埋嵌,老公的妹妹穿的比我還像新娘破加。我一直安慰自己,他們只是感情好雹嗦,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布范舀。 她就那樣靜靜地躺著,像睡著了一般了罪。 火紅的嫁衣襯著肌膚如雪锭环。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天泊藕,我揣著相機與錄音辅辩,去河邊找鬼。 笑死,一個胖子當著我的面吹牛玫锋,可吹牛的內(nèi)容都是我干的蛾茉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼撩鹿,長吁一口氣:“原來是場噩夢啊……” “哼谦炬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起节沦,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤键思,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后甫贯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吼鳞,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年获搏,在試婚紗的時候發(fā)現(xiàn)自己被綠了赖条。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡常熙,死狀恐怖肃续,靈堂內(nèi)的尸體忽然破棺而出底哥,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布毁葱,位于F島的核電站奕坟,受9級特大地震影響距潘,放射性物質(zhì)發(fā)生泄漏甜熔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一聋袋、第九天 我趴在偏房一處隱蔽的房頂上張望队伟。 院中可真熱鬧,春花似錦幽勒、人聲如沸嗜侮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锈颗。三九已至,卻和暖如春咪惠,著一層夾襖步出監(jiān)牢的瞬間击吱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工遥昧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留覆醇,地道東北人朵纷。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像永脓,于是被迫代替她去往敵國和親柴罐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

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