iOS開(kāi)發(fā):Metal初探(畫(huà)五角星+將圖片3D化)

背景

在 WWDC 2014 上捺檬,Apple為游戲開(kāi)發(fā)者推出了新的平臺(tái)技術(shù) Metal,該技術(shù)能夠?yàn)?D圖像提高 10 倍的渲染性能贸铜,充分利用GPU的運(yùn)算能力堡纬。
在現(xiàn)階段,AVFoundation蒿秦、?臉識(shí)別等大量需要顯示計(jì)算的時(shí)候烤镐,蘋(píng)果采用了硬件加速器驅(qū)動(dòng)GPU工作;在音視頻方面棍鳖,?頻編碼/解碼 / 視頻編碼/解碼 ->壓縮任務(wù)都與硬件加速器分不開(kāi)炮叶,蘋(píng)果提供的Metal,能發(fā)揮GPU/CPU的最大性能渡处,并且管理我們的資源镜悉,蘋(píng)果想用metal替代opengl作為底層繪制框架。metal常見(jiàn)應(yīng)用于一些游戲医瘫、濾鏡侣肄、相機(jī)類的app。
設(shè)備支持:iOS 8以上醇份,A7處理器以上稼锅,因此只有iphone5以上機(jī)型才支持metal,并且不支持模擬器運(yùn)行僚纷,只支持真機(jī)矩距。

基本概念

坐標(biāo)系

這里的坐標(biāo)系先不講坐標(biāo)空間,只是最基礎(chǔ)的頂點(diǎn)坐標(biāo)和紋理坐標(biāo)畔濒。
跟平時(shí)我們開(kāi)發(fā)寫(xiě)UI以左上角為原點(diǎn)不一樣剩晴,Metal的頂點(diǎn)坐標(biāo)系跟openGL一樣,是以屏幕中心為原點(diǎn),歸一化的坐標(biāo)系赞弥。
四維均勻向量 ( x毅整,y,z绽左,w) 指定一個(gè)三維點(diǎn)剪輯空間坐標(biāo)悼嫉。頂點(diǎn)著色器在剪輯空間坐標(biāo)中生成位置。Metal分 x 拼窥,y戏蔑,和z值由w將剪輯空間坐標(biāo)轉(zhuǎn)換為標(biāo)準(zhǔn)化設(shè)備坐標(biāo)左下角位于( x,y)的坐標(biāo)(- 1.0鲁纠,-1.0) 而上角在(1.0,1.0) 总棵。


頂點(diǎn)坐標(biāo).png

如果只是繪制單色形狀的話,只用頂點(diǎn)坐標(biāo)然后填充顏色就行改含,但如果要繪制圖片情龄,或者給形狀貼上紋理,就需要用到紋理坐標(biāo)系捍壤。在metal中骤视,紋理的原點(diǎn)坐標(biāo)在左上角,這和openGL是不同的(OpenGL的紋理原點(diǎn)坐標(biāo)在左下角)


紋理坐標(biāo).png

著色器

metal的著色器有主要頂點(diǎn)著色器鹃觉、片元著色器专酗、內(nèi)核計(jì)算函數(shù)
vertex: 表示該函數(shù)是一個(gè)頂點(diǎn)著色函數(shù),它將為頂點(diǎn)數(shù)據(jù)流中的每一個(gè)頂點(diǎn)執(zhí)行一次然后為每一個(gè)頂點(diǎn)生成數(shù)據(jù)輸出到繪制管線盗扇。
示例代碼:

vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]],uint vid [[vertex_id]]){
    return vertices[vid];
}

fragment: 表示該函數(shù)是一個(gè)片元函數(shù)祷肯,它將為片元數(shù)據(jù)流中的每一個(gè)片元和其關(guān)聯(lián)執(zhí)行一次然后將每一個(gè)片元的顏色數(shù)據(jù)輸出到繪制管線中。
示例代碼:

fragment float4 fragment_func(Vertex vert [[stage_in]]){
    return float4(1.0, 1.0, 0.0, 1.0);
}

kernel:表示該函數(shù)是一個(gè)并行計(jì)算著色函數(shù)粱玲,它可以被分配在一維/二維/三維線程中去執(zhí)行躬柬。
示例代碼:

kernel void blend(texture2d<float, access::read> imageTexture [[texture(0)]],
                  texture2d<float, access::read> faceTexture [[texture(1)]],
                  texture2d<float, access::write> blendTexture [[texture(2)]],
                  uint2 gid [[thread_position_in_grid]]) {
    float width = faceTexture.get_width();
    float height = faceTexture.get_height();
    if ((gid.x >= width) || (gid.y >= height)) {
        return;
    }
    float4 face = faceTexture.read(gid);
    if(face.a > 0.0){
        blendTexture.write(face, gid);
    }
    else {
        uint2 pos = uint2(gid.x, gid.y);
        float4 image = imageTexture.read(pos);
        blendTexture.write(image, gid);
    }
}

著色器的基礎(chǔ)語(yǔ)法規(guī)范可以參考蘋(píng)果官方文檔拜轨,文檔比較復(fù)雜抽减,還是全英文的,中文翻譯可以參考這個(gè)專欄

Metal工作流程

Metal渲染管道流程


渲染管道流程.jpg

在OpenGLES中橄碾,圖元裝配有9種卵沉,在Metal中,圖元裝配只有五種法牲,他們分別是:
MTLPrimitiveTypePoint = 0, 點(diǎn)
MTLPrimitiveTypeLine = 1, 線段
MTLPrimitiveTypeLineStrip = 2, 線環(huán)
MTLPrimitiveTypeTriangle = 3, 三角形
MTLPrimitiveTypeTriangleStrip = 4, 三角型扇
metal的驅(qū)動(dòng)GPU進(jìn)行繪制的流程如圖所示史汗,下面進(jìn)行一些參數(shù)名詞的解釋


Metal驅(qū)動(dòng)GPU繪制流程.png

基礎(chǔ)名詞解釋

MTLDevice

可以理解為GPU對(duì)象,可以用如下方法獲得:

let device = MTLCreateSystemDefaultDevice()
guard device != nil else {
   print("Metal is not supported on this device")
   return
}

上面說(shuō)過(guò)只有iOS8以及A7芯片以上才支持Metal拒垃,所以MTLDevice可以為空停撞,需要判斷

MTLCommandQueue

有了GPU之后,需要?jiǎng)?chuàng)建一個(gè)渲染隊(duì)列MTLCommandQueue,隊(duì)列是單一隊(duì)列戈毒,確保了指令能夠按順序執(zhí)行艰猬,里面的對(duì)象是需要渲染的指令MTLCommandBuffer,可以支持多個(gè)CommandBuffer同時(shí)編碼埋市。通過(guò)MTLDevice可以獲取MTLCommandQueue:

let queue:MTLCommandQueue = device?.makeCommandQueue()

MTKView

承接Metal繪制的視圖冠桃,初始化方法為let viewiew = MTKView.init(frame: self.bounds, device: MTLCreateSystemDefaultDevice)
MTKView有兩個(gè)delegate:

- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size;
- (void)drawInMTKView:(nonnull MTKView *)view;

drawInMTKView:方法是MetalKit每幀的渲染回調(diào),可以在內(nèi)部做渲染的處理
drawableSizeWillChange:方法是繪制區(qū)域發(fā)生改變的方法道宅,在里面可以給繪圖區(qū)域大小重新賦值
在demo中沒(méi)有使用這兩個(gè)delegate食听,而是直接重寫(xiě)了MTKView的draw方法,也能實(shí)現(xiàn)同樣的效果
不用MTKView也可以使用CAMetalLayer污茵,添加到當(dāng)前view的layer上樱报。
剩下的一些參數(shù)會(huì)通過(guò)一個(gè)畫(huà)五角星例子具體說(shuō)明。

Metal繪制步驟

1. 新建MTKView

可以用let viewiew = MTKView.init(frame: self.bounds, device: MTLCreateSystemDefaultDevice)初始化MTKView泞当,demo中是使用storyBoard拖入使用的

2. 設(shè)置頂點(diǎn)數(shù)據(jù)

demo是畫(huà)一個(gè)三角形肃弟、五角星,三角形的話需要提供三個(gè)頂點(diǎn)的坐標(biāo)零蓉,五角星的話笤受,由于Metal繪制都是通過(guò)畫(huà)三角形去繪制,所以敌蜂,需要繪制下圖所示的十個(gè)三角形箩兽,加上中心點(diǎn)總計(jì)10個(gè)頂點(diǎn)數(shù)據(jù)


五角星頂點(diǎn).jpg
func setFiveAngleData() {
    vertexData = [0.0, 0.0, 0.0, 1.0,
                  0.0, BIG_R*Y_SCALE, 0.0, 1.0,
                  SMALL_R*cos(54*RAD), SMALL_R*sin(54*RAD)*Y_SCALE, 0.0, 1.0,
                  BIG_R*cos(18*RAD), BIG_R*sin(18*RAD)*Y_SCALE, 0.0, 1.0,
                  SMALL_R*cos(18*RAD), -SMALL_R*sin(18*RAD)*Y_SCALE, 0.0, 1.0,
                  BIG_R*cos(54*RAD), -BIG_R*sin(54*RAD)*Y_SCALE, 0.0, 1.0,
                  0.0, -SMALL_R*Y_SCALE, 0.0, 1.0,
                  -BIG_R*cos(54*RAD), -BIG_R*sin(54*RAD)*Y_SCALE, 0.0, 1.0,
                  -SMALL_R*cos(18*RAD), -SMALL_R*sin(18*RAD)*Y_SCALE, 0.0, 1.0,
                  -BIG_R*cos(18*RAD), BIG_R*sin(18*RAD)*Y_SCALE, 0.0, 1.0,
                  -SMALL_R*cos(54*RAD), SMALL_R*sin(54*RAD)*Y_SCALE, 0.0, 1.0]
    indexData = [0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4 ,5, 0, 5, 6, 0, 6, 7, 0, 7, 8, 0, 8, 9, 0, 9 , 10, 0, 10, 1]
}

其中,vertexData是頂點(diǎn)數(shù)據(jù)章喉,四個(gè)代表一個(gè)點(diǎn)(x,y,z,w)汗贫;indexData是索引數(shù)據(jù),三個(gè)為一組秸脱,代表頂點(diǎn)組成三角形的順序

3. 設(shè)置紋理數(shù)據(jù)

將一張UIImage渲染到MKTView上落包,需要用到紋理數(shù)據(jù)MTLTexture,可以由以下方法獲忍健:

func getTexture() {
        do {
            self.imageTexture = try MTKTextureLoader(device: self.device).newTexture(cgImage: image.cgImage!, options: [MTKTextureLoader.Option.SRGB:false])
        } catch {
            assertionFailure("Could not create Texture - \(error) ")
        }
        render(texture: imageTexture)
    }

然后需要將紋理坐標(biāo)(二維)加入到頂點(diǎn)坐標(biāo)中

4. 設(shè)置渲染管道

func render() {
    let library = device?.makeDefaultLibrary()!
    let vertex_func = library?.makeFunction(name: "vertex_func")
    let frag_func = library?.makeFunction(name: "fragment_func")
    let rpld = MTLRenderPipelineDescriptor()
    rpld.vertexFunction = vertex_func
    rpld.fragmentFunction = frag_func
    rpld.colorAttachments[0].pixelFormat = .bgra8Unorm
    do{
        try rps = device?.makeRenderPipelineState(descriptor: rpld)
    }catch let error{
        fatalError("\(error)")
    }
}

其中咐蝇,MTLRenderPipelineDescriptor是渲染管道的描述符,可以設(shè)置頂點(diǎn)處理函數(shù)巷查、片元處理函數(shù)有序、輸出顏色格式等;

5. 具體渲染過(guò)程

override func draw(_ rect: CGRect) {
    if let drawable = currentDrawable, let rpd = currentRenderPassDescriptor {
        let dataSize = vertexData!.count * MemoryLayout<Float>.size
        // 設(shè)置頂點(diǎn)buffer
        vertexBuffer = device?.makeBuffer(bytes: vertexData!, length: dataSize, options: [])
        // 設(shè)置索引buffer
        indexBuffer = device?.makeBuffer(bytes: indexData!, length: MemoryLayout<UInt16>.size * indexData!.count , options: [])
        // 設(shè)置背景色為紅色
        rpd.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 0.0, 0.0, 1.0)
        // 創(chuàng)建commandBuffer
        let commandBuffer = commandQueue!.makeCommandBuffer()
        let commandEncode = commandBuffer?.makeRenderCommandEncoder(descriptor: rpd)
        commandEncode?.setRenderPipelineState(rps!)
        // 設(shè)置頂點(diǎn)緩存
        commandEncode?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
        // 繪制
        commandEncode?.drawIndexedPrimitives(type: .triangle, indexCount: indexBuffer!.length / MemoryLayout<UInt16>.size, indexType: MTLIndexType.uint16, indexBuffer: indexBuffer!, indexBufferOffset: 0)
        // 結(jié)束設(shè)置
        commandEncode?.endEncoding()
        // 顯示繪制內(nèi)容
        commandBuffer?.present(drawable)
        // 提交命令編碼器
        commandBuffer?.commit()
    }
}

繪制的第一步是從commandQueue里面創(chuàng)建commandBuffer岛请,commandQueue是整個(gè)app繪制的隊(duì)列旭寿,commandBuffer存放每次渲染的指令,commandQueue內(nèi)部存在著多個(gè)commandBuffer崇败,CommandEncoder是命令編碼器

6. Shader處理

#include <metal_stdlib>
using namespace metal;

struct Vertex {
    float4 position [[position]];
};

vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]],uint vid [[vertex_id]]){
    return vertices[vid];
}

fragment float4 fragment_func(Vertex vert [[stage_in]]){
    return float4(1.0, 1.0, 0.0, 1.0);
}

Shader的語(yǔ)法與C++類似盅称,參數(shù)名前面的是類型,后面的[[ ]]是描述符。
其中vertex函數(shù)是讀取頂點(diǎn)信息缩膝,fragment函數(shù)是進(jìn)行顏色填充處理搭幻,這里填充的是黃色。
運(yùn)行出來(lái)的效果:

Metal五角星.jpeg

demo地址

Metal圖片處理

上面第一個(gè)例子通過(guò)一個(gè)五角星講了最基礎(chǔ)的Metal用法逞盆,接下來(lái)會(huì)用另一個(gè)例子來(lái)講一下圖片的一些處理效果大咱,是將一張平面圖片轉(zhuǎn)化為三維圖片歌溉。

實(shí)現(xiàn)思路

二維圖片頂點(diǎn)坐標(biāo)z軸的值都為0凿叠,要變成三維程腹,只需要z軸不為0即可,因此可以在將圖片顯示到MTKView上后舅逸,處理頂點(diǎn)著色器桌肴,給每個(gè)頂點(diǎn)的z軸都賦上相應(yīng)的值,這里可以用每個(gè)像素點(diǎn)的RGB轉(zhuǎn)YUV的算法琉历,取每個(gè)像素的亮度Y的值坠七,作為z方向上的深度值,具體轉(zhuǎn)化為:
inVertex.position.z = 0.299 * color.r + 0.587 * color.g + 0.114 * color.b
如果只給z軸不為0的值旗笔,圖片雖然是三維的彪置,但是依舊只能看到正面的部分,z軸的深度部分是看不到的蝇恶,所以還需要給圖片加入相應(yīng)的空間矩陣變換,會(huì)用到三種矩陣MVP:分別是模型矩陣(model)拳魁、觀察矩陣(view)、投影矩陣(Projection):

投影矩陣:GLKMatrix4MakePerspective

透視投影是模仿人眼觀察物體撮弧,有遠(yuǎn)小近大的效果潘懊,所以這種投影更加真實(shí)
由于Metal并沒(méi)有對(duì)應(yīng)的矩陣運(yùn)算的框架(不知道到底有沒(méi)有,我沒(méi)找到)贿衍,所以這里采用的是GLKit框架里面的矩陣授舟,下同理。
來(lái)看一下這個(gè)投影矩陣的參數(shù)贸辈,是個(gè)4X4矩陣

public func GLKMatrix4MakePerspective(_ fovyRadians: Float, _ aspect: Float, _ nearZ: Float, _ farZ: Float) -> GLKMatrix4
投影矩陣.jpg

其中參數(shù)fovyRadians定義視野在Y-Z平面的角度释树,范圍是[0.0,180.0];參數(shù)aspect是投影平面寬度與高度的比率裙椭;參數(shù)nearZ和farZ分別是近遠(yuǎn)裁剪面到視點(diǎn)(沿Z負(fù)軸)的距離躏哩,它們總為正值。

觀察矩陣:GLKMatrix4MakeLookAt

來(lái)看一下這個(gè)觀察矩陣的參數(shù)揉燃,是個(gè)4X4矩陣

public func GLKMatrix4MakeLookAt(_ eyeX: Float, _ eyeY: Float, _ eyeZ: Float, _ centerX: Float, _ centerY: Float, _ centerZ: Float, _ upX: Float, _ upY: Float, _ upZ: Float) -> GLKMatrix4

這個(gè)矩陣模擬了人眼或者攝像機(jī)在空間的一些位置參數(shù),設(shè)置這9個(gè)參數(shù)以控制攝像機(jī)從不同的角度觀察物體:
eyeX, eyeY, eyeZ定義攝像機(jī)的位置筋栋;
centerX, centerY, centerZ攝像機(jī)看向的點(diǎn)炊汤;
相機(jī)還可以旋轉(zhuǎn)360,upX, upY, upZ三個(gè)參數(shù)確定相機(jī)向上的朝向。

模型矩陣

模型矩陣就設(shè)為單位矩陣GLKMatrix4Identity抢腐,這里可以給他加上旋轉(zhuǎn)變換矩陣GLKMatrix4Rotate姑曙,給模型矩陣加上旋轉(zhuǎn)矩陣是讓物體自己動(dòng),如果修改上面觀察矩陣的一些參數(shù)迈倍,就是攝像機(jī)或人眼圍繞著物體在動(dòng)伤靠,無(wú)論哪一種方法都能模擬出物體旋轉(zhuǎn)的效果,這里選擇的是物體自己動(dòng)啼染,也就是在模型矩陣上加入旋轉(zhuǎn)變化宴合。
然后將mvp直接相乘,結(jié)果再與頂點(diǎn)坐標(biāo)相乘迹鹅。注意相乘的順序先進(jìn)行模型矩陣變換卦洽,再是觀察矩陣,最后是投影矩陣變換斜棚,所以應(yīng)為
P * V * M * vertex.position阀蒂。

繪制

基本流程跟上面說(shuō)的大體一致,但是需要額外設(shè)置一些東西

func draw(renderEncoder: MTLRenderCommandEncoder, texture: MTLTexture, type: MTLPrimitiveType) {
    self.uniforms = Uniforms.init()
    self.vertexData = buildPointData()
    self.indexData = buildIndexData()
    
    // 設(shè)置頂點(diǎn)和索引buffer    
    let vertexBufferSize = MemoryLayout<Float>.stride * self.vertexData.count
    let indexBufferSize = MemoryLayout<UInt32>.stride * self.indexData.count
    let vertexBuffer = device.makeBuffer(bytes: self.vertexData, length: vertexBufferSize, options: MTLResourceOptions.cpuCacheModeWriteCombined)
    let indexBuffer = device.makeBuffer(bytes: self.indexData, length: indexBufferSize , options: MTLResourceOptions.cpuCacheModeWriteCombined)
    
    // 設(shè)置MVP矩陣及其buffer  
    let aspect = self.bounds.width / self.bounds.height
    // 這里將pinch手勢(shì)的縮放參數(shù)傳入投影矩陣弟蚀,就能進(jìn)行縮放
    var GLKPerspective = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(degree), Float(aspect), 0.1, 10.0)
    var GLKView = GLKMatrix4MakeLookAt(0.0, 0.0, 2.0, 0, 0, 0.0, 0.0, 1.0, 0.0)
    var GLKModel = GLKMatrix4Identity
    // 這里將pan手勢(shì)的滑動(dòng)距離參數(shù)經(jīng)過(guò)調(diào)整后傳到旋轉(zhuǎn)矩陣中蚤霞,就能旋轉(zhuǎn)滑動(dòng)了
    GLKModel = GLKMatrix4Rotate(GLKModel, centerX, 1, 0, 0)
    GLKModel = GLKMatrix4Rotate(GLKModel, centerY, 0, 1, 0)
    let perspectiveBuffer = device.makeBuffer(bytes: &GLKPerspective, length: MemoryLayout<float4x4>.size, options: .cpuCacheModeWriteCombined)
    let viewBuffer = device.makeBuffer(bytes: &GLKView, length: MemoryLayout<float4x4>.size, options: .cpuCacheModeWriteCombined)
    let modelBuffer = device.makeBuffer(bytes: &GLKModel, length: MemoryLayout<float4x4>.size, options: .cpuCacheModeWriteCombined)
    
    // 設(shè)置頂點(diǎn)著色器的緩沖區(qū),index要對(duì)應(yīng)shader   
    renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
    renderEncoder.setVertexBuffer(modelBuffer, offset: 0, index: 1)
    renderEncoder.setVertexBuffer(viewBuffer, offset: 0, index: 2)
    renderEncoder.setVertexBuffer(perspectiveBuffer, offset: 0, index: 3)
    let uniformBuffer = device.makeBuffer(bytes: self.uniforms.data(), length: Uniforms.sizeInBytes(), options: MTLResourceOptions.cpuCacheModeWriteCombined)
    renderEncoder.setVertexBuffer(uniformBuffer, offset: 0, index: 4)
    // 設(shè)置紋理緩沖區(qū)
    renderEncoder.setFragmentTexture(texture, index: 0)
    // 設(shè)置頂點(diǎn)紋理义钉,因?yàn)轫旤c(diǎn)的z軸數(shù)據(jù)需要獲取亮度值争便,所以需要把紋理傳到頂點(diǎn)著色器中
    renderEncoder.setVertexTexture(texture, index: 0)
    
    // 圖元裝配
    if type == .point {
       renderEncoder.drawPrimitives(type: .point, vertexStart: 0, vertexCount: self.vertexData.count / 5)
    } else if type == .triangle {
       renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: self.indexData.count, indexType: MTLIndexType.uint32, indexBuffer: indexBuffer!, indexBufferOffset: 0)
    } else {
       renderEncoder.drawPrimitives(type: .lineStrip, vertexStart: 0, vertexCount: self.vertexData.count / 5)
    }
}

著色器shader:

vertex VertexOut vertex_func(uint vid [[vertex_id]],
                             texture2d<float> diffuse [[texture(0)]],
                             // 對(duì)應(yīng)緩沖區(qū)的下標(biāo)
                             const device VertexIn* vertexIn [[buffer(0)]],
                             const device float4x4& model [[buffer(1)]],
                             const device float4x4& view [[buffer(2)]],
                             const device float4x4& perspective [[buffer(3)]],
                             const device Uniforms& uniforms [[buffer(4)]])
{
    VertexOut outVertex;
    VertexIn inVertex = vertexIn[vid];
    float4 color =  diffuse.sample(s, inVertex.uv);
    // 亮度作為z軸深度值
    inVertex.position.z = 0.3 * (0.299 * color.r + 0.587 * color.g + 0.114 * color.b);
    outVertex.uv = inVertex.uv;
    // MVP矩陣相乘
    outVertex.position = perspective * view * model * float4(inVertex.position);
    outVertex.pointSize = uniforms.pointSizeInPixel;
                                                                 
    return outVertex;
};

fragment float4 fragment_func(VertexOut infrag [[stage_in]], texture2d<float> diffuse [[texture(0)]]) {
    
    // 紋理采樣
    float4 imageColor = diffuse.sample(s, infrag.uv);
    return imageColor;
};

運(yùn)行效果


3D圖片.gif

demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市断医,隨后出現(xiàn)的幾起案子滞乙,更是在濱河造成了極大的恐慌,老刑警劉巖鉴嗤,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斩启,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡醉锅,警方通過(guò)查閱死者的電腦和手機(jī)兔簇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)硬耍,“玉大人垄琐,你說(shuō)我怎么就攤上這事【瘢” “怎么了狸窘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坯认。 經(jīng)常有香客問(wèn)我翻擒,道長(zhǎng)氓涣,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任陋气,我火速辦了婚禮劳吠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巩趁。我一直安慰自己痒玩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布议慰。 她就那樣靜靜地躺著蠢古,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褒脯。 梳的紋絲不亂的頭發(fā)上便瑟,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音番川,去河邊找鬼到涂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛颁督,可吹牛的內(nèi)容都是我干的践啄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沉御,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼屿讽!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起吠裆,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤伐谈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后试疙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體诵棵,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年祝旷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了履澳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怀跛,死狀恐怖距贷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吻谋,我是刑警寧澤忠蝗,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站滨溉,受9級(jí)特大地震影響什湘,放射性物質(zhì)發(fā)生泄漏长赞。R本人自食惡果不足惜晦攒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一闽撤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脯颜,春花似錦哟旗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至矾芙,卻和暖如春舍沙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剔宪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工拂铡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人葱绒。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓感帅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親地淀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子失球,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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