iOS-OpenGLES-入門-三角形

前言

這是一篇iOS-Swift版的OpenGL 入門教程晰房,環(huán)境 Xcode10 + OpenGL ES 3.0编曼。iOS 12 之后已經(jīng)棄用 OpenGL ES, 系統(tǒng)的一些框架全面改成默認(rèn)Metal 支持志衣。MetalOpenGL ES、Core Graphics屬于同一級(jí)的破停,也可以直接從Metal學(xué)起骇扇,后續(xù)會(huì)推出Metal`系列文章 。

構(gòu)建自己的GL ES View

為了更了解OpenGL ES 在iOS是怎么使用的间坐,這里拋棄了GLKit 提供 OpenGL ES 輔助的 GLKView灾挨。

1、首先創(chuàng)建 AGLKView 繼承自 UIView竹宋,這些變量后面會(huì)有介紹

class AGLKView: UIView {
    var myContext:EAGLContext?
    var myColorFrameBuffer:GLuint = 0
    var myColorRenderBuffer:GLuint = 0
    var myProgram:GLuint?
    var positionSlot:GLuint = 0
}

2劳澄、添加CAEAGLLayer支持

    // 只有CAEAGLLayer 類型的 layer 才支持 OpenGl 描繪
    override class var layerClass : AnyClass {
        return CAEAGLLayer.self
    }

3、默認(rèn)的 CALayer 是透明的蜈七,我們需要設(shè)置opaque才能使其可見

    fileprivate func setupLayer() {
        let eagLayer = layer as? CAEAGLLayer

        // CALayer 默認(rèn)是透明的秒拔,必須將它設(shè)為不透明才能讓其可見
        eagLayer?.isOpaque = true
        // 設(shè)置描繪屬性,在這里設(shè)置不維持渲染內(nèi)容以及顏色格式為 RGBA8
        eagLayer?.drawableProperties = [kEAGLDrawablePropertyRetainedBacking:false,kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8]
    }

4飒硅、至此 layer 的配置已經(jīng)就緒砂缩,下面我們來(lái)創(chuàng)建也設(shè)置 OpenGL ES 相關(guān)的東西。首先三娩,我們需要?jiǎng)?chuàng)建OpenGL ES 渲染上下文(在iOS 中對(duì)應(yīng)的實(shí)現(xiàn)為EAGLContext)庵芭,這個(gè) context 管理所有使用OpenGL ES 進(jìn)行描繪的狀態(tài),命令以及資源信息雀监。這與使用 Core Graphics 進(jìn)行描繪必須創(chuàng)建 Core Graphics Context 的道理是一樣双吆。

    fileprivate func setupContext() {
        // 指定 OpenGL 渲染 API 的版本,在這里我們使用 OpenGL ES 3.0
        myContext = EAGLContext(api: .openGLES3)
        
        if myContext == nil {
            print("Failed to initialize OpenGLES 3.0 context")
            return
        }
        // 設(shè)置為當(dāng)前上下文
        if !EAGLContext.setCurrent(myContext) {
            print("Failed to set current OpenGL context")
            return
        }
    }

5会前、創(chuàng)建 RenderBufferFramebuffer
有了上下文好乐,OpenGL還需要在一塊 buffer 上進(jìn)行描繪,這塊 buffer 就是 RenderBuffer(OpenGL ES 總共有三大不同用途的color buffer瓦宜,depth bufferstencil buffer蔚万,這里是最基本的 color buffer)

    fileprivate func setupBuffer() {
        var buffer:GLuint = 0
        glGenRenderbuffers(1, &buffer)
        myColorRenderBuffer = buffer
        glBindRenderbuffer(GLenum(GL_RENDERBUFFER), myColorRenderBuffer)
        // 為 顏色緩沖區(qū) 分配存儲(chǔ)空間
        myContext?.renderbufferStorage(Int(GL_RENDERBUFFER), from: layer as? CAEAGLLayer)
        
        glGenFramebuffers(1, &buffer)
        myColorFrameBuffer = buffer
        // 設(shè)置為當(dāng)前 framebuffer
        glBindFramebuffer(GLenum(GL_FRAMEBUFFER), myColorFrameBuffer)
        // 將 _colorRenderBuffer 裝配到 GL_COLOR_ATTACHMENT0 這個(gè)裝配點(diǎn)上
        glFramebufferRenderbuffer(GLenum(GL_FRAMEBUFFER), GLenum(GL_COLOR_ATTACHMENT0), GLenum(GL_RENDERBUFFER), myColorRenderBuffer)
    }

glGenRenderbuffers 原型為:

func glGenRenderbuffers(_ n: GLsizei, _ renderbuffers: UnsafeMutablePointer<GLuint>!)

它是為 renderbuffer 申請(qǐng)一個(gè)id。參數(shù)n 表示生成 renderbuffer 的個(gè)數(shù)临庇,而 renderbuffers 返回分配給 renderbuffer 的 id笛坦,注意:返回的 id 不會(huì)為0区转,id 0 是OpenGL ES 保留的,我們也不能使用 id 為0的 renderbuffer版扩。
glBindRenderbuffer 的原型為:

func glBindRenderbuffer(_ target: GLenum, _ renderbuffer: GLuint)

這個(gè)函數(shù)將指定 id 的 renderbuffer 設(shè)置為當(dāng)前 renderbuffer。參數(shù) target 必須為 GL_RENDERBUFFER侄泽,參數(shù) renderbuffer 是就是使用 glGenRenderbuffers 生成的 id礁芦。當(dāng)指定 id 的 renderbuffer 第一次被設(shè)置為當(dāng)前 renderbuffer 時(shí),會(huì)初始化該 renderbuffer 對(duì)象悼尾,其初始值為:

widthheight:像素單位的寬和高柿扣,默認(rèn)值為0;

internal format:內(nèi)部格式闺魏,三大 buffer 格式之一 -- color未状,depth or stencil;

Color bit-depth:僅當(dāng)內(nèi)部格式為 color 時(shí)析桥,設(shè)置顏色的 bit-depth司草,默認(rèn)值為0;

Depth bit-depth:僅當(dāng)內(nèi)部格式為 depth時(shí)泡仗,默認(rèn)值為0埋虹;

Stencil bit-depth: 僅當(dāng)內(nèi)部格式為 stencil,默認(rèn)值為0娩怎;

renderbufferStorage 原型為:

func renderbufferStorage(_ target: Int, from drawable: EAGLDrawable?) -> Bool

作用:將可繪制對(duì)象的存儲(chǔ)綁定到OpenGL ES renderbuffer對(duì)象搔课。參數(shù)target必須為GL_RENDERBUFFERdrawable管理renderbuffer的數(shù)據(jù)存儲(chǔ)的對(duì)象截亦,在iOS中爬泥,此參數(shù)的值必須是一個(gè)CAEAGLLayer對(duì)象。
創(chuàng)建Framebuffer Object
framebuffer object 通常也被稱之為 FBO崩瓤,它相當(dāng)于 buffer(color, depth, stencil)的管理者袍啡,三大buffer 可以附加到一個(gè) FBO 上。我們是用 FBO 來(lái)在 off-screen buffer上進(jìn)行渲染谷遂。

glFramebufferRenderbuffer 原型為:

func glFramebufferRenderbuffer(_ target: GLenum, _ attachment: GLenum, _ renderbuffertarget: GLenum, _ renderbuffer: GLuint)

該函數(shù)是將相關(guān) buffer(三大buffer之一)attach到framebuffer上(如果 renderbuffer不為 0葬馋,知道前面為什么說(shuō)glGenRenderbuffers 返回的id 不會(huì)為 0 吧)或從 framebuffer上detach(如果 renderbuffer為 0)。參數(shù) attachment 是指定 renderbuffer 被裝配到那個(gè)裝配點(diǎn)上肾扰,其值是GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT中的一個(gè)畴嘶,分別對(duì)應(yīng) color,depth和 stencil三大buffer集晚。

6窗悯、由于 layer 的寬高變化,導(dǎo)致原來(lái)創(chuàng)建的 renderbuffer不再相符偷拔,我們需要銷毀既有 renderbuffer 和 framebuffer蒋院。

    fileprivate func destoryRenderAndFrameBuffer() {
        //        當(dāng) UIView 在進(jìn)行布局變化之后亏钩,由于 layer 的寬高變化,導(dǎo)致原來(lái)創(chuàng)建的 renderbuffer不再相符欺旧,我們需要銷毀既有 renderbuffer 和 framebuffer姑丑。下面,我們依然創(chuàng)建私有方法 destoryRenderAndFrameBuffer 來(lái)銷毀生成的 buffer
        glDeleteFramebuffers(1, &myColorFrameBuffer)
        myColorFrameBuffer = 0
        glDeleteRenderbuffers(1, &myColorRenderBuffer)
        myColorRenderBuffer = 0
    }

基本準(zhǔn)備工作完畢辞友,下面插播一些渲染理論知識(shí)栅哀。

渲染管線與著色器

管線(pipeline):OpenGL ES在渲染處理過(guò)程中會(huì)順序執(zhí)行一系列操作,這一系列相關(guān)的處理階段就被稱為OpenGL ES 渲染管線称龙。下圖就是OpenGL ES 的管線圖留拾。


pipeline.png

圖中陰影部分的 Vertex Shader 和 Fragment Shader 就是可編程管線■曜穑可動(dòng)態(tài)編程實(shí)現(xiàn)這一功能一般都是腳本提供的痴柔,在OpenGL ES 中也一樣,編寫這樣腳本的能力是由著色語(yǔ)言GLSL提供的疫向。

1咳蔚、Shader (著色器語(yǔ)言)
著色語(yǔ)言是一種類C的編程語(yǔ)言,但不像C語(yǔ)言一樣支持雙精度浮點(diǎn)型(double)鸿捧、字節(jié)型(byte)屹篓、短整型(short)、長(zhǎng)整型(long)匙奴,并且取消了C中的聯(lián)合體(union)堆巧、枚舉類型(enum)、無(wú)符號(hào)數(shù)(unsigned)以及位運(yùn)算等特性泼菌。 著色語(yǔ)言中有許多內(nèi)建的原生數(shù)據(jù)類型以及構(gòu)建數(shù)據(jù)類型谍肤,如:浮點(diǎn)型(float)、布爾型(bool)哗伯、整型(int)荒揣、矩陣型(matrix)以及向量型(vec2、vec3等)等焊刹∠等危總體來(lái)說(shuō),這些數(shù)據(jù)類型可以分為標(biāo)量虐块、向量俩滥、矩陣、采樣器贺奠、結(jié)構(gòu)體以及數(shù)組等霜旧。 shader支持下面數(shù)據(jù)類型:

float  bool  int    基本數(shù)據(jù)類型
vec2                包含了2個(gè)浮點(diǎn)數(shù)的向量
vec3                包含了3個(gè)浮點(diǎn)數(shù)的向量
vec4                包含了4個(gè)浮點(diǎn)數(shù)的向量
ivec2               包含了2個(gè)整數(shù)的向量
ivec3               包含了3個(gè)整數(shù)的向量
ivec4               包含了4個(gè)整數(shù)的向量
bvec2               包含了2個(gè)布爾數(shù)的向量
bvec3               包含了3個(gè)布爾數(shù)的向量
bvec4               包含了4個(gè)布爾數(shù)的向量
mat2                2*2維矩陣
mat3                3*3維矩陣
mat4                4*4維矩陣
sampler1D           1D紋理采樣器
sampler2D           2D紋理采樣器
sampler3D           3D紋理采樣器

2、Vertex Shader (頂點(diǎn)著色器)

Vertex Shader.png

頂點(diǎn)著色器接收的輸入:

Attributes:由 vertext array 提供的頂點(diǎn)數(shù)據(jù)儡率,如空間位置挂据,法向量以清,紋理坐標(biāo)以及頂點(diǎn)顏色,它是針對(duì)每一個(gè)頂點(diǎn)的數(shù)據(jù)崎逃。屬性只在頂點(diǎn)著色器中才有掷倔,片元著色器中沒(méi)有屬性。屬性可以理解為針對(duì)每一個(gè)頂點(diǎn)的輸入數(shù)據(jù)婚脱。

Uniforms:uniforms保存由應(yīng)用程序傳遞給著色器的只讀常量數(shù)據(jù)今魔。在頂點(diǎn)著色器中,這些數(shù)據(jù)通常是變換矩陣障贸,光照參數(shù),顏色等吟宦。由 uniform 修飾符修飾的變量屬于全局變量篮洁,該全局性對(duì)頂點(diǎn)著色器與片元著色器均可見,也就是說(shuō)殃姓,這兩個(gè)著色器如果被連接到同一個(gè)應(yīng)用程序中袁波,它們共享同一份 uniform 全局變量集。因此如果在這兩個(gè)著色器中都聲明了同名的 uniform 變量蜗侈,要保證這對(duì)同名變量完全相同:同名+同類型篷牌,因?yàn)樗鼈儗?shí)際是同一個(gè)變量。
Samplers:一種特殊的 uniform踏幻,用于呈現(xiàn)紋理枷颊。sampler 可用于頂點(diǎn)著色器和片元著色器。

Shader program:由 main 申明的一段程序源碼该面,描述在頂點(diǎn)上執(zhí)行的操作:如坐標(biāo)變換夭苗,計(jì)算光照公式來(lái)產(chǎn)生 per-vertex 顏色或計(jì)算紋理坐標(biāo)。

頂點(diǎn)著色器的輸出:

Varying:varying 變量用于存儲(chǔ)頂點(diǎn)著色器的輸出數(shù)據(jù)隔缀,當(dāng)然也存儲(chǔ)片元著色器的輸入數(shù)據(jù)题造,varying 變量最終會(huì)在光柵化處理階段被線性插值。頂點(diǎn)著色器如果聲明了 varying 變量猾瘸,它必須被傳遞到片元著色器中才能進(jìn)一步傳遞到下一階段界赔,因此頂點(diǎn)著色器中聲明的 varying 變量都應(yīng)在片元著色器中重新聲明同名同類型的 varying 變量。

在頂點(diǎn)著色器階段至少應(yīng)輸出位置信息-即內(nèi)建變量:gl_Position牵触,其它兩個(gè)可選的變量為:gl_FrontFacing 和 gl_PointSize淮悼。

示例代碼:

uniform mat4 uMVPMatrix;                             // 應(yīng)用程序傳入頂點(diǎn)著色器的總變換矩陣
attribute vec4 aPosition;                            // 應(yīng)用程序傳入頂點(diǎn)著色器的頂點(diǎn)位置
attribute vec2 aTextureCoord;                        // 應(yīng)用程序傳入頂點(diǎn)著色器的頂點(diǎn)紋理坐標(biāo)
attribute vec4 aColor                                // 應(yīng)用程序傳入頂點(diǎn)著色器的頂點(diǎn)顏色變量
varying vec4 vColor                                  // 用于傳遞給片元著色器的頂點(diǎn)顏色數(shù)據(jù)
varying vec2 vTextureCoord;                          // 用于傳遞給片元著色器的頂點(diǎn)紋理數(shù)據(jù)
void main()
{
    gl_Position = uMVPMatrix * aPosition;            // 根據(jù)總變換矩陣計(jì)算此次繪制此頂點(diǎn)位置
    vColor = aColor;                                 // 將頂點(diǎn)顏色數(shù)據(jù)傳入片元著色器
    vTextureCoord = aTextureCoord;                   // 將接收的紋理坐標(biāo)傳遞給片元著色器
}

3、Fragment Shader (片元著色器)

Fragment Shader.png

片元管理器接受如下輸入:
Varyings:這個(gè)在前面已經(jīng)講過(guò)了荒吏,頂點(diǎn)著色器階段輸出的 varying 變量在光柵化階段被線性插值計(jì)算之后輸出到片元著色器中作為它的輸入敛惊,即上圖中的 gl_FragCoord,gl_FrontFacing 和 gl_PointCoord绰更。

Uniforms:前面也已經(jīng)講過(guò)瞧挤,這里是用于片元著色器的常量锡宋,如霧化參數(shù),紋理參數(shù)等特恬;

Samples:一種特殊的 uniform执俩,用于呈現(xiàn)紋理。

Shader program:由 main 申明的一段程序源碼癌刽,描述在片元上執(zhí)行的操作役首。

在頂點(diǎn)著色器階段只有唯一的 varying 輸出變量-即內(nèi)建變量:gl_FragColor。
示例代碼:

precision mediump float;                           // 設(shè)置工作精度
varying vec4 vColor;                               // 接收從頂點(diǎn)著色器過(guò)來(lái)的頂點(diǎn)顏色數(shù)據(jù)
varying vec2 vTextureCoord;                        // 接收從頂點(diǎn)著色器過(guò)來(lái)的紋理坐標(biāo)
uniform sampler2D sTexture;                        // 紋理采樣器显拜,代表一幅紋理
void main()
{                                                                                   
    gl_FragColor = texture2D(sTexture, vTextureCoord) * vColor;// 進(jìn)行紋理采樣
}

使用頂點(diǎn)著色器與片元著色器

接著前面的準(zhǔn)備工作衡奥,下面開始鏈接著色器。
主要步驟:
1远荠、創(chuàng)建 shader:glCreateShader
2矮固、裝載 shader:glShaderSource
3、編譯 shader:glCompileShader
4譬淳、刪除 shader:glDeleteShader 釋放資源
著色器加載封裝

struct GLESUtils {
    
    static func loadShaderFile(type:GLenum, fileName:String) -> GLuint {
        guard let path = Bundle.main.path(forResource: fileName, ofType: nil) else {
            print("Error: file does not exist !")
            return 0;
        }
        
        do {
            let shaderString = try String(contentsOfFile: path, encoding: .utf8)
            return GLESUtils.loadShaderString(type: type, shaderString: shaderString)
        } catch {
            print("Error: loading shader file: \(path)")
            return 0;
        }
    }
    
    static func loadShaderString(type:GLenum, shaderString:String) ->GLuint {
        // 創(chuàng)建著色器對(duì)象
        let shaderHandle = glCreateShader(type)
        
        var shaderStringLength: GLint = GLint(Int32(shaderString.count))
        var shaderCString = NSString(string: shaderString).utf8String
        
        /* 把著色器源碼附加到著色器對(duì)象上
         glShaderSource(shader: GLuint, count: GLsizei, String: UnsafePointer<UnsafePointer<GLchar>?>!, length: UnsafePointer<GLint>!)
         shader: 著色器對(duì)象
         count:指定要傳遞的源碼字符串?dāng)?shù)量档址,這里只有一個(gè)
         String:著色器源碼
         length:源碼長(zhǎng)度
         */
        glShaderSource(shaderHandle, GLsizei(1), &shaderCString, &shaderStringLength)
        
        // 編譯著色器
        glCompileShader(shaderHandle)
        
        // 編譯是否成功的狀態(tài) GL_FALSE GL_TRUE
        var compileStatus: GLint = 0
        // 獲取編譯狀態(tài)
        glGetShaderiv(shaderHandle, GLenum(GL_COMPILE_STATUS), &compileStatus)
        
        if compileStatus == GL_FALSE {
            var infoLength: GLsizei = 0
            let bufferLength: GLsizei = 1024
            glGetShaderiv(shaderHandle, GLenum(GL_INFO_LOG_LENGTH), &infoLength)
            
            let info: [GLchar] = Array(repeating: GLchar(0), count: Int(bufferLength))
            var actualLength: GLsizei = 0
            
            // 獲取錯(cuò)誤消息
            glGetShaderInfoLog(shaderHandle, bufferLength, &actualLength, UnsafeMutablePointer(mutating: info))
            NSLog(String(validatingUTF8: info)!)
            print("Error: Colourer Compilation Failure: \(String(validatingUTF8: info) ?? "")")
            return 0
        }
        
        return shaderHandle
    }
    
    static func loanProgram(verShaderFileName:String,fragShaderFileName:String) -> GLuint {
        
        let vertexShader = GLESUtils.loadShaderFile(type: GLenum(GL_VERTEX_SHADER), fileName: verShaderFileName)
        
        if vertexShader == 0 {return 0}
        
        let fragmentShader = GLESUtils.loadShaderFile(type: GLenum(GL_FRAGMENT_SHADER), fileName: fragShaderFileName)
        
        if fragmentShader == 0 {
            glDeleteShader(vertexShader)
            return 0
        }
        
        // 創(chuàng)建著色器程序?qū)ο?        let programHandel = glCreateProgram()
        
        if programHandel == 0 {return 0}
        
        // 將著色器附加到程序上
        glAttachShader(programHandel, vertexShader)
        glAttachShader(programHandel, fragmentShader)
        
        // 鏈接著色器程序
        glLinkProgram(programHandel)
        
        // 獲取鏈接狀態(tài)
        var linkStatus: GLint = 0
        glGetProgramiv(programHandel, GLenum(GL_LINK_STATUS), &linkStatus)
        if linkStatus == GL_FALSE {
            var infoLength: GLsizei = 0
            let bufferLenght: GLsizei = 1024
            glGetProgramiv(programHandel, GLenum(GL_INFO_LOG_LENGTH), &infoLength)
            
            let info: [GLchar] = Array(repeating: GLchar(0), count: Int(bufferLenght))
            var actualLenght: GLsizei = 0
            
            // 獲取錯(cuò)誤消息
            glGetProgramInfoLog(programHandel, bufferLenght, &actualLenght, UnsafeMutablePointer(mutating: info))
            print("Error: Colorer Link Failed: \(String(validatingUTF8: info) ?? "")")
            return 0
        }
        
        // 釋放資源
        glDeleteShader(vertexShader)
        glDeleteShader(fragmentShader)
        
        return programHandel
    }
}

編寫著色腳本
頂點(diǎn)著色器 shaderv.glsl

attribute vec4 vPosition;    
attribute vec4 a_Color;      

varying lowp vec4 frag_Color;
void main(void)
{
    frag_Color = a_Color;
    gl_Position = vPosition;
}

片元著色器shaderf.glsl

varying lowp vec4 frag_Color;

void main()
{
    gl_FragColor = frag_Color;
}

編譯著色器

    fileprivate func setupProgram() {
        myProgram = GLESUtils.loanProgram(verShaderFileName: "shaderv.glsl", fragShaderFileName: "shaderf.glsl")
        guard let myProgram = myProgram else {
            return
        }
        
        glUseProgram(myProgram)
    
        positionSlot = GLuint(glGetAttribLocation(myProgram, "vPosition"))
        colorSlot = GLuint(glGetAttribLocation(myProgram, "a_Color"))
    }

繪制

    fileprivate func render() {
        
        glClearColor(0, 1.0, 0, 1.0)
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
        
        glViewport(0, 0, GLsizei(frame.size.width), GLsizei(frame.size.height))
        
        let vertices: [GLfloat] = [
            0, 0.5, 0,
            -0.5, -0.5, 0,
            0.5, -0.5, 0
        ]
        
        let colors: [GLfloat] = [
            1, 0, 0, 1,
            0, 1, 0, 1,
            0, 0, 1, 1
        ]
        
        // 加載頂點(diǎn)數(shù)據(jù)
        glVertexAttribPointer(positionSlot, 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, vertices )
        glEnableVertexAttribArray(positionSlot)
        
        // 加載顏色數(shù)據(jù)
        glVertexAttribPointer(colorSlot, 4, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, colors )
        glEnableVertexAttribArray(colorSlot)
        
        // 繪制
        glDrawArrays(GLenum(GL_TRIANGLES), 0, 3)
        
        myContext?.presentRenderbuffer(Int(GL_RENDERBUFFER))
    }

glViewport 表示渲染 surface 將在屏幕上的哪個(gè)區(qū)域呈現(xiàn)出來(lái),然后我們創(chuàng)建一個(gè)三角形頂點(diǎn)數(shù)組邻梆,通過(guò) glVertexAttribPointer 將三角形頂點(diǎn)數(shù)據(jù)裝載到 OpenGL ES 中并與 vPositon 關(guān)聯(lián)起來(lái)守伸,最后通過(guò) glDrawArrays 將三角形圖元渲染出來(lái)。

效果圖

效果圖.png

這里可以下載demo代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浦妄,一起剝皮案震驚了整個(gè)濱河市尼摹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌校辩,老刑警劉巖窘问,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宜咒,居然都是意外死亡惠赫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門故黑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)儿咱,“玉大人,你說(shuō)我怎么就攤上這事场晶』觳海” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵诗轻,是天一觀的道長(zhǎng)钳宪。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么吏颖? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任搔体,我火速辦了婚禮,結(jié)果婚禮上半醉,老公的妹妹穿的比我還像新娘疚俱。我一直安慰自己,他們只是感情好缩多,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布呆奕。 她就那樣靜靜地躺著,像睡著了一般衬吆。 火紅的嫁衣襯著肌膚如雪梁钾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天逊抡,我揣著相機(jī)與錄音陈轿,去河邊找鬼。 笑死秦忿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蛾娶。 我是一名探鬼主播灯谣,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蛔琅!你這毒婦竟也來(lái)了胎许?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤罗售,失蹤者是張志新(化名)和其女友劉穎辜窑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寨躁,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡穆碎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了职恳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片所禀。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖放钦,靈堂內(nèi)的尸體忽然破棺而出色徘,到底是詐尸還是另有隱情,我是刑警寧澤操禀,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布褂策,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏斤寂。R本人自食惡果不足惜耿焊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扬蕊。 院中可真熱鬧搀别,春花似錦、人聲如沸尾抑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)再愈。三九已至榜苫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間翎冲,已是汗流浹背垂睬。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抗悍,地道東北人驹饺。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像缴渊,于是被迫代替她去往敵國(guó)和親赏壹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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