IOS渲染圖片OpenGLES(五)

前言

前面學(xué)習(xí)了opengl es渲染管線基跑,可編程語言GLSL,常用的opengl es函數(shù)歹篓,有了這些基礎(chǔ),現(xiàn)在就可以實(shí)現(xiàn)如何將一張圖片渲染到屏幕上了揉阎。圖片庄撮,最終在內(nèi)存中的表現(xiàn)形式就是由一個個像素組成的,而每個像素又由RGB組成毙籽,其實(shí)opengl es最終渲染的像素是由RGBA四個通道組成的洞斯。

opengl es系列文章

opengl es之-基礎(chǔ)概念(一)
opengl es之-GLSL語言(二)
opengl es之-GLSL語言(三)
opengl es之-常用函數(shù)介紹(四)
opengl es之-渲染兩張圖片(五)
opengl es之-在圖片上添加對角線(六)
opengl es之-離屏渲染簡介(七)
opengl es之-CVOpenGLESTextureCache介紹(八)
opengl es之-播放YUV文件(九)

需求

渲染兩張圖片,并將它們混合坑赡,最終呈現(xiàn)到屏幕上

準(zhǔn)備

引入頭文件烙如,在ios平臺,上下文和窗口管理的是由EAGL實(shí)現(xiàn)的毅否,這里例子中的opengl es版本為2.0

#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/EAGL.h>

創(chuàng)建上下文環(huán)境

基于UIView創(chuàng)建上下文環(huán)境亚铁,首先要重寫class方法,返回return [CAEAGLLayer class];

// 必不可少螟加,否則無法成功創(chuàng)建opengl es環(huán)境
+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

1徘溢、首先設(shè)置當(dāng)前UIView的layer的屬性,這里要注意opaque最好為NO捆探,這樣可以降低性能消耗甸昏。
2、contentsScale系統(tǒng)默認(rèn)為1.0徐许,這里最好設(shè)置為[UIScreen mainScreen].scale施蜜,否則由EAGL創(chuàng)建的上下文窗口真正的像素大小就是ios中基于point單位的大小了,這樣很明顯會造成最終渲染的圖片壓縮
3雌隅、drawableProperties屬性按照下面的例子來進(jìn)行即可翻默,沒什么好講的

- (void)setupContext
{
    CAEAGLLayer calayer = (CAEAGLLayer*)self.layer;
    calayer.opaque = NO; //CALayer默認(rèn)是透明的缸沃,透明的對性能負(fù)荷大,故將其關(guān)閉
    // 表示屏幕的scale修械,默認(rèn)為1趾牧;會影響后面renderbufferStorage創(chuàng)建的renderbuffer的長寬值;
    // 它的長寬值等于=layer所在視圖的邏輯長寬*contentsScale
    // 最好這樣設(shè)置肯污,否則后面按照紋理的實(shí)際像素渲染翘单,會造成圖片被放大。
    calayer.contentsScale = [UIScreen mainScreen].scale;
    calayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                  // 由應(yīng)用層來進(jìn)行內(nèi)存管理
                                  @(NO),kEAGLDrawablePropertyRetainedBacking,
                                  kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat,
                                  nil];
    
    
    // 創(chuàng)建指定OpenGL ES的版本的上下文蹦渣,一般選擇2.0的版本
    _context = [[EAGLContext alloc] initWithAPI:version];
    // 當(dāng)為yes的時候哄芜,所有關(guān)于Opengl渲染,指令真正執(zhí)行都在另外的線程中柬唯。NO认臊,則關(guān)于渲染,指令真正執(zhí)行在當(dāng)前調(diào)用的線程
    // 對于多核設(shè)備有大的性能提升
    _context.multiThreaded = yesOrnot;
    
    _memoryPool = CMMemoryPoolCreate(NULL);
    // 收到內(nèi)存不足警告后锄奢,需要清除部分內(nèi)存
    __unsafe_unretained __typeof__ (self) weakSelf = self;
    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification
                                                      object:nil
                                                       queue:nil
                                                  usingBlock:^(NSNotification *notification) {
                                                      __typeof__ (self) strongSelf = weakSelf;
                                                      if (strongSelf) {
                                                          CVOpenGLESTextureCacheFlush([strongSelf coreVideoTextureCache], 0);
                                                      }
                                                  }];
}

備注:這里特別要說明的就是上面代碼中有一個CMMemoryPoolCreate()創(chuàng)建的內(nèi)存池失晴,該函數(shù)來自于<CoreMedia/CoreMedia.h>框架,其實(shí)正常渲染一張圖片不需要這個東西拘央,對于渲染視頻才需要這個內(nèi)存池(可以避免重復(fù)創(chuàng)建內(nèi)存)涂屁,后面在說這個,這里可以先略過

創(chuàng)建幀緩沖區(qū)和渲染緩沖區(qū)

先說兩個概念灰伟,幀緩沖區(qū)和渲染緩沖區(qū)

幀緩沖區(qū):在opengl es中也成為FBO拆又,即用于渲染物體(比如渲染一個白色的正方形,比如將一張圖片渲染出來)的一塊內(nèi)存(可能是GPU或者內(nèi)存條中內(nèi)存)
渲染緩沖區(qū):在opengl es中袱箱,其實(shí)將物體渲染到幀緩沖區(qū)整個渲染過程就已經(jīng)結(jié)束了,渲染緩沖區(qū)是為了將物體再呈現(xiàn)到屏幕上時的一個過渡緩沖區(qū)义矛,系統(tǒng)要將物體渲染到屏幕上會先將幀緩沖區(qū)中內(nèi)容拷貝到渲染緩沖區(qū)发笔,然后在呈現(xiàn)到屏幕上

具體代碼如下
創(chuàng)建幀緩沖區(qū)

    // 創(chuàng)建frameBuffer
    glGenFramebuffers(1, &_frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);

創(chuàng)建渲染緩沖區(qū)

    // 創(chuàng)建渲染buffer
    glGenRenderbuffers(1, &_renderBuffer);    //第一個參數(shù) 創(chuàng)建buffer的數(shù)量 第二個    參數(shù) 創(chuàng)建的bufferId(為0表示創(chuàng)建失敗)
    // 綁定剛剛創(chuàng)建的buffer為GL_RENDERBUFFER類型。
    glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer); // 第一個參數(shù)凉翻,buffer的類型了讨,要與前面創(chuàng)建的buffer類型對應(yīng)。第二個參數(shù)制轰,前面創(chuàng)建的buffer id

將渲染緩沖區(qū)關(guān)聯(lián)到幀緩沖區(qū)前计,并且由EAGL分配上下文

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
    // 為render buffer 開辟存儲空間 重要;EAGLContext必須設(shè)置正確,否則下面會出現(xiàn)36054錯誤
    [_context.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];

    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_renderWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_renderHeight);

glFramebufferRenderbuffer()將渲染緩沖區(qū)和幀緩沖區(qū)關(guān)聯(lián)了起來垃杖,這樣渲染結(jié)果才能沖這個指定的幀緩沖區(qū)拷貝到渲染緩沖區(qū)中(此過程由系統(tǒng)完成)男杈;renderbufferStorage則是為渲染緩沖區(qū)分配了內(nèi)存,可以看到调俘,其實(shí)由glGen glGenRenderbuffers()和glGenFramebuffers()函數(shù)并沒有真正創(chuàng)建內(nèi)存伶棒,它只是生產(chǎn)了一個可以用來指向這個內(nèi)存的句柄(也可以成為內(nèi)存地址)

加載著色器程序

著色器程序由頂點(diǎn)著色器程序和片元著色器程序組成旺垒,頂點(diǎn)著色器程序用于渲染管線中的頂點(diǎn)確定階段,片元著色器程序則用于片元處理階段肤无。完成一個著色器程序需要經(jīng)過編譯GLSL源代碼先蒋,鏈接頂點(diǎn)著色器和片元著色器,執(zhí)行著色器程序三個步驟宛渐。

1竞漾、編寫GLSL代碼
這里的頂點(diǎn)著色器和片元著色器代碼如下:
頂點(diǎn)著色器;這里定義了position變量,它表示最終要渲染的圖元的頂點(diǎn)的坐標(biāo); texcoord表示要加載的圖片的紋理的坐標(biāo)

 attribute vec4 position;
 attribute vec2 texcoord;
 
 varying highp vec2 v_texcoord;
 
 void main()
 {
     gl_Position = position;
     v_texcoord = texcoord.xy;
 }

片元著色器
這個片元著色器的含義就是窥翩,將兩張圖片混合

 uniform sampler2D inputImageTexture1;
 uniform sampler2D inputImageTexture2;
 
 varying highp vec2 v_texcoord;
 
 void main() {
     gl_FragColor = texture2D(inputImageTexture1, v_texcoord)+texture2D(inputImageTexture2, v_texcoord);
 }

2业岁、編譯GLSL源代碼(頂點(diǎn)著色器和片元著色器方式一樣)

- (BOOL)compileShader:(GLenum)type sString:(NSString*)sString shader:(GLuint*)shaderRet
{
    if (sString.length == 0) {
        NSLog(@"著色器程序不能為nil");
        return NO;
    }
    
    const GLchar *sources = (GLchar*)[sString UTF8String];
    
    // 創(chuàng)建著色器程序句柄
    GLuint shader = glCreateShader(type);
    if (shader == 0 || shader == GL_INVALID_ENUM) {
        NSLog(@"glCreateShader fail");
        return NO;
    }
    
    // 為著色器句柄添加GLSL代碼;可一次添加多個代碼,一般添加一個鳍烁,若添加一個源代碼則最后一個參數(shù)為NULL 即可
    glShaderSource(shader, 1, &sources, NULL);
    // 編譯該GLSL代碼
    glCompileShader(shader);
    
    // 打印出編譯過程中產(chǎn)生的GLSL的日志
#ifdef DEBUG
    GLint logLenght;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLenght);
    if (logLenght > 0) {
        GLchar *log = (GLchar*)malloc(logLenght);
        glGetShaderInfoLog(shader, logLenght, &logLenght, log);
        NSLog(@"Shader compile log:\n%s", log);
        free(log);
    }
#endif
    // 檢查編譯結(jié)果
    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if (status == GL_FALSE) {
        NSLog(@"compile fail %d",status);
        return NO;
    }
    
    *shaderRet = shader;
    return YES;
}

可以看到大致分為這幾個步驟
a叨襟、由glCreateShader(type)函數(shù)創(chuàng)建著色器句柄,type有兩種類型:GL_VERTEX_SHADER(頂點(diǎn)著色器)和GL_FRAGMENT_SHADER(片元著色器),他們分別是opengl es的常量
b幔荒、glShaderSource(shader, 1, &sources, NULL);將源程序載入到上面創(chuàng)建的著色器句柄中
c糊闽、glCompileShader(shader);編譯程序
d、【可選】爹梁,可以打印編譯過程中的日志右犹,測試階段用來調(diào)試GLSL代碼的正確性
e、glGetShaderiv(shader, GL_COMPILE_STATUS, &status);檢查編譯是否正確
3姚垃、生成最終的著色器程序

// 創(chuàng)建一個最終程序句柄念链;它由頂點(diǎn)著色器和片段著色器組成
        filterProgram = glCreateProgram();
        // 分別添加頂點(diǎn)著色器程序和片段著色器程序
        glAttachShader(filterProgram, vShader);
        glAttachShader(filterProgram, fShader);
        
        /** 連接成一個最終程序
         *  當(dāng)這一步完成之后,app就可以和opengl es進(jìn)行交互了积糯,
         *  1掂墓、比如app獲取glsl中的頂點(diǎn)變量,設(shè)置幾何圖元的頂點(diǎn)以確定圖元的最終形狀
         *  2看成、app獲取glsl中的紋理變量君编,然后將本地圖片傳遞給這個紋理變量以實(shí)現(xiàn)將圖片傳遞給顯卡進(jìn)行渲染和其
         *  它處理
         */
        glLinkProgram(filterProgram);
        
        // 輸出連接過程中的日志
        GLint status;
        glValidateProgram(filterProgram);
#ifdef DEBUG
        GLint logLength;
        glGetProgramiv(filterProgram, GL_INFO_LOG_LENGTH, &logLength);
        if (logLength > 0){
            GLchar *log = (GLchar *)malloc(logLength);
            glGetProgramInfoLog(filterProgram, logLength, &logLength, log);
            NSLog(@"Program validate log:\n%s", log);
            free(log);
        }
#endif
        
        // 檢查連接結(jié)果
        glGetProgramiv(filterProgram, GL_LINK_STATUS, &status);
        if (status == GL_FALSE) {
            NSLog(@"link program fail %d",status);
            return nil;
        }

由如下幾個步驟完成
a、filterProgram = glCreateProgram();創(chuàng)建最終著色器程序句柄
b川慌、glAttachShader(filterProgram, vShader);加載頂點(diǎn)著色器和片元著色器到上面的句柄中
c吃嘿、glLinkProgram(filterProgram);執(zhí)行鏈接
d、【可選】打印鏈接過程中日志梦重,測試階段使用
e兑燥、glGetProgramiv(filterProgram, GL_INFO_LOG_LENGTH, &logLength);檢查連接是否成功
4、執(zhí)行著色器程序
著色器程序必須得執(zhí)行才能最終使用

glUseProgram(filterProgram);

渲染圖片到幀緩沖區(qū)

開辟渲染緩沖區(qū)域

glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, _renderWidth, _renderHeight);

int w1,h1;
int w2,h2;
void *image1 = [CGImageUtils rgbaImageDataForPath:path1 width:&w1 height:&h1];
NSLog(@"圖片的寬和高 w1 %d h1 %d",w1,h1);
void *image2 = [CGImageUtils rgbaImageDataForPath:path2 width:&w2 height:&h2];
NSLog(@"圖片的寬和高 w2 %d h2 %d",w2,h2);

1琴拧、glViewport(0, 0, _renderWidth, _renderHeight);這個函數(shù)很重要降瞳,在渲染到幀緩沖區(qū)之前,必須開辟一塊渲染區(qū)域蚓胸,否則無法渲染
2力崇、rgbaImageDataForPath是我自己封裝的一個方法斗塘,具體參考Demo,將一張圖片轉(zhuǎn)換成RGBA像素數(shù)據(jù)

給著色器程序傳頂點(diǎn)和圖片

[self.blendTwoTextureProgram use];
GLuint position = [self.blendTwoTextureProgram attribLocationForName:@"position"];
GLuint texcoord = [self.blendTwoTextureProgram attribLocationForName:@"texcoord"];
GLuint s_texture1 = [self.blendTwoTextureProgram uniformLocationForName:@"inputImageTexture1"];
GLuint s_texture2 = [self.blendTwoTextureProgram uniformLocationForName:@"inputImageTexture2"];

glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, verData1);
glEnableVertexAttribArray(position);
glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, 0, uvData);
glEnableVertexAttribArray(texcoord);

GLint active,textureSize;
glGetIntegerv(GL_ACTIVE_TEXTURE, &active);
NSLog(@"該設(shè)備支持的最大紋理單元數(shù)目 %d",active);
// 超過此大小則必須先壓縮再傳給opengl es亮靴,否則opengl es無法渲染
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &textureSize);
NSLog(@"該設(shè)備支持的紋理最大的長或?qū)?%d",textureSize);

glActiveTexture(GL_TEXTURE1);
glGenTextures(1,&texturexId2);
glBindTexture(GL_TEXTURE_2D, texturexId2);
// 此方法必須有馍盟,第二個參數(shù)要與前面激活的紋理單元數(shù)對應(yīng),前面是GL_TEXTURE1茧吊,這里就是1
glUniform1i(s_texture2, 1);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // S方向上的貼圖模式
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // T方向上的貼圖模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)w2, (int)h2, 0, GL_RGBA, GL_UNSIGNED_BYTE, image2);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);


/** 紋理的工作原理
 *  紋理單元用來將app中圖片像素數(shù)據(jù)傳遞給OpenGL ES,對應(yīng)片段著色器中的uniform sampler2D變量贞岭。它是一個全局對象,每臺設(shè)備都會創(chuàng)建數(shù)個獨(dú)立的紋理單元搓侄,要使用
 *  一個紋理單元之前必須先激活它才能使用瞄桨,激活之后還需要綁定,則才可以使用多個紋理對象;
 *  如果應(yīng)用中只有一個激活的紋理讶踪,則不需要調(diào)用glBindTexture()函數(shù)也可以芯侥,但是如果要使用多個紋理,則必須要調(diào)用glBindTexture()進(jìn)行區(qū)分乳讥,否則會造成數(shù)據(jù)被覆蓋
 */
glActiveTexture(GL_TEXTURE0);
glGenTextures(1,&texturexId1);
NSLog(@"texturexId %d",texturexId1);
glBindTexture(GL_TEXTURE_2D, texturexId1);
// 此方法必須有柱查,第二個參數(shù)要與前面激活的紋理單元數(shù)對應(yīng),前面是GL_TEXTURE0云石,這里就是0
glUniform1i(s_texture1, 0);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // S方向上的貼圖模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // T方向上的貼圖模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 1字節(jié)對齊唉工,效率比較低,默認(rèn)是4汹忠;必須在glTexImage2D前面設(shè)置
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// 因?yàn)閕mage1像素數(shù)據(jù)本身就是由RGBA構(gòu)成的淋硝,所以這里format只能是GL_RGBA,如果傳其它值則會造成數(shù)據(jù)不對稱而出錯
// 比如進(jìn)行視頻渲染是宽菜,分別傳遞YUV三個分量的像素給Opengl es谣膳,則那個時候format取值就必須為GL_LUMINANCE或者GL_ALPHA等只有一個字節(jié)的
// 最后一個參數(shù)可以為NULL,只是分配一塊w1xh1的內(nèi)存空間铅乡,當(dāng)為NULL時继谚,如果前后調(diào)用了CGContextDrawImage,這個函
// 數(shù)的繪制結(jié)果會傳給glTexImage2D()開辟的內(nèi)存空間
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w1, h1, 0, GL_RGBA, GL_UNSIGNED_BYTE, image1);    // border參數(shù)為1 渲染不出來

// 釋放圖片像素數(shù)據(jù)
if (image1) {
    free(image1);
}
if (image2) {
    free(image2);
}

1隆判、blendTwoTextureProgram是定義的著色器變量對象犬庇,通過它可以獲取著色器程序中頂點(diǎn)變量(即前面attribute vec4 position;)僧界、紋理坐標(biāo)變量(attribute vec2 texcoord;)侨嘀、紋理單元變量(uniform sampler2D inputImageTexture1;)的地址
glVertexAttribPointer()函數(shù)用于給頂點(diǎn)變量和紋理坐標(biāo)變量傳值,這里verData1為:

static float verData1[8] = {
-1.0f,-1.0f,// 左下角
1.0f,-1.0f, // 右下角
-1.0f,1.0f, // 左上角
1.0f,1.0f,  // 右上角
};
static float uvData[8] = {
0.0f, 1.0f, // 左下角
1.0f, 1.0f, // 右下角
0.0f, 0.0f, // 左上角
1.0f, 0.0f, // 右上角
};

2捂襟、glActiveTexture(GL_TEXTURE1);激活紋理單元1咬腕,表示使用紋理單元1來處理第二張圖
glGenTextures(1,&texturexId2);生成一個紋理句柄
glBindTexture(GL_TEXTURE_2D, texturexId2);將紋理句柄綁定到紋理單元1的GL_TEXTURE_2D類型紋理對象上,那么后續(xù)通過glTexxxx()函數(shù)設(shè)置的參數(shù)都是針對這個紋理句柄設(shè)置的
glUniform1i()指定前面片元著色中變量uniform sampler2D inputImageTexture1;和uniform sampler2D inputImageTexture2;分別使用哪個紋理單元(在opengl es一般都有數(shù)百個紋理單元)來處理圖片葬荷,
glTexImage2D()用來給著色器傳圖片

開始渲染

// 確定要繪制的幾何圖形涨共,該指令執(zhí)行后才opengl es指令才開始真正的執(zhí)行纽帖;處于渲染管線的第一階段,每次glDrawArrays()的調(diào)用
    // 代表前面所有的指令是一次完整的渲染
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

將渲染結(jié)果呈現(xiàn)到屏幕上

// 即光柵化階段举反,它與opengl es的渲染是獨(dú)立的懊直,不相干的。當(dāng)它會將前面所有的渲染結(jié)果呈現(xiàn)到屏幕上;
[_context.context presentRenderbuffer:GL_RENDERBUFFER];

項(xiàng)目地址

具體可參考項(xiàng)目中GLView的方法火鼻,里面有很詳細(xì)的注釋

// 一次渲染多張圖片室囊;同時渲染兩張張圖片,效果是混合這兩張圖片
- (void)renderTextureWithPath:(NSString*)path path2:(NSString*)path2;

Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末魁索,一起剝皮案震驚了整個濱河市融撞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粗蔚,老刑警劉巖尝偎,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鹏控,居然都是意外死亡致扯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門牧挣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來急前,“玉大人,你說我怎么就攤上這事瀑构●烧耄” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵寺晌,是天一觀的道長世吨。 經(jīng)常有香客問我,道長呻征,這世上最難降的妖魔是什么耘婚? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮陆赋,結(jié)果婚禮上沐祷,老公的妹妹穿的比我還像新娘。我一直安慰自己攒岛,他們只是感情好赖临,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著灾锯,像睡著了一般兢榨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天吵聪,我揣著相機(jī)與錄音凌那,去河邊找鬼。 笑死吟逝,一個胖子當(dāng)著我的面吹牛帽蝶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播块攒,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嘲碱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了局蚀?” 一聲冷哼從身側(cè)響起麦锯,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎琅绅,沒想到半個月后扶欣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡千扶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年料祠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澎羞。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡髓绽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妆绞,到底是詐尸還是另有隱情顺呕,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布括饶,位于F島的核電站株茶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏图焰。R本人自食惡果不足惜启盛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望技羔。 院中可真熱鬧僵闯,春花似錦、人聲如沸藤滥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽超陆。三九已至牺弹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間时呀,已是汗流浹背张漂。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谨娜,地道東北人航攒。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像趴梢,于是被迫代替她去往敵國和親漠畜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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