ARkit

ARkit

Introducing ARKit

iOS 11引入ARKit,这是 个全新的框架,允许开发者轻松地为iPhone和iPad创建 与伦比

的增强现实体验。通过将虚拟对象和虚拟信息同 户周围的环境相互融合,ARKit使得应 跳出

屏幕的限制,让它们能够以全新的 式与现实世界进 交互。

基础技术视觉惯性 程计

ARKit使 视觉惯性 程计(Visual Inertial Odometry, VIO)来精准追踪周围的世界。VIO将摄像

头的传感器数据同Core Motion数据进 融合。这两种数据允许设备能够 精度地感测设备在房

间内的动作, 且 需额外校准。

场景识别与光亮估

借助ARKit,iPhone和iPad可以分析相机界 中所呈现的场景,并在房间当中寻找 平 。ARKit不仅可以检测诸如桌 和地板之类的 平 ,还可以在较 特征点(feature points)上追踪

和放置对象。ARKit还  摄像头传感器来估算场景当中的可 光总亮度,并为虚拟对象添加符

合环境照明 的光 。

性能硬件与渲染优化

ARKit运 在Apple A9和A10处 器上。这些处 器能够为ARKit提供突破性的性能,从 可

以实现快速场景识别,并且还可以让您基于现实世界场景,来构建详细并引 注 的虚拟内容。

您可以Metal、Scenekit以及诸如Unity、虚幻引擎之类的第三  具,来对ARKit进 优

化。

ARKit

ARKit将iOS设备的摄像头和设备动作检测(Motion)功能,集成到您的应 或者游戏当中,从

为 户提供增强现实体验。

>所谓的增强现实(Augmented Reality, AR),指的是向设备摄像头产 的实时动态视图中,添加2D或者3D元素,然后 某种 法让这些元素看起来就处于现实世界当中,所产  种 户体

验。ARKit提供 设备动作追踪、相机场景捕获和 级场景处 ,并让AR元素的展 变得极为

,从  简化 建立AR户体验的 作难度。

探索AR的概 、特性,以及 解构建优秀AR场景的最佳实践。1.1.ARSession类

这是 个单,是ARKit的核 类, 于控制设备摄像头,处 传感器数据,对捕获的图像进

分析等等。

2.ARSessionConfiguration类

跟踪设备 向的 个基本配置,在运 时,需要指定AR运 的配置

ARWorldTrackingSessionConfiguration类

配置跟踪设备的 向和位置,以及检测设备摄像头所看到的现实世界的表

ARSCNView类

来增强相机通过3D SceneKit所捕捉到的内容并展AR效果的 个view

ARSKView类

来增强相机通过2D SpriteKit所捕捉到的内容并展AR效果的 个view

ARAnchor类

真实世界的位置和 向,于在 个AR场景中放置 个物体

ARPlaneAnchor类

在 个AR Session会话中检测 个真实世界中平 的位置和 向的相关信息

ARHitTestResult类

在 个AR Session会话中通过检测相机视图中的 个点来获取真实世界中表 的相关信息

ARFrame类

捕获 个视频图像和位置追踪信息作为 个AR会话的 部分。

ARCamera类

在 个AR会话中摄像机的位置和成像特征信息为捕获视频帧

ARLightEstimate类

在 个AR会话中估计场景照明信息关联到 个捕获的视频帧

Understanding Augmented Reality

>探索AR的概 、特性,以及 解构建优秀AR场景的最佳实践。

对于所有的AR场景  ,最基本要求是:创建并追踪现实空间和虚拟空间之间的关系,其中,

现实空间是 户所处的世界,虚拟空间是可对可视化内容进 建模的世界,这同时也是ARKit的

基本特征。当您的应 将这些虚拟内容与实时视频结合,并 起显 的时候, 户就可以体验到

所谓的「增强现实」:您的虚拟内容成为 真实世界的 部分,尽管这只是「错觉」 已。

How World Tracking Works

为 在现实世界和虚拟世界之间建立对应关系,ARKit使  种被称为视觉惯性 程计的技

术。这项技术会将iOS设备的动作感测硬件信息,加上对可 场景的计算机视觉分析功能,然后

与设备的摄像头相结合。ARKit将会去识别场景图像当中的显著特征,然后在视频帧中追踪这些

特征位置的距离,然后再将这些信息与动作感测数据进 比较。从  成具备设备位置和动作特

征的 精度模型。

全局追踪(World Tracking)同样也可以分析和识别场景当中的内容。通过使 点击测试(hit-

testing)法(参ARHitTestResult类),从 找到与相机图像中的某个点所对应的真实世界

。如果您在Session (会话)配置当中启planeDetection配置的话,那么ARKit就会去检测

相机图像当中的 平 ,并报告其位置和  。您可以使 点击测试所 成的结果,或者使 所

检测到的 平 ,从 就可以在场景当中放置虚拟内容,或者与之进 交互。

Best Practices and Limitations

全局追踪是 项不精确的科学(inexact science)。尽管在这个过程当中,经常会产 可观的准确

度,从 让AR的体验 加真实。然 ,它严重依赖于设备物 环境的相关细节, 这些细节并

不总是 致,有些时候也难以实时测 ,这也就导致这些物 细节往往都会存在某种程度的错

误。要建立 品质的AR体验,那么请注意下述这些注意事项和提 :

基于可 的照明条件来设计AR场景。全局追踪涉及到 图像分析的相关内容,因此就需要我们

提供清晰的图像。如果摄像头没有办法看到相关的物 细节,比如说摄像头拍到的是  空空如

也的墙壁,或者场景的光线实在太暗的话,那么全局追踪的质 就会  降低。

根据追踪质 的相关信息来给 户进 反馈提 。全局追踪会将图像分析与设备的动作模式关联

起来。如果设备正在移动的话,那么ARKit就可以 好地对场景进 建模,这样即 设备只是

微晃动,也不会影响追踪质 。但是 旦 户的动作过多、过快或者晃动过于激烈,就会导致图

像变得模糊,或者导致视频帧中要追踪的特征之间的距离过 ,从 致使追踪质 的降低。ARCamera类能够提供追踪状态,此外还能提供导致该状态出现的相关原因,您可以在UI上展

这些信息,告诉 户如何解决追踪质 低这个问题。

给 平 检测预 点时间来 成清晰的结果, 旦您获得所需的结果后,就禁  平 检测。

开始对 平 进 检测的时候,所检测到的 平 位置和范围很可能不准确。不过随着时间的推

移,只要 平 仍然保持在场景当中,那么ARKit就能够较为精确地估计 平 的位置和范围。

当场景中有 个比较 的平坦表 的话,就算您已经使 过这个 平 来放置内容,那么ARKit可能还会继续对 平 的锚点位置、范围和变换点进 修正。

ARSession类

>这是 个单,是ARKit的核 类, 于控制设备摄像头,处 传感器数据,对捕获的图像进

分析等等

每 个使ARKit创建的AR程必须要有 个ARSession单 对象.如果你使ARSCNView或者ARSKView来 容 的创建AR程的 部分,这个View已经包含  个ARSession实.如果你使

编写的渲染器来渲染AR内容,你必须实 化和持有 个ARSession对象

运  个会话必须要有相关配置:可以实 化ARSessionConfiguration或者它的 类ARWorldTrackingSessionConfiguration,这些类确定,相对于现实世界, ARKit跟踪设备的位置和

运动,从 影响您可以创建的基于"增强现实”技术的类型

//run法的声明:

//开始为session在指定的配置和选项下处AR

//configuration个对象,定义 会话的运动和现场跟踪 为

//ARSession.RunOptions这是 个结构体,所以当使 系统默认的时候可以写个[],当你改变它的配置

的时候,这个选项会影响怎么过渡 个AR会话的状态

func run(_ configuration: ARSessionConfiguration, options: ARSession.RunOptions

= [])

//pause法的声明:

func pause()

//代//代  法可以实现接收视频帧图像捕获和跟踪状态的AR会话//个 法都是可选

var delegate: ARSessionDelegate?

//代 队,如果没有设置的话,默认 主队var delegateQueue: DispatchQueue?

//实现  的 法可以对AR会话的状态进 改变protocol ARSessionObserver

-显 和影响AR内容

var currentFrame: ARFrame?

func add(anchor: ARAnchor)

func remove(anchor: ARAnchor)

Building a Basic AR Experience构建基本的AR场景

配置AR Session,然后使SceneKit或者SpriteKit来展AR内容。

如果您使ARSCNView或者ARSKView类的话,那么ARKit就可  满 创建AR场景的基本

要求:即每个视图的背景 实时的相机图像来展 ,然后还可以渲染您提供的2D或者3D覆盖

物(overlay),从 构建出「这些覆盖物实际上是位于现实世界中」这样 种错觉。要使 这些视

图类的话,您可以根据您所想要创建的AR场景类型来进 配置,然后为覆盖物选定位置和表

式。

如果您需要构建 定义的视图来展AR场景的话,请参阅使Metal来展AR场景 节。

本 所涉及的代码均可以在Xcode项 模板当中找到。如果要获取完整的  代码,请使“Augmented Reality”模板来创建 个新的iOS应 ,然后在弹出的Content Technology菜单

当中选择SceneKit或者SpriteKit。

Configure and Run the AR Session /配置AR Session并运

ARSCNView和ARSKView类都是包含在ARSession当中的,ARSession对象则 来管 设备动

作追踪和进 图像处 的,也就是创建AR场景的必需品。但是,要运Session,您 先必须

选择 种Session配置。

您所选择的配置对象的类型,决定 您所创建的AR场景的 格和质 :

在具备A9或者  版本处 器的iOS设备当中,ARWorldTrackingSessionConfiguration类

提供  精度的设备动作追踪功能,可以帮助您将虚拟内容「放置」在现实世界中的某个表

上。

在ARKit所 持的其他设备当中,ARSessionConfiguration这个基础类则提供 基本的动作追踪

功能,可以提供 弱的沉浸式AR体验。

要启动AR Session,那么 先要使 您所需要的配置,来创建Session配置对象,然后对ARSCNView或者ARSKView实 中的session对象调run(_:options:)法:

override func viewWillAppear(_ animated: Bool) {

super.viewWillAppear(animated)

// Create a session configuration

let configuration = ARWorldTrackingSessionConfiguration()

configuration.planeDetection = .horizontal

// Run the view's session

sceneView.session.run(configuration)

}

>重要

>只有当视图即将展 在屏幕的时候,才能够运 视图Session。

当您配置完AR Session之后,就可以使SceneKit或者SpriteKit,来将虚拟内容放置到视图当

中 。

Providing 3D Virtual Content with SceneKit使SceneKit来提供3D虚

拟元素

>使SceneKit在您的AR场景中放置逼真的三维图像。

由于ARKit会 动将SceneKit空间与现实世界进 匹配,因此只需要放置 个虚拟对象,然后

让其位于 个看起来比较真实的位置,这就需要您适当地设置SceneKit对象的位置。 如,在

默认配置下,以下代码会将 个10立 厘米的立 体放置在相机初始位置的前20厘米处:

let cubeNode = SCNNode(geometry: SCNBox(width: 0.1, height: 0.1, length: 0.1,

chamferRadius: 0))

cubeNode.position = SCNVector3(0, 0, -0.2) // SceneKit/AR coordinates are in

meters

sceneView.scene.rootNode.addChildNode(cubeNode)

>上述代码直接在视图的SceneKit场景中放置  个对象。该对象会 动追踪真实世界的位置,

因为ARKit将SceneKit空间与现实世界空间互相匹配 起来

此外,您也可以使ARAnchor类来追踪现实世界的位置,可以  创建锚点并将其添加到Session当中,也可以对ARKit动创建的锚点进 观察(observing)。 如,当 平 检测启

的时候,ARKit会为每个检测到的 平 添加锚点,并保持 新。要为这些锚点添加可视化内容

的话,就需要实现ARSCNViewDelegate法,如下所 :

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor:

ARAnchor) {

// This visualization covers only detected planes.

guard let planeAnchor = anchor as? ARPlaneAnchor else { return }

// Create a SceneKit plane to visualize the node using its position and

extent.

let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height:

CGFloat(planeAnchor.extent.z))

let planeNode = SCNNode(geometry: plane)

planeNode.position = SCNVector3Make(planeAnchor.center.x, 0,

planeAnchor.center.z)

// SCNPlanes are vertically oriented in their local coordinate space.

// Rotate it to match the horizontal orientation of the ARPlaneAnchor.

planeNode.transform = SCNMatrix4MakeRotation(-Float.pi / 2, 1, 0, 0)

// ARKit owns the node corresponding to the anchor, so make the plane a child

node.

node.addChildNode(planeNode)

}

Follow Best Practices for Designing 3D Assets /遵循设计3D资源时的最佳实践

>使SceneKit基于物 引擎的照明模型,以 获得 为逼真的外观。(参SCNMaterial类和

SceneKit代码项 当中的 「Badger: Advanced Rendering」。)

打上环境光遮蔽阴影(Bake ambient occlusion shading),使得物体在各种场景照明条件下能够

正常亮起。

>如果您打算创建 个虚拟对象,并打算将其放置在AR的真实平(real-world flat surface)上,

那么请在3D素材中的对象下 ,添加 个带有柔和阴影纹 的透明平 。

Providing 2D Virtual Content with SpriteKit(使SpriteKit来提供2D虚

拟元素)

使SpriteKit在您的3D AR场景中放置 维图像。

SpriteKit只包含有2D元素,但是增强现实则涉及到现实世界的3D空间。因此,使ARSKView类来创建AR场景,这样就可以在真实世界对应的3D位置( ARAnchor对象)中放置2D精灵元素( SKNode)对象)。当 户移动设备的时候,视图会 动旋转并缩放与锚点相对应的SpriteKit结点,看起来这些元素能够追踪相机所看到的真实世界。

举个  ,您可以将2D图像以漂浮的 式,放置在3D空间当中:

// Create a transform with a translation of 0.2 meters in front of the camera.

var translation = matrix_identity_float4x4

translation.columns.3.z = -0.2

let transform = simd_mul(view.session.currentFrame.camera.transform, translation)

// Add a new anchor to the session.

let anchor = ARAnchor(transform: transform)

view.session.add(anchor: anchor)

// (Elsewhere...) Provide a label node to represent the anchor.

func view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {

return SKLabelNode(text: "👾")

}

上 的这个view(_:nodeFor:)法将会返回 个SKLabelNode对象, 以展  本标签。与 多

数SpriteKit结点 样,这个类将会创建 个2D的可视化表 ,因此ARSKView类将会以 告牌

的样式来展 这个结点:精灵可以通过(围绕z轴)缩放以及旋转,让其看起来能够跟随锚点的3D位置,但是却始终 向相机。

Displaying an AR Experience with Metal(使Metal来展AR场景)通过渲染相机图像,以及使 位置追踪信息来展 覆盖(overlay)物,从 来构建 定义的AR视

图场景。

ARKit中内置  些视图类,从 能够轻松地SceneKit或者SpriteKit来展AR场景。然 ,

如果您使 的是  的渲染引擎(或者集成 第三 引擎),那么ARKit还提供  定义视图以

及其他的 持环境,来展AR场景。

在所有的AR场景中, 先就是要配置 个ARSession对象, 来管 摄像头拍摄和对设备动作

进 处 。Session定义并维护现实空间和虚拟空间之间的关联关系,其中,现实空间是 户所

处的世界,虚拟空间是可对可视化内容进 建模的世界。如果要在 定义视图当中展 您的AR场景的话,那么您需要:

从Session中检索视频帧和追踪信息

将这些帧图像作为背景,渲染到 定义视图当中

使 追踪信息,从 在相机图像上 定位并绘制AR内容

>本 所涉及的代码均可以在Xcode项 模板当中找到。如果要获取完整的  代码,请使“Augmented Reality”模板来创建 个新的iOS应 ,然后在弹出的Content Technology菜单

当中选择“Metal”

Get Video Frames and Tracking Data from the Session(从Session中获取视频帧和追

踪数据)

请  创建并维护ARSession实 ,然后根据您所希望提供的AR场景类型,使 合适的Session配置来运 这个实 。(要实现这点的话,请参阅「构建基本的AR场景」。)Session从摄像机当中捕获视频,然后在建模的3D空间中追踪设备的位置和 向,并提供ARFrame对

象。每个ARFrame对象都包含有单独的视频帧(frame)图像和被捕获时的设备位置追踪信息。

要访问AR Session中 成的ARFrame对象的话,有以下两种 法,使 何种 法取决于您应

的设计模式是偏好主动拉取(pull)还是被动推送(push)。

如果您倾向于定时获取视频帧的话(也就是主动拉取设计模式),那么请使Session的currentFrame属性,这样就可以在每次重绘视图内容的时候,获取当前的帧图像和追踪信息。ARKit Xcode模板使  如下 法:

// in Renderer class, called from MTKViewDelegate.draw(in:) via Renderer.update()

func updateGameState() {

guard let currentFrame = session.currentFrame else {

return

}

updateSharedUniforms(frame: currentFrame)

updateAnchors(frame: currentFrame)

updateCapturedImageTextures(frame: currentFrame)

if viewportSizeDidChange {

viewportSizeDidChange = false

updateImagePlane(frame: currentFrame)

}

}

相反,如果您的应 设计倾向于使 被动推送模式的话,那么请实现session(_:didUpdate:)代

法,当每个视频帧被捕获之后,Session就会调 这个代  法(默认每秒捕获60帧)。

获得 个视频帧之后,您就需要绘制相机图像 ,然后将AR场景中包含的所有覆盖物进  新

和展 。

Draw the Camera Image(绘制相机图像)

每个ARFrame对象的capturedImage属性都包含 从设备相机中捕获的像素缓冲区(pixel

buffer)。要将这个图像作为背景绘制到 定义视图当中,您需要从图像内容中构建纹(texture),然后提交使 这些纹 进GPU渲染的命令。

像素缓冲区的内容将被编码为双(biplanar) YCbCr数据格式(也成为YUV);要渲染图像的

话,您需要将这些像素数据转换为可绘制的RGB格式。对于Metal渲染  ,最 效的 法

是使GPU着 代码(shader code)来执 这个转换 。借助CVMetalTextureCacheAPI,可以

从像素缓冲区中 成两个Metal纹——个 于决定缓冲区的亮度(Y), 个 于决定缓冲区的

度(CbCr)。

func updateCapturedImageTextures(frame: ARFrame) {

// Create two textures (Y and CbCr) from the provided frame's captured image//从所提供的视频帧中,根据其中所捕获的图像,创建两个纹(Y and CbCr)

let pixelBuffer = frame.capturedImage

if (CVPixelBufferGetPlaneCount(pixelBuffer) < 2) {

return

}

capturedImageTextureY = createTexture(fromPixelBuffer: pixelBuffer,

pixelFormat:.r8Unorm, planeIndex:0)!

capturedImageTextureCbCr = createTexture(fromPixelBuffer: pixelBuffer,

pixelFormat:.rg8Unorm, planeIndex:1)!

}

func createTexture(fromPixelBuffer pixelBuffer: CVPixelBuffer, pixelFormat:

MTLPixelFormat, planeIndex: Int) -> MTLTexture? {

var mtlTexture: MTLTexture? = nil

let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex)

let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex)

var texture: CVMetalTexture? = nil

let status = CVMetalTextureCacheCreateTextureFromImage(nil,

capturedImageTextureCache, pixelBuffer, nil, pixelFormat, width, height,

planeIndex, &texture)

if status == kCVReturnSuccess {

mtlTexture = CVMetalTextureGetTexture(texture!)

}

return mtlTexture

}

接下来,使 借助颜 变换矩阵将YCbCr转换为RGB的函数片段,完成这两个纹 的绘制,我

们这 将整个渲染命令进 编码。

fragment float4 capturedImageFragmentShader(ImageColorInOut in [[stage_in]],

texture2d

capturedImageTextureY [[ texture(kTextureIndexY) ]],

texture2d

capturedImageTextureCbCr [[ texture(kTextureIndexCbCr) ]]) {

constexpr sampler colorSampler(mip_filter::linear,

mag_filter::linear,

min_filter::linear);

const float4x4 ycbcrToRGBTransform = float4x4(

float4(+1.164380f, +1.164380f, +1.164380f, +0.000000f),

float4(+0.000000f, -0.391762f, +2.017230f, +0.000000f),

float4(+1.596030f, -0.812968f, +0.000000f, +0.000000f),

float4(-0.874202f, +0.531668f, -1.085630f, +1.000000f)

);

// Sample Y and CbCr textures to get the YCbCr color at the given texture

coordinate

float4 ycbcr = float4(capturedImageTextureY.sample(colorSampler,

in.texCoord).r,

capturedImageTextureCbCr.sample(colorSampler,

in.texCoord).rg, 1.0);

// Return converted RGB color

return ycbcrToRGBTransform * ycbcr;

}

请使displayTransform(withViewportSize:orientation:)法来确保整个相机图像完全覆盖 整

个视图。关于如何使 这个 法,以及完整的Metal管道配置代码,请参阅完整的Xcode模

板。(请使“Augmented Reality”模板来创建 个新的iOS应 ,然后在弹出的Content

Technology菜单当中选择“Metal”。)

Track and Render Overlay Content(追踪并渲染覆盖内容)

>AR场景通常侧重于渲染3D覆盖物,使得这些内容似乎是从相机中所看到的真实世界的 部分。

为 实现这种效果,我们使ARAnchor类,来对3D内容相对于现实世界空间的位置和 向进

建模。锚点提供 变换(transform)属性,在渲染的时候可供参考。

举个  ,当 户点击屏幕的时候,Xcode模板会在设备前  约20厘米处,创建 个锚点。

func handleTap(gestureRecognize: UITapGestureRecognizer) {

// Create anchor using the camera's current position

if let currentFrame = session.currentFrame {

// Create a transform with a translation of 0.2 meters in front of the

camera

var translation = matrix_identity_float4x4

translation.columns.3.z = -0.2

let transform = simd_mul(currentFrame.camera.transform, translation)

// Add a new anchor to the session

let anchor = ARAnchor(transform: transform)

session.add(anchor: anchor)

}

}

在您的渲染引擎当中,使 每个ARAnchor对象的transform属性来放置虚拟内容。Xcode模板

在内部的handleTap法中,使 添加到Session当中每个锚点来定位 个简单的立 体 格(cube mesh):

func updateAnchors(frame: ARFrame) {

// Update the anchor uniform buffer with transforms of the current frame's

anchors

anchorInstanceCount = min(frame.anchors.count, kMaxAnchorInstanceCount)

var anchorOffset: Int = 0

if anchorInstanceCount == kMaxAnchorInstanceCount {

anchorOffset = max(frame.anchors.count - kMaxAnchorInstanceCount, 0)

}

for index in 0..

let anchor = frame.anchors[index + anchorOffset]

// Flip Z axis to convert geometry from right handed to left handed

var coordinateSpaceTransform = matrix_identity_float4x4

coordinateSpaceTransform.columns.2.z = -1.0

let modelMatrix = simd_mul(anchor.transform, coordinateSpaceTransform)

let anchorUniforms = anchorUniformBufferAddress.assumingMemoryBound(to:

InstanceUniforms.self).advanced(by: index)

anchorUniforms.pointee.modelMatrix = modelMatrix

}

}

在 为复杂的AR场景中,您可以使 点击测试或者 平 检测,来寻找真实世界当中曲 的位

置。要 解关于此内容的详细信息,请参阅planeDetection属性和hitTest(_:types:)法。对于这

两者  ,ARKit都会 成ARAnchor对象作为结果,因此您仍然需要使 锚点的transform属

性来放置虚拟内容。

Render with Realistic Lighting(根据实际光照度进 渲染)

当您在场景中配置 于绘制3D内容的着 器时,请使 每个ARFrame对象当中的预计光照度信

息,来产  为逼真的阴影:

// in Renderer.updateSharedUniforms(frame:):

// Set up lighting for the scene using the ambient intensity if provided

var ambientIntensity: Float = 1.0

if let lightEstimate = frame.lightEstimate {

ambientIntensity = Float(lightEstimate.ambientIntensity) / 1000.0

}

let ambientLightColor: vector_float3 = vector3(0.5, 0.5, 0.5)

uniforms.pointee.ambientLightColor = ambientLightColor * ambientIntensity

要 解该  中的全部Metal配置,以及所使 的渲染命令,请参 完整的Xcode模板。(请

使“Augmented Reality”模板来创建 个新的iOS应 ,然后在弹出的Content Technology菜单当中选择“Metal”。

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

推荐阅读更多精彩内容