【转载】3D中的OBJ文件格式详解

       常见到的*.obj文件有两种:第一种是基于COFF(Common Object File Format)格式的OBJ文件(也称目标文件),这种格式用于编译应用程序;第二种是Alias|Wavefront公司推出的OBJ模型文件。本文对第二种obj模型文件进行分析。


3D文件格式,常见的有几种 "*.3ds","*.max","*.lw","*.mb","*.dxf","*.obj"。但是,OBJ文件的具体特征,却很少有人能给出较为圆满的描述。很多人认识OBJ文件是从使用Poser开始的,Poser是一款人体建模软件,要把Poser生成的人体导出到其它3D软件中进行再加工,就用到了OBJ文件。OBJ文件是一种标准的3D模型文件格式,很适合用于3D软件模型之间的互导。比如在3dsMax或LightWave中建了一个模型,想把它调到Maya里面渲染或动画,导出OBJ文件就是一种很好的选择。目前几乎所有知名的3D软件都支持OBJ文件的读写,不过很多软件需要通过插件才能做到这一点。

另外,作为一种优秀的文件格式,很多游戏引擎也都支持OBJ文件的读取。 3D软件模型之间的互导是一件很常见的事情,不幸的是,目前的3D软件模型导出功能都不那么完美,经常会出现缺面少线的情况,有时还会遇到导出的模型根本打不开的情况。

OBJ文件是一种文本文件格式,比起二进制文件为主、连每个块的用途也得试探来试探去的3DS,文本文件为主的OBJ对我们更友好。与3DS文件的树状[块结构]不同,OBJ文件只是很单纯的字典状结构,没有块ID来表征名字而是简单地用易懂的表意字符来表示。总之看上去是赏心悦目的样子,而苦处也就只有实际写导入代码的时候才知道了- -。OBJ文件优化了存储但劣化了读写。

如果Maya自身的模型出错,也可以先转成OBJ格式,修改之后再导回Maya。

OBJ文件 -- 概念

OBJ文件是Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种文件格式,这种格式同样也以通过Maya读写。

OBJ文件是一种文本文件,可以直接用写字板打开进行查看和编辑修改。

另外,有一种与此相关二进制文件格式(使用".MOD"后缀),二进制格式作为专利未公开,因此在这里我们不作讨论。

OBJ最近的有文档的版本是v3.0,代替以前的v2.11版本。

OBJ3.0格式支持多边形(Polygon),直线(Lines),表面(Surfaces),和自由形态曲线(Free-form Curves)。

直线和多角形通过它们的点来描述,曲线和表面则根据于它们的控制点和依附于曲线类型的额外信息来定义。这些信息支持规则和不规则的曲线,包括那些基于贝塞尔(Bezier)曲线,B样条(B-spline),基数(Cardinal/Catmull-Rom样条),和泰勒方程(Taylor equations)的曲线。

1、OBJ文件 -- 特点

(1)OBJ是一种3D模型文件,因此不包含动画、材质特性、贴图路径、动力学、粒子等信息。

(2)OBJ文件主要支持多边形(Polygons)模型。

虽然OBJ文件也支持曲线(Curves)、表面(Surfaces)、点组材质(Point Group Materials),但Maya导出的OBJ文件并不包括这些信息。

(3)OBJ文件支持三个点以上的面,这一点很有用。

很多其它的模型文件格式只支持三个点的面,所以我们导入Maya的模型经常被三角化了,这对于我们对模型的再加工甚为不利。

(4)OBJ文件支持法线和贴图坐标。

在其它软件中调整好贴图后,贴图坐标信息可以存入OBJ文件中,这样文件导入Maya后只需指定一下贴图文件路径就行了,不需要再调整贴图坐标了。

2、OBJ文件 -- 基本结构

OBJ文件不需要任何种文件头(File Header),尽管经常使用几行文件信息的注释作为文件的开头。

OBJ文件由一行行文本组成,注释行以一个“井”号(#)为开头,空格和空行可以随意加到文件中以增加文件的可读性。有字的行都由一两个标记字母也就是关键字(Keyword)开头,关键字可以说明这一行是什么样的数据。多行可以逻辑地连接在一起表示一行,方法是在每一行最后添加一个连接符(\)。

注意连接符(\)后面不能出现空格或tab格,否则将导致文件出错。

下列关键字可以在OBJ文件使用。

在这个列表中, 关键字根据数据类型排列,每个关键字有一段简短描述。

顶点数据(Vertex data):

v 几何体顶点 (Geometric vertices)

vt 贴图坐标点 (Texture vertices)

vn 顶点法线 (Vertex normals)

vp 参数空格顶点 (Parameter space vertices)

自由形态曲线(Free-form curve)/表面属性(surface attributes):

deg 度 (Degree)

bmat 基础矩阵 (Basis matrix)

step 步尺寸 (Step size)

cstype 曲线或表面类型 (Curve or surface type)

元素(Elements):

p 点 (Point)

l 线 (Line)

f 面 (Face)

curv 曲线 (Curve)

curv2 2D曲线 (2D curve)

surf 表面 (Surface)

自由形态曲线(Free-form curve)/表面主体陈述(surface body statements):

parm 参数值 (Parameter values )

trim 外部修剪循环 (Outer trimming loop)

hole 内部整修循环 (Inner trimming loop)

scrv 特殊曲线 (Special curve)

sp 特殊的点 (Special point)

end 结束陈述 (End statement)

自由形态表面之间的连接(Connectivity between free-form surfaces):

con 连接 (Connect)

成组(Grouping):

g 组名称 (Group name)

s 光滑组 (Smoothing group)

mg 合并组 (Merging group)

o 对象名称 (Object name)

显示(Display)/渲染属性(render attributes):

bevel 导角插值 (Bevel interpolation)

c_interp 颜色插值 (Color interpolation)

d_interp 溶解插值 (Dissolve interpolation)

lod 细节层次 (Level of detail)

usemtl 材质名称 (Material name)

mtllib 材质库 (Material library)

shadow_obj 投射阴影 (Shadow casting)

trace_obj 光线跟踪 (Ray tracing)

ctech 曲线近似技术 (Curve approximation technique)

stech 表面近似技术 (Surface approximation technique)

3、OBJ文件 -- 实例:

下面通过实例来具体讲解。

OBJ文件记录一个四边形的代码:

v -0.58 0.84 0

v 2.68 1.17 0

v 2.84 -2.03 0

v -1.92 -2.89 0

f 1 2 3 4

让我们来创建一个OBJ文件,不过这一回我们不用3D软件,而是用写字板来创建。

打开写字板,把上面的5行代码写上去,可以适当加一点注释。

保存文件为文本格式,文件名为"myObj.obj",。

注意:代码最后一定要按一下回车把光标切换到下一行,就是说加一个换行符(\n)。否则会看到如下错误信息:

// Error: line 1: OBJ file line 5: index out of range. //

// Error: line 1: Error reading file. //

在Maya中导入"myObj.obj"文件,看见了吧,导入了一个四边形。这个四边形的形状是完全由前面的那5行代码决定的。

下面我们来分析一个这些代码。

v -0.58 0.84 0

画一个四边形需要四个顶点,这是第一个顶点,"v"表示顶点(vertex),"-0.58"为这个顶点的X轴坐标值,"-0.84"为Y轴坐标值,"0"为Z轴坐标值。这是第一个顶点,它的索引号是1。索引号是画面时要用到的。

v 2.68 1.17 0

v 2.84 -2.03 0

v -1.92 -2.89 0

这分别是第二、三、四个顶点,它们的索引号分别是2,3,4。

现在开始画面,"f"表示面(face),1,2,3,4是前面那四个顶点的索引号。请注意画这个面连接点的顺序,是从第一个点出发,依次连接第二、三、四个点。如果连接的顺序不同所生成的面也会截然不同,例如"f 1 2 4 3"会产生一个交迭的面,如图。 面的连接点是按顺时针排列或逆时针排列,将决定面的法线方向(面的反正)。 例如:"f 1 2 3 4"面的法线向外,"f 4 3 2 1"面的法线向里。 面的连接点顺序错误,是导致导入模型产生碎面的一个重要原因。

一个面不能出现两个以上相同的顶点,这也是检查OBJ文件出错的一个要点。

例如:"f 1 2 3 4 3",有两个相同的顶点,索引号是3。一个面出现两个相同顶点,可能造成程序的内存分配错误。

下面来研究一下Maya导出的OBJ文件。

在Maya中创建一个多边形立方体,选中这个立方体,选择菜单"File -> Export Selection..."导出格式为OBJ,文件名为"cube.obj",如果没有此格式,请在Plug-in Manager中载入"objExport.mll"。 用写字板打开"cube.obj",可以看到如下代码:

# The units used in this file are centimeters.

g default

v -0.500000 -0.500000 0.500000

v 0.500000 -0.500000 0.500000

v -0.500000 0.500000 0.500000

v 0.500000 0.500000 0.500000

v -0.500000 0.500000 -0.500000

v 0.500000 0.500000 -0.500000

v -0.500000 -0.500000 -0.500000

v 0.500000 -0.500000 -0.500000

vt 0.000000 0.000000

vt 1.000000 0.000000

vt 0.000000 1.000000

vt 1.000000 1.000000

vt 0.000000 2.000000

vt 1.000000 2.000000

vt 0.000000 3.000000

vt 1.000000 3.000000

vt 0.000000 4.000000

vt 1.000000 4.000000

vt 2.000000 0.000000

vt 2.000000 1.000000

vt -1.000000 0.000000

vt -1.000000 1.000000

vn 0.000000 0.000000 1.000000

vn 0.000000 0.000000 1.000000

vn 0.000000 0.000000 1.000000

vn 0.000000 0.000000 1.000000

vn 0.000000 1.000000 0.000000

vn 0.000000 1.000000 0.000000

vn 0.000000 1.000000 0.000000

vn 0.000000 1.000000 0.000000

vn 0.000000 0.000000 -1.000000

vn 0.000000 0.000000 -1.000000

vn 0.000000 0.000000 -1.000000

vn 0.000000 0.000000 -1.000000

vn 0.000000 -1.000000 0.000000

vn 0.000000 -1.000000 0.000000

vn 0.000000 -1.000000 0.000000

vn 0.000000 -1.000000 0.000000

vn 1.000000 0.000000 0.000000

vn 1.000000 0.000000 0.000000

vn 1.000000 0.000000 0.000000

vn 1.000000 0.000000 0.000000

vn -1.000000 0.000000 0.000000

vn -1.000000 0.000000 0.000000

vn -1.000000 0.000000 0.000000

vn -1.000000 0.000000 0.000000

s off

g pCube1

usemtl initialShadingGroup

f 1/1/1 2/2/2 4/4/3 3/3/4

f 3/3/5 4/4/6 6/6/7 5/5/8

f 5/5/9 6/6/10 8/8/11 7/7/12

f 7/7/13 8/8/14 2/10/15 1/9/16

f 2/2/17 8/11/18 6/12/19 4/4/20

f 7/13/21 1/1/22 3/3/23 5/14/24

这个文件看起来稍复杂一些,用到了许多关键词,你可以对照前面的列表查看一下每个关键词的意思。

我来解释一下。

"vt 1.000000 0.000000"这句"vt"代表点的贴图坐标。

"vn 0.000000 0.000000 -1.000000"这句"vn"代表点的法线。

"s off"表示关闭光滑组。

"usemtl initialShadingGroup"表示使用的材质。

"f 7/13/21"这时在面的数据中多了贴图坐标uv点和法线的索引号,索引号分别用左斜线(/)隔开。

格式:"f 顶点索引/uv点索引/法线索引"。

"g pCube1"表示组,这里的成组与Maya中的成组不一样,这里的成组是指把"g pCube1"后出现的面都结合到一起,组成一个整的多边形几何体。

把"cube.obj"文件修改一下就知道成组的意思了。把"s off"这句后面的代码替换成以下代码:

usemtl initialShadingGroup

g pCube_Face1

f 1/1/1 2/2/2 4/4/3 3/3/4

g pCube_Face2

f 3/3/5 4/4/6 6/6/7 5/5/8

g pCube_Face3

f 5/5/9 6/6/10 8/8/11 7/7/12

g pCube_Face4

f 7/7/13 8/8/14 2/10/15 1/9/16

g pCube_Face5

f 2/2/17 8/11/18 6/12/19 4/4/20

g pCube_Face6

f 7/13/21 1/1/22 3/3/23 5/14/24

导入Maya后可以看到,立方体的每个面是分离的,每个面的名称分别是"pCube_Face(1~6)",可见组的名称其实就是单独几何体的名称。

可不可以用中文命名几何体(组)呢?试试就知道了,把前面的代码改成:

usemtl initialShadingGroup

g 立方体面1

f 1/1/1 2/2/2 4/4/3 3/3/4

g 立方体面2

f 3/3/5 4/4/6 6/6/7 5/5/8

g 立方体面3

f 5/5/9 6/6/10 8/8/11 7/7/12

g 立方体面4

f 7/7/13 8/8/14 2/10/15 1/9/16

g 立方体面5

f 2/2/17 8/11/18 6/12/19 4/4/20

g 立方体面6

f 7/13/21 1/1/22 3/3/23 5/14/24

试一下,会发现模型顺利的导入了。虽然物体的名称都变乱码了,可这并不是很严重的事。

不过使用中文名并不总是这么顺利,把"g 立方体面1"这行改为"g 选择"再试试看,这回导入时模型根本无法出现,只会出现如下的错误信息:

// Error: line 1: Your OBJ file contains a line which is too long to be parsed. Please edit your obj file. //

// Error: line 1: Error reading file. //

由此可见,物体命名的不规范也是导致OBJ文件出错的原因之一。

关于Maya的物体命名,英文名是很保险的,标点符号中只有下划线(_)可用,数字不能用放到名称的开头,尽量不要用中、日、韩等双字节文字。

OBJ文件不支持有孔的多边形面。

举个例子说明一下:

选择Maya的创建多边形工具(Polygons -> Create Polyon Tool),在视图中画一个四边形,不要按回车,按Ctrl在四边形中间点一下,可以继续在四边形中挖一个洞。把这个有孔的多边形存成OBJ格式,在导入Maya时,会发现多边形少了一块。如果你把这也看成错误,现在至少你已经知道错误的原因了,就是OBJ文件不支持有孔的多边形面。

OBJ文件 -- 实际问题:

现在来讨论一点比较实际的问题吧,就是一旦你遇到了一个出错的OBJ文件,倒底该怎么办?

当你打开OBJ文件后,往往会看到有几万行的代码,你恐怕还没本事情一眼看出错误所在行,除非程序的错误信息中已经告诉你错误行。如果你不知道错误在哪里,可以用排除法,弄清楚肯定正确的代码范围,通过缩减错误代码范围定位错误。例如,你先新建一个空的OBJ文件,把有错的OBJ文件代码粘贴一半过来,然后把这个只有一半代码的新OBJ文件导入Maya。如果这时没有错误信息,说明错误行是在另一半代码中,可以从另一半代码中再粘贴一部分代码试试看;如果这时出现错误,说明错误行就在粘贴的代码中,可以把粘贴过来的代码删去一部分再试试看。就这样,逐步缩减范围直到找到错误行为止。

这种方法虽然很麻烦,不过颇为有效。如果你不会编程,又遇到非常紧急的情况,这种方法还是值得一试的。

OBJ文件 -- 细节:

掌握了这么多差不多也够用了,不过由于网上详细讲解OBJ文件的中文文档很少,我还是再讲一些例子,给大家提供多一点的信息吧。

简单的OBJ格式写法。

# Simple Wavefront file

v 0.0 0.0 0.0

v 0.0 1.0 0.0

v 1.0 0.0 0.0

f 1 2 3

面可以使用负值索引,有时用负值索引描述面更为简便。

"f -4 -3 -2 -1"这句索引值"-3"表示从"f"这行往上数第3个顶点,就是"v -0.500000 0.000000 -0.800000",其它的索引值以此类推。 因此与这一行等效的正值索引写法为:"f 1 2 3 4"

v -0.500000 0.000000 0.400000

v -0.500000 0.000000 -0.800000

v -0.500000 1.000000 -0.800000

v -0.500000 1.000000 0.400000

f -4 -3 -2 -1

OBJ文件不包含面的颜色定义信息,不过可以引用材质库,材质库信息储存在一个后缀是".mtl"的独立文件中。关键字"mtllib"即材质库的意思。

材质库中包含材质的漫射(diffuse),环境(ambient),光泽(specular)的RGB(红绿蓝)的定义值,以及反射(specularity),折射(refraction),透明度(transparency)等其它特征。

"usemtl"指定了材质之后,以后的面都是使用这一材质,直到遇到下一个"usemtl"来指定新的材质。

下面的例子说明了指定材质的方法。

Cube with Materials:

# This cube has a different material

# applied to each of its faces.

mtllib master.mtl

v 0.000000 2.000000 2.000000

v 0.000000 0.000000 2.000000

v 2.000000 0.000000 2.000000

v 2.000000 2.000000 2.000000

v 0.000000 2.000000 0.000000

v 0.000000 0.000000 0.000000

v 2.000000 0.000000 0.000000

v 2.000000 2.000000 0.000000

# 8 vertices

g front

usemtl red

f 1 2 3 4

g back

usemtl blue

f 8 7 6 5

g right

usemtl green

f 4 3 7 8

g top

usemtl gold

f 5 1 4 8

g left

usemtl orange

f 5 6 2 1

g bottom

usemtl purple

f 2 6 7 3

# 6 elements

贝塞尔片面(Bezier Patch):

Maya不能导出OBJ格式的贝塞尔片面,却能够导入它。导入的贝塞尔片面自动转换为Nurbs表面。

# 3.0 Bezier patch

v -5.000000 -5.000000 0.000000

v -5.000000 -1.666667 0.000000

v -5.000000 1.666667 0.000000

v -5.000000 5.000000 0.000000

v -1.666667 -5.000000 0.000000

v -1.666667 -1.666667 0.000000

v -1.666667 1.666667 0.000000

v -1.666667 5.000000 0.000000

v 1.666667 -5.000000 0.000000

v 1.666667 -1.666667 0.000000

v 1.666667 1.666667 0.000000

v 1.666667 5.000000 0.000000

v 5.000000 -5.000000 0.000000

v 5.000000 -1.666667 0.000000

v 5.000000 1.666667 0.000000

v 5.000000 5.000000 0.000000

# 16 vertices

cstype bezier

deg 3 3

# Example of line continuation

surf 0.000000 1.000000 0.000000 1.000000 13 14 \

15 16 9 10 11 12 5 6 7 8 1 2 3 4

parm u 0.000000 1.000000

parm v 0.000000 1.000000

end

# 1 element

基数曲线(Cardinal Curve):

Maya好像不支持OBJ格式的曲线,导入时不会出现错误信息,却也不会出现曲线。

# 3.0 Cardinal curve

v 0.940000 1.340000 0.000000

v -0.670000 0.820000 0.000000

v -0.770000 -0.940000 0.000000

v 1.030000 -1.350000 0.000000

v 3.070000 -1.310000 0.000000

# 6 vertices

cstype cardinal

deg 3

curv 0.000000 3.000000 1 2 3 4 5 6

parm u 0.000000 1.000000 2.000000 3.000000 end

# 1 element

贴图映射(Texture-Mapped):

# A 2 x 2 square mapped with a 1 x 1 square

# texture stretched to fit the square exactly.

mtllib master.mtl

v 0.000000 2.000000 0.000000

v 0.000000 0.000000 0.000000

v 2.000000 0.000000 0.000000

v 2.000000 2.000000 0.000000

vt 0.000000 1.000000 0.000000

vt 0.000000 0.000000 0.000000

vt 1.000000 0.000000 0.000000

vt 1.000000 1.000000 0.000000

# 4 vertices

usemtl wood

# The first number is the point,

# then the slash,

# and the second is the texture point

f 1/1 2/2 3/3 4/4

# 1 element

【后记】

1、为什么要研究OBJ?

学习三维软件,不仅要学习如何制作模型、渲染、动画,还要掌握它能载入哪些文件,导出哪些格式的文件,每种格式的文件有哪些特点等。计算机CG技术就是依照“计算机图形学”理论的各种计算,所以你想深入了解,应该搜索看看相关的文章。否则你就不知道为什么、怎么、如何去应用。这篇关于三维模型格式OBJ,实际研究它的数据结构,结构决定属性。从这篇你应该知道OBJ本身就是文本格式,用记事本可以打开。它包含UV数据,但不包含材质特性及路径等、不包含动画、粒子和动力学信息。同时生成的MTL是材质数据,需要在三维软件中重新定义一下。它支持三点以上成面,就是说它能存储四边面数据。但是文章说,OBJ不能存储有孔的模型文件,我用C4D测试一下,好像这个结论不正确,C4D能够载入有孔的OBJ模型文件。

这个格式的模型文件就是兼容好,可以在各种不同的三维软件使用,这也许是它最大的优点吧?

如果你想导出动画到其他三维软件,还有一个格式是FBX,以后再研究。

2、天气太热了,不愿意写啊。好多文章都躺着草稿箱里,等天气凉爽一些再写吧?只能转载一些有用的文章凑数,但这些文章我也是用“心”的啊。

声明:文章摘自

阳光下的蒲公英

http://www.cnblogs.com/slysky/p/4081307.html

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

推荐阅读更多精彩内容