GAMES101笔记(1)——Transformation

课程链接:GAMES101-现代计算机图形学入门-闫令琪
课程讲师:闫令琪
本系列笔记为本人根据学习该门课程的笔记,仅分享出来供大家交流,希望大家多多支持GAMES相关讲座及课程,如涉及侵权请联系我删除:albertlidesign@gmail.com

二维变换

Scale

图1 Rotation

在图形学中Scale变换是非常简单的,如果你想把一个物体Scale至它的s倍,那么只需要将这个物体上所有点的分量都乘以s,写成矩阵形式就是

\left[\begin{matrix}x'\\y'\end{matrix}\right] = \left[\begin{matrix}s & 0\\ 0& s\end{matrix}\right]\left[\begin{matrix}x\\y\end{matrix}\right]

Reflection

图2 Reflection

将一个物体的镜像也很简单,在二维中,这个物体想沿着哪个轴镜像就将另一个轴的分量乘以即可,即

\left[\begin{matrix}x'\\y'\end{matrix}\right] = \left[\begin{matrix}-1 & 0\\ 0& 1\end{matrix}\right]\left[\begin{matrix}x\\y\end{matrix}\right]

Shear

图3 Shear

在做切变的时候要注意,假设xy平面上左下角过原点的边长为1的正方形上方的两个点沿x轴平移a个单位,那么我们会发现:

(1) y=0上的点没有移动

(2) y=1上的点移动了a个单位,如点(0,1)移动至了(a,1)

(3) 垂直方向没有移动

(4) 如果假设y=0.5上有一个点(0,0.5),那么它应该移动至(\frac{a}{2},0.5)

那么也就是说,实际上所有的y都没变,而所有的x都变为x+ay

因此用矩阵表示如下:

\left[\begin{matrix}x'\\y'\end{matrix}\right] = \left[\begin{matrix}1 & a\\ 0& 1\end{matrix}\right]\left[\begin{matrix}x\\y\end{matrix}\right]

Rotate

图4 Rotate

首先规定,任何时候我们说旋转都是默认绕着原点(0,0)进行旋转(绕其他点旋转可以看作是先将物体移动至原点,进行旋转操作后再移动回去),另外,如果不规定旋转方向,那么我们默认都是逆时针旋转。现有一个物体,假设让它旋转\theta度,那么可以根据三角函数来求出旋转后的对应点的坐标。例如点(1,0)将会旋转至点(cos\theta,sin\theta),点(0,1)将会旋转至点(-sin\theta,cos\theta)

这样我们就能很轻易地写成矩阵形式:

R_\theta = \left[\begin{matrix}cos\theta & -sin\theta\\ sin\theta& cos\theta\end{matrix}\right]

那么如果向顺时针方向旋转该怎么表达呢?顺时针方向旋转其实就是旋转了-\theta,将其代如旋转矩阵得到

R_{-\theta} = \left[\begin{matrix}cos\theta & sin\theta\\ -sin\theta& cos\theta\end{matrix}\right]

这里我们发现刚好R_{-\theta} = R_{\theta}^T,并且,旋转\theta角和旋转-\theta角正好是互逆的操作,因此还有R_{-\theta} = R_\theta^{-1}(逆变换的意义其实就是将矩阵变换的操作反过来,下文会继续提到),因此R_\theta^{-1} = R_\theta^T。这是因为旋转矩阵是一个正交矩阵

求变换矩阵的方法

在变换中,无非就是将点(x,y)\rightarrow(x',y'),表示成矩阵形式就是

\left[\begin{matrix}x'\\y'\end{matrix}\right] = \left[\begin{matrix}a & b\\ c& d\end{matrix}\right]\left[\begin{matrix}x\\y\end{matrix}\right]x' = Mx

我们所做的就是求a,b,c,d,那么既然一个变换矩阵会对这个物体的所有点都起作用,那么也一定对一些特殊点起作用,那么我们就可以利用几个简单的特殊点来进行问题的求解,例如上面旋转矩阵的例子中,有(1,0)\rightarrow(cos\theta,sin\theta)(0,1)\rightarrow(sin\theta,cos\theta)

用矩阵表示就是

\left[\begin{matrix}cos\theta\\sin\theta\end{matrix}\right] = \left[\begin{matrix}a & b\\ c& d\end{matrix}\right]\left[\begin{matrix}1\\0\end{matrix}\right]\\\left[\begin{matrix}-sin\theta\\cos\theta\end{matrix}\right] = \left[\begin{matrix}a & b\\ c& d\end{matrix}\right]\left[\begin{matrix}0\\1\end{matrix}\right]

通过第一个矩阵等式我们直接就能求出ac,再代入第二个矩阵求出bd即可,最终我们就能求得旋转矩阵为

R_\theta = \left[\begin{matrix}cos\theta & -sin\theta\\ sin\theta& cos\theta\end{matrix}\right]

这也启发了我们,如果想求一个变换矩阵,只需要将变换前后的矩阵列出来,再代入特殊点求解即可。

Translation

图5 Translation

平移操作我们可以很简单地将一个点操作前后的坐标写出来,即

x'=x+t_x \\ y'=y+t_y

但是我们会发现,我们不能将其表达成两个矩阵相乘的形式,这个操作的矩阵形式为

\left[\begin{matrix}x'\\y'\end{matrix}\right] = \left[\begin{matrix}a & b\\ c& d\end{matrix}\right]\left[\begin{matrix}x\\y\end{matrix}\right]+\left[\begin{matrix}t_x\\t_y\end{matrix}\right]

这样一来,这里的变换就不是线性变换了,它就只能是一种特殊的变换了。

齐次坐标

在发现这件事之后人们就开始思考,有没有一种方法能将它表达成线性变换?答案是有,这就引入了齐次坐标(Homogeneous Coordinates) 的概念。人们引入了一种新的形式来表示物体的坐标,他们在二维坐标后面又加了一个分量w,规定

Point(2D) = (x,y,1)^T \\Vector(2D) = (x,y,0)^T

这样,平移的变换就可以写成线性变换形式,即

\left[\begin{matrix}x'\\y'\\w'\end{matrix}\right] = \left[\begin{matrix}1 & 0 & t_x\\ 0& 1 & t_y \\ 0 & 0 &1\end{matrix}\right]\left[\begin{matrix}x\\y\\1\end{matrix}\right]=\left[\begin{matrix}x+t_x\\y+t_y\\1\end{matrix}\right]

为什么在二维点的后面增加了1而在二维向量的后面增加了0呢?其实是有意义的。因为我们知道,在空间里,两向量和必为一个新的向量,如果坐标最后是0,那么相加后最后还是0;如果空间中的点,如果一个点减一个点,这样就形成了一个向量(末点-初点),我们发现最后的坐标是1时相减得到0,变成了一个向量,刚好也满足;一个点加一个向量表示为一个点沿着一个方向移动,移动到了一个新的点上,最后得到的还是一个点,最后的分量还是1,也可验证。因此第三个分量的引入,在点上加一个1,在向量上加一个0,保证了这些操作最后的结果是对的。

vector + vector = vector\\ point-point=vector\\point+vector = point\\point+point =??

那么最后一个,两点相加后,最后的分量是2,这是什么意思呢?人们也扩充了它的定义,即齐次坐标\left[\begin{matrix}x\\y\\w\end{matrix}\right]表示的是二维点\left[\begin{matrix}x/w\\y/w\\1\end{matrix}\right],w\not =0。因此两点相加表达的是它们的中点。

在引入齐次坐标之前,我们做平移、旋转等操作可以起个名字叫做仿射变换(Affine Transformation),仿射(Affine map) = linear map + translation,即

\left[\begin{matrix}x'\\y'\end{matrix}\right] = \left[\begin{matrix}a & b\\ c& d\end{matrix}\right]\left[\begin{matrix}x\\y\end{matrix}\right]+\left[\begin{matrix}t_x\\t_y\end{matrix}\right]

使用齐次坐标来表达就可以写成

\left[\begin{matrix}x'\\y'\\1\end{matrix}\right] = \left[\begin{matrix}a & b & t_x\\ c& d & t_y \\ 0 & 0 &1\end{matrix}\right]\left[\begin{matrix}x\\y\\1\end{matrix}\right]

下面利用齐次坐标来重新书写我们前面所学的各种变换

Scale: S(s_x,s_y) = \left[\begin{matrix}s_x & 0 & 0\\ 0& s_y & 0\\0 & 0 &1\end{matrix}\right]

Rotation: R(\alpha) = \left[\begin{matrix}cos\alpha & -sin\alpha & 0\\ sin\alpha& cos\alpha & 0\\0 & 0 &1\end{matrix}\right]

Translation: T(t_x,t_y) = \left[\begin{matrix}1 & 0 & t_x\\ 0& 1 & t_y\\0 & 0 &1\end{matrix}\right]

逆变换和组合变换

逆变换

逆变换的意义其实就是将矩阵变换的操作反过来,例如矩阵M通过旋转矩阵R得到M',那么如果我们已知变换后的矩阵M'想求得M只需要左乘旋转矩阵R的逆R^{-1},即

M' = RM \\ M = R^{-1}M'

组合变换

图6 组合变换

复杂的变换可以通过多个变换组合来得到,其中,变换顺序至关重要,因为矩阵相乘不满足交换律。做一次变换可以理解为左乘一个矩阵,当然我们也可以将多次变换的矩阵的作用效果看作是一个矩阵的作用效果,因为矩阵相乘满足结合律。

那么如图所示,我们想求左侧矩阵变换到最右侧矩阵的效果,需要先旋转再平移,那么我们可以写成

M' = T_{(1,0)}R_{45}M

图7 先将物体移动至原点,做旋转操作,最后再移动回原位置

对于一个起点不在原点的物体做宣传操作该如何做呢?我们可以先把物体移动到原点,做旋转操作后再移动回原来的位置,即

M' = T_{(c)}R_{\alpha}T_{(-c)}M

三维变换

了解了二维变换之后,三维就变得很简单了,首先我们依然引入齐次坐标,得到三维点和三维向量:

Point(3D) = (x,y,z,1)^T \\Vector(3D) = (x,y,z,0)^T

并且一般来说,齐次坐标\left[\begin{matrix}x\\y\\z\\w\end{matrix}\right],w\not =0表示的是三维点\left[\begin{matrix}x/w\\y/w\\z/w\end{matrix}\right]

当然,我们也可以用4×4的矩阵表达仿射变换,即

\left[\begin{matrix}x'\\y'\\z'\\1\end{matrix}\right] = \left[\begin{matrix}a & b & c & t_x\\ d& e &f & t_y \\ g&h&i&t_z\\0& 0 & 0 &1\end{matrix}\right]\left[\begin{matrix}x\\y\\z\\1\end{matrix}\right]

对应的三维中的各种变换表达如下:

Scale: S(s_x,s_y,s_z) = \left[\begin{matrix}s_x & 0 & 0 & 0\\ 0& s_y & 0 & 0\\0 & 0 & s_z & 0 \\ 0 & 0 & 0 & 1\end{matrix}\right]

Translation: T(t_x,t_y,t_z) = \left[\begin{matrix}1 & 0 & 0 & t_x\\ 0& 1 & 0 & t_y\\0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1\end{matrix}\right]

Rotation: R_x(\alpha) = \left[\begin{matrix}1 & 0 & 0 & 0\\ 0& cos\alpha & -sin\alpha & 0\\ 0& sin\alpha& cos\alpha & 0\\0 &0& 0 &1\end{matrix}\right]

Rotation: R_y(\alpha) = \left[\begin{matrix}cos\alpha & 0 & sin\alpha & 0\\ 0& 1 & 0 & 0\\ -sin\alpha & 0 & cos\alpha & 0\\0 &0& 0 &1\end{matrix}\right]

Rotation: R_z(\alpha) = \left[\begin{matrix}cos\alpha & -sin\alpha & 0 & 0\\ sin\alpha& cos\alpha & 0 & 0\\ 0& 0& 1 & 0\\0 &0& 0 &1\end{matrix}\right]

注意,绕y轴旋转时,左下角变为-sin\alpha,右上角变为sin\alpha,这是因为轴的顺序问题造成的,我们说轴的顺序是x\rightarrow y\rightarrow z,有x×y =zy×z=x,但是根据右手定则,得到y = z×x,而不是x×z,因此这里是反的。

对于一般性旋转我们可以将其转化成三个轴的旋转,即

R_{xyz}(\alpha,\beta,\gamma) = R_x(\alpha)R_y(\beta)R_z(\gamma)

如果想绕着任意一个点旋转,我们可以先将物体沿着这个点平移至原点,做旋转操作后再移回该点。
如果想绕任意轴旋转,我们需要Rodrigues' Rotation Formula,给定角度\alpha和轴n,我们有

R(n,\alpha) = cos(\alpha)I +(1-cos(\alpha))nn^T+sin(\alpha)\left[\begin{matrix}0 & -n_z & n_y\\ n_z & 0 & -n_x\\ -n_y & n_x &0\end{matrix}\right]

观测变换 (Viewing Transformation)

现实生活中,拍照片我们需要如下步骤:

(1) 找一个好的场地,集合所有人 (模型变换 Model Transformation)

(2) 找一个好的角度,放置相机 (视图变换 View Transformation)

(3) 拍照 (投影变换 Projection Transformation)

1. 定义相机

  • 相机的位置 \vec e
  • 拍摄方向 (look-at / gaze direction) \hat g
  • 向上方向 (up direction) \hat t (垂直于拍摄方向)

2.视图变换(View Transformation)

在现实生活中,假如在摄影棚里拍照,相同的人,相同的相机,相同的相对摆放位置,不管在哪一个摄影棚,拍出来的效果是一样的。也就是说,如果相机和所有物体(包括前景背景)都一起移动时,拍出来的照片一定是一样的。更抽象地说,当我们移动物体和移动相机没有相对运动时,拍出来的照片是一样的。那么我们可以将相机永远放在原点这个固定的位置上,物体都可以移动,相机永远不动,并且相机的向上方向为Y方向,看向-Z

通过变换将相机放到标准位置上

首先,相机原本在位置\vec e,向\hat g看,并且向上方向为\hat t,现在要把它变成固定在原点,向-Z方向看,并且up方向为Y。那么我们可以先将相机从\vec e移到原点,然后再把观原点察方向\hat g旋转到-Z上,再把向上方向\hat t旋转到Y,写成矩阵表达为M_{view} = R_{view}T_{view}

其中,将相机从\vec e移到原点很容易写出,将向量\vec e的三个分量各减去他们本身即可,为

T_{view} = \left[\begin{matrix}1 & 0 & 0 & -x_e\\ 0& 1 & 0 & -y_e\\0 & 0 & 1 & -z_e \\ 0 & 0 & 0 & 1\end{matrix}\right]

但是如何把观原点察方向\hat g旋转到-Z上,再把向上方向\hat t旋转到Y呢?这件事并不容易做,但是反过来,将X旋转到(\hat g×\hat t),将Y旋转到\hat t,将Z旋转到-\hat g很容易实现,它和我们需要做的操作是一个互逆的操作,因此我们只需要求这一操作的矩阵的逆即可。

R_{view}^{-1} = \left[\begin{matrix}x_{\hat g×\hat t} & x_{\hat t} & x_{-\hat g} & 0\\ y_{\hat g×\hat t}& y_{\hat t} & y_{-\hat g} & 0\\z_{\hat g×\hat t} & z_{\hat t} & z_{-\hat g} & 0 \\ 0 & 0 & 0 & 1\end{matrix}\right]\quad R_{view} = \left[\begin{matrix}x_{\hat g×\hat t} & y_{\hat g×\hat t}& z_{\hat g×\hat t} & 0 \\ x_{\hat t} & y_{\hat t} & z_{\hat t}& 0\\ x_{-\hat g} & y_{-\hat g} & z_{-\hat g} & 0\\ 0 & 0 & 0 & 1\end{matrix}\right]

需要注意的是,为了保证相对结果不变,我们要将场景中的所有物体都做这样的变换。

3. 投影变换(Projection Transformation)

投影包含两种投影方式:正交投影 (Orthographic Projection) 和透视投影 (Perspective Projection)

图8 正交投影与透视投影(1)
图9 正交投影与透视投影(2)

正交投影 (Orthographic Projection)

正交投影很简单,不管物体的远近,我们只需将它“挤”到某个平面上即可。投影到XY平面的操作步骤如下:

  • 先将相机放到标准位置上(原点,看向-Z,向上为Y
  • 移除掉物体的Z坐标
  • 平移、缩放将结果映射到[-1,1]^2 (约定俗称的方法,方便后续计算)
图10 正交投影(1)

上述方法是一个简单的理解方式,但在图形学中,还有更方便的一种操作:

  • 首先定义空间中的立方体[l,r]×[b,t]×[f,n],只需定义立方体的左右在X轴上的值,下上在Y轴上的值和前后在Z轴上的值(由于右手坐标系,远的值小于近),一共6个数
  • 将这个方体映射到标准立方体[-1,1]^3,将立方体的中心移动到原点,在将模型缩放至[-1,1]^3
图11 正交投影(2)

实现方法:首先通过平移,将立方体中心移动至原点,然后再缩放,以长宽高都缩放至2为例,则

M_{ortho} = \left[\begin{matrix}\frac{2}{r-l} & 0 & 0 & 0\\ 0& \frac{2}{t-b} & 0 & 0\\0 & 0 & \frac{2}{n-f} & 0 \\ 0 & 0 & 0 & 1\end{matrix}\right]\left[\begin{matrix}1 & 0 & 0 & -\frac{r+l}{2}\\ 0& 1 & 0 & -\frac{t+b}{2}\\0 & 0 & 1 & -\frac{n+f}{2} \\ 0 & 0 & 0 & 1\end{matrix}\right]

注意:

  • 由于我们定义相机看向-Z,所以近>远,这也是为什么在OpenGL中使用左手系,但左手系意味着X×Y \not =Z

  • 也可以用变换坐标系的方式来理解,道理是一样的,但是不直观、不好理解。

透视投影 (Perspective Projection)

图12 透视投影(1)

透视投影是应用最广泛的投影,满足近大远小的性质,带来的视觉效果是平行线不再平行,相交于一点。

回顾一下我们之前关于齐次坐标的定义:

  • (x,y,z,1),(kx,ky,kz,k\not =0),(xz,yz,z^2,z \not =0)都表示三维空间中的一个相同点(x,y,z),因为z也属于任何一个数k
  • 举个例子:(1,0,0,1)(2,0,0,2)都表示点(1,0,0)

透视投影是从一个点(相机)开始,往外延伸出的一个四棱锥,我们定义近平面n和远平面f,称为Frustum,和正交投影的区别在于远平面f相对更大,这也是透视投影和正交投影的主要区别。

图13 透视投影(2)

因此我们只需要在正交投影之前增加一步,将远平面f先挤压至与近平面n相同的尺寸。也就是说透视投影的过程为两个步骤:先将远平面挤压至近平面的尺寸,再进行正交投影(M_{persp\rightarrow ortho}^{(4×4)})。在这一过程中,我们规定:

  • 近平面n永远不变
  • 远平面f上的点的z值不会变(因为是在平面内挤压,z值不变)
  • 远平面的中心点挤压前后不变
图13 透视投影(3)

从侧面看Frustum会发现远近平面与相机有着相似三角形的关系,从y坐标来看就有y' = \frac{n}{z}y,因此写成矩阵形式就有

\left[\begin{matrix}x\\y\\z\\1\end{matrix}\right] \Rightarrow \left[\begin{matrix}nx/z\\ny/z\\unknown\\1\end{matrix}\right] == \left[\begin{matrix}nx\\ny\\unknown\\z\end{matrix}\right](同乘z后仍表示同一个点)

那么我们从

M_{persp\rightarrow ortho}^{(4×4)} \left[\begin{matrix}x\\y\\z\\1\end{matrix}\right] = \left[\begin{matrix}nx\\ny\\unknown\\z\end{matrix}\right]

可知

M_{persp\rightarrow ortho}^{(4×4)} = \left[\begin{matrix}n& 0 & 0 & 0\\0 & n & 0 & 0\\?& ? & ? & ?\\0& 0 & 1 & 0\end{matrix}\right]

为了求第三行,我们需要另外两个条件:任意在近平面上的点都保持不变,任意在远平面上的点的z坐标保持不变。

(1)在近平面上,点坐标的z值其实是n,代入

M_{persp\rightarrow ortho}^{(4×4)} \left[\begin{matrix}x\\y\\z\\1\end{matrix}\right] = \left[\begin{matrix}nx\\ny\\unknown\\z\end{matrix}\right]

\left[\begin{matrix}x\\y\\n\\1\end{matrix}\right] \Rightarrow \left[\begin{matrix}x\\y\\n\\1\end{matrix}\right]== \left[\begin{matrix}nx\\ny\\n^2\\n\end{matrix}\right]

第三行为n^2,那么我们可以求出M_{persp\rightarrow ortho}^{(4×4)}的第三行的前两项为0,即

\left[\begin{matrix}0& 0&A&B\end{matrix}\right] \left[\begin{matrix}x\\y\\n\\1\end{matrix}\right] = n^2

现在还剩下两个未知数AB,接着我们再使用第二个条件:任意在远平面上的点的z坐标保持不变。

(2)在远平面上,点坐标的z值是f,代入

\left[\begin{matrix}0\\0\\f\\1\end{matrix}\right]\Rightarrow \left[\begin{matrix}0\\0\\f\\1\end{matrix}\right] == \left[\begin{matrix}0\\0\\f^2\\f\end{matrix}\right](同乘f后仍表示同一个点)

根据近平面我们得到的结果,将其展开得

\left[\begin{matrix}0& 0&A&B\end{matrix}\right] \left[\begin{matrix}x\\y\\n\\1\end{matrix}\right] = n^2 \Rightarrow An+B=n^2

根据远平面我们得到的结果,将其展开得

\left[\begin{matrix}0\\0\\f\\1\end{matrix}\right]\Rightarrow \left[\begin{matrix}0\\0\\f\\1\end{matrix}\right] == \left[\begin{matrix}0\\0\\f^2\\f\end{matrix}\right] \Rightarrow Af+B=f^2

于是将两个展开式联立

An+B=n^2\\ Af+B=f^2

A = n+f\\B=-nf

因此

M_{persp\rightarrow ortho}^{(4×4)} = \left[\begin{matrix}n& 0 & 0 & 0\\0 & n & 0 & 0\\ 0 & 0 & n+f & -nf\\0& 0 & 1 & 0\end{matrix}\right]

透视投影的矩阵变换为

M_{persp} = M_{ortho}M_{persp\rightarrow ortho}

之前已经把如何将透视投影转化成正交投影说明白了,透视投影转化成正交投影需要保证近和远两个平面都是不变的,大小上远平面要变成和近平面一样大。在表示立方体上我们需要 左右前后远近(l,r,b,t,f,n) 6个值来表示立方体,既然远和近在正交投影和透视投影中都是一样的,就不用管它了,我们可以将Frustum变成一个长方体,问题在于我们如何定义这个Frustum?

图14

如图所示,我们从摄像机出发看向某一个区域,如果假设看到的就是这个近的平面,那么我们可以定义一个宽度和高度,就好像我们在看一个显示器一样,我们需要定义一个宽高比 (aspect ratio),如4:3,16:9。我们还需要定义另外一个概念,称为field-of-view,即能看到的角度的范围。假如我们在看一个屏幕,我们可以分别从相机出发与屏幕顶边的中点和底边的中点连出两条红线,它们所形成的夹角就是垂直可视角度 (field-of-view Y, fovY)。因此,定义一个视锥需要定义一个宽高比和垂直可视角度。有些游戏里有水平可视角度,这个可以通过长宽比和垂直可视角度推出。

有了这两个概念,我们就可以将它们和之前定义的空间中的长方体转化成同一个概念。如图所示,从侧面来看这个视锥体可以看到一个三角形,如果我们取垂直可视角度的tangent,可以发现tan\frac{fovY}{2} = \frac{t}{|n|},也就是说,如果我们知道这个近平面的距离,就能知道这个屏幕一半的高度是多少,屏幕的最高点对应的y值就是t,最低点对应的y值就是-t,也就是说,如果定义一个空间中的长方体,b=-t,根据宽高比aspect = \frac{r}{t}即可求出水平方向上两边中点的坐标。

图15

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