OpenGL ES案例03 - 使用GLSL完成紋理圖片加載

案例:根據(jù)對GLSL語言的理解蒂誉,自定義一個頂點著色器和一個片元著色器怖侦,使用著色器API完成紋理的加載篡悟。
進階:解決紋理倒置問題。
效果如下:


翻轉前效果

翻轉后效果

準備工作

  1. 新建iOS應用工程匾寝,修改當前controller的view搬葬。將原來的view繼承于UIView改成繼承于HView。
  2. 自定義一個HVIew類艳悔,后續(xù)繪制圖片在該類中完成急凰。
  3. 新建頂點著色器文件和片元著色器文件。
    3.1 command + N猜年,開始新建文件抡锈。
    3.2 選擇iOS->Other->Empty,新建兩個空文件乔外,分別命名為:shaderv.vsh床三、shaderf.fsh

至此準備工作完成,接下來就開始編碼工作杨幼。

自定義著色器

自定義著色器本質上其實是一個字符串撇簿,但是在Xcode的編寫過程沒有任何錯誤提示,因此差购,在編寫過程中需要格外仔細补疑。

  1. 頂點著色器shaderv.vsh
  • 定義兩個attribute修飾符修飾的變量,分別表示頂點坐標和紋理坐標
  • 定義一個varying修飾符修飾的變量歹撒,用于將紋理坐標從頂點著色器傳遞給片元著色器
  • main函數(shù)莲组,在該函數(shù)內給內建變量gl_Position賦值。若頂點坐標不需要變換暖夭,則直接將頂點坐標賦值給內建變量gl_Position锹杈。若頂點坐標需要進行變換,則將變換后的結果賦值給內建變量gl_Position迈着。
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main(){
    varyTextCoord = textCoordinate;
    gl_Position = position;
}

2.片元著色器shaderf.fsh

  • 指定片元著色器中float類型的精度竭望,如果不寫,可能會報一些異常錯誤
  • 定義一個與頂點著色器橋接的紋理坐標裕菠,寫法必須同在頂點著色器寫法一致咬清,否則將無法收到從頂點著色器傳遞過來的數(shù)據(jù)
  • 定義一個unifom修飾符修飾的變量,用于獲取紋理坐標上每個像素點的紋素。
  • main函數(shù)旧烧,在函數(shù)內給內建變量gl_FragColor賦值影钉。通過texture2D內建函數(shù)獲取當前顏色值,它有兩個參數(shù):參數(shù)1:紋理圖片掘剪;參數(shù)2:紋理坐標平委,返回值:vec4類型的顏色值。當顏色不需要進行修改時夺谁,可直接將vec4類型的顏色值賦值給內建變量gl_FragColor廉赔。當顏色需要修改時,將最終修改的結果賦值給內建變量gl_FragColor匾鸥。
precision highp float;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main(){
    gl_FragColor = texture2D(colorMap, varyTextCoord);
}

初始化

1. 創(chuàng)建圖層

1.1 圖層主要是顯示OpenGL ES繪制內容的載體蜡塌。它的創(chuàng)建有兩種方式:
  • 直接使用當前view的layer。但是view的layer是繼承于CALayer勿负,需要重寫類方法layerClass岗照,使其繼承于CAEAGLLayer
  • 直接使用[[CAEAGLLayer alloc] init]方法創(chuàng)建一個CAEAGLLayer類型的圖層笆环,并將新創(chuàng)建的圖層添加到當前圖層上攒至。
self. myEagLayer = (CAEAGLLayer*)self.layer;
+ (Class)layerClass{
    return [CAEAGLLayer class];
}
1.2 設置scale,這里設置當前view的scale與屏幕的scale一樣大
[self setContentScaleFactor:[[UIScreen mainScreen] scale]];
1.3 設置描述屬性躁劣,這里設置不維持渲染內容以及顏色格式為RGBA8
  • kEAGLDrawablePropertyRetainedBacking:表示繪圖表面顯示后迫吐,是否保留其內容,true-保留账忘,false-不保留志膀。
  • kEAGLDrawablePropertyColorFormat:可繪制表面的內部顏色緩存區(qū)格式,這個key對應的值是一個NSString指定特定顏色緩存區(qū)對象鳖擒。默認是kEAGLColorFormatRGBA8溉浙;
顏色緩沖區(qū)格式 描述
kEAGLColorFormatRGBA8 32位RGBA的顏色,4*8=32位
kEAGLColorFormatRGB565 16位RGB的顏色
kEAGLColorFormatSRGBA8 sRGB代表了標準的紅蒋荚、綠戳稽、藍,即CRT顯示器期升、LCD顯示器惊奇、投影機、打印機以及其他設備中色彩再現(xiàn)所使用的三個基本色素播赁。sRGB的色彩空間基于獨立的色彩坐標颂郎,可以使色彩在不同的設備使用傳輸中對應于同一個色彩坐標體系,而不受這些設備各自具有的不同色彩坐標的影響容为。
self.myEagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:@false, kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatSRGBA8, kEAGLDrawablePropertyColorFormat, nil];

2. 創(chuàng)建上下文

上下文主要用來保存OpenGL ES的狀態(tài)乓序,是一個狀態(tài)機寺酪,不論GLKit還是GLSL,都需要使用context替劈。
2.1 創(chuàng)建上下文寄雀,并指定OpenGL ES渲染API的版本號

self.myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];

2.2 設置當前上下文

[EAGLContext setCurrentContext:self.myContext];

3. 清空緩沖區(qū)

清除緩沖區(qū)的殘留數(shù)據(jù),防止其它無用數(shù)據(jù)對繪制效果造成影響

//清空渲染緩存區(qū)
glDeleteBuffers(1, &_myColorRenderBuffer);
self.myColorRenderBuffer = 0;
    
//清空幀緩存區(qū)
glDeleteBuffers(1, &_myColorFrameBuffer);
self.myColorFrameBuffer = 0;

4. 設置緩沖區(qū)

設置緩沖區(qū)包括設置RenderBuffer和FrameBuffer抬纸。

  1. RenderBuffer:是一個通過應用分配的2D圖像緩沖區(qū),需要附著在FrameBuffer上耿戚。
    1.1 RenderBuffer有3種緩沖區(qū)

    • 深度緩沖區(qū)(Depth Buffer):存儲深度值等
    • 紋理緩沖區(qū)(Depth Buffer):存儲紋理坐標中對應的紋素湿故、顏色值等
    • 模板緩沖區(qū)(Stencil Buffer):存儲模板等

    1.2 設置RenderBuffer

    • 定義一個緩存區(qū)ID
    • 申請一個緩沖區(qū)標志
    • 將緩沖區(qū)標識綁定到GL_RENDERBUFFER
    • 綁定一個可繪制對象(layer)的存儲到一個OpenGL ES RenderBuffer對象
-(void)setupRenderBuffer{
    //1.定義一個緩存區(qū)ID
    GLuint buffer;
    //2.申請一個緩存區(qū)標志
    glGenRenderbuffers(1, &buffer);
    
    self.myColorRenderBuffer = buffer;
    
    glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
    
    [self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];
}
  1. FrameBuffer:是一個收集顏色、深度膜蛔、模板緩沖區(qū)的附著點坛猪,簡稱FBO,即是一個管理者皂股,用來管理RenderBuffer墅茉,且FrameBuffer沒有實際的存儲功能,真正實現(xiàn)存儲的是RenderBuffer呜呐。
    2.1 FrameBuffer有3個附著點

    • 顏色附著點(Color Attachment):管理紋理就斤、顏色緩沖區(qū)
    • 深度附著點(depth Attachment):管理深度緩沖區(qū),會根據(jù)當前深度緩沖中的值修改顏色緩沖中的內容
    • 模板附著點(Stencil Attachment):管理模板緩沖區(qū)

    2.2 設置FrameBuffer

    • 定義一個緩存區(qū)ID
    • 申請一個緩沖區(qū)標志
    • 將緩沖區(qū)標識綁定到GL_FRAMEBUFFER
    • 通過FrameBuffer來管理RenderBuffer蘑辑,將RenderBuffer附著到FrameBuffer的GL_COLOR_ATTACHMENT0附著點上洋机。
-(void)setupFrameBuffer{
    GLuint buffer;
    glGenFramebuffers(1, &buffer);
    
    self.myColorFrameBuffer = buffer;
    
    glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
    
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
}

注意點:綁定renderBuffer和FrameBuffer是有順序的,先有RenderBuffer洋魂,才有FrameBuffer绷旗。

開始繪制

初始化

清除屏幕顏色,清空顏色緩沖區(qū)副砍,設置視口大小衔肢。

//設置清屏顏色
glClearColor(0.3, 0.45, 0.5, 1.0);
    
//清除屏幕
glClear(GL_COLOR_BUFFER_BIT);

//1.設置視口大小
CGFloat scale = [[UIScreen mainScreen] scale];
glViewport(self.frame.origin.x * scale, self.frame.origin.y * scale, self.frame.size.width * scale, self.frame.size.height * scale);

加載自定義著色器

1. 讀取并編譯頂點著色程序、片元著色程序

1.1 創(chuàng)建一個頂點/片元著色器

*shader = glCreateShader(type);

1.2 以字符串的形式將著色器源碼讀取出來豁翎,并將著色器源碼加載到著色器對象上

NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
const GLchar* source = (GLchar*)content.UTF8String;
glShaderSource(*shader, 1, &source, NULL);

1.3 編譯著色器角骤,把著色器源代碼編譯成目標代碼。此時得到一個可附著到程序的著色器對象

glCompileShader(*shader);
2. 加載著色器

2.1 創(chuàng)建program

GLint program = glCreateProgram();

2.2 將編譯好的著色器對象附著到程序中

glAttachShader(program, verShader);
glAttachShader(program, fragShader);

2.3 釋放不需要的著色器對象

glDeleteShader(verShader);
glDeleteShader(fragShader);
  1. 鏈接program
    在鏈接之后可調用glGetProgramiv函數(shù)判斷當前是否鏈接成功
glLinkProgram(self.myPrograme);
  1. 使用program
glUseProgram(self.myPrograme);

設置并處理頂點數(shù)據(jù)

  1. 設置頂點數(shù)據(jù)
GLfloat attrArr[] ={
    0.5f, -0.5f, -1.0f,     1.0f, 0.0f,
    -0.5f, 0.5f, -1.0f,     0.0f, 1.0f,
    -0.5f, -0.5f, -1.0f,    0.0f, 0.0f,
        
    0.5f, 0.5f, -1.0f,      1.0f, 1.0f,
    -0.5f, 0.5f, -1.0f,     0.0f, 1.0f,
    0.5f, -0.5f, -1.0f,     1.0f, 0.0f,
};
  1. 申請一個頂點緩沖區(qū)ID心剥,并將它綁定到GL_ARRAY_BUFFER標識符上
GLuint attrBuffer;
glGenBuffers(1, &attrBuffer);
glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
  1. 把頂點數(shù)據(jù)從CPU拷貝到GPU
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
  1. 打開頂點/片元著色器屬性通道
  • 通過glGetAttribLocation函數(shù)獲取頂點屬性入口启搂,它需要兩個參數(shù),參數(shù)1:program刘陶;參數(shù)2:自定義著色器文件中變量名稱的字符串胳赌,重點:這里的字符串必須同自定義著色器文件中變量名稱保持一致
  • 通過glEnableVertexAttribArray函數(shù)打開著色器的屬性通道
  • 通過glVertexAttribPointer函數(shù)設置讀取方式
//設置頂點坐標
GLuint position = glGetAttribLocation(self.myPrograme, "position");
glEnableVertexAttribArray(position);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), NULL);

//設置紋理坐標
GLuint textCoor = glGetAttribLocation(self.myPrograme, "textCoordinate");
glEnableVertexAttribArray(textCoor);
glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (float *)NULL + 3);

加載紋理

加載紋理的過程是將png/jpg圖片解壓縮成位圖匙隔,并通過自定義著色器讀取每個像素點的紋素疑苫。

  1. 解壓縮png/jpg圖片,將UIImage轉換為CGImageRef。
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
  1. 根據(jù)CGImageRef屬性獲取圖片的寬和高捍掺,并開辟一段空間用于存放解壓縮后的位圖信息撼短。位圖數(shù)據(jù)的大小為寬4。為什么是寬4挺勿?因為圖片共有寬高個像素點曲横,每個像素點有4個字節(jié),即RGBA不瓶,因此共有寬高*4大小的空間禾嫉。
//讀取圖片的大小,寬和高
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);

//獲取圖片字節(jié)數(shù) 寬*高*4(RGBA)
GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
  1. 創(chuàng)建CGContextRef上下文
/*
 參數(shù)1:data,指向要渲染的繪制圖像的內存地址
 參數(shù)2:width,bitmap的寬度蚊丐,單位為像素
 參數(shù)3:height,bitmap的高度熙参,單位為像素
 參數(shù)4:bitPerComponent,內存中像素的每個組件的位數(shù),比如32位RGBA麦备,就設置為8
 參數(shù)5:bytesPerRow,bitmap的沒一行的內存所占的比特數(shù)
 參數(shù)6:colorSpace,bitmap上使用的顏色空間  kCGImageAlphaPremultipliedLast:RGBA
 */
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
  1. 在CGContextRef上將圖片繪制出來孽椰,調用CGContextDrawImage函數(shù),使用默認方式繪制
/*
 CGContextDrawImage 使用的是Core Graphics框架凛篙,坐標系與UIKit 不一樣黍匾。UIKit框架的原點在屏幕的左上角,Core Graphics框架的原點在屏幕的左下角呛梆。
 CGContextDrawImage 
 參數(shù)1:繪圖上下文
 參數(shù)2:rect坐標
 參數(shù)3:繪制的圖片
*/
CGContextDrawImage(spriteContext, rect, spriteImage);
  1. 繪制完成之后膀捷,需要將上下文釋放掉
CGContextRelease(spriteContext);
  1. 經過重繪之后,就將jpg/png圖片轉換成了位圖得到了紋理數(shù)據(jù)削彬。接下來就是載入紋理數(shù)據(jù)全庸。
    6.1 綁定紋理到默認的紋理ID
    6.2 設置紋理屬性
    6.3 載入2D紋理數(shù)據(jù)
//綁定紋理到默認的紋理ID
glBindTexture(GL_TEXTURE_2D, 0);

//設置紋理屬性
/*
 參數(shù)1:紋理維度
 參數(shù)2:線性過濾、為s,t坐標設置模式
 參數(shù)3:wrapMode,環(huán)繞模式
*/
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

/*
 參數(shù)1:紋理模式融痛,GL_TEXTURE_1D壶笼、GL_TEXTURE_2D、GL_TEXTURE_3D
 參數(shù)2:加載的層次雁刷,一般設置為0
 參數(shù)3:紋理的顏色值GL_RGBA
 參數(shù)4:寬
 參數(shù)5:高
 參數(shù)6:border覆劈,邊界寬度
 參數(shù)7:format
 參數(shù)8:type
 參數(shù)9:紋理數(shù)據(jù)
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);

//釋放spriteData
free(spriteData); 
  1. 設置紋理采樣器
    主要是用來獲取紋理中對應像素點的顏色值,即紋素沛励。
  • 通過glGetUniformLocation函數(shù)獲取片元著色器中uniform的入口责语。該函數(shù)需要傳入兩個參數(shù),參數(shù)1:program目派;參數(shù)2:在片元著色器中用uniform修飾的變量名字的字符串坤候。注意,該字符串必須同片元著色器中對應的變量名保持一致企蹭。
  • 使用glUniform1i函數(shù)獲取紋素白筹,它也有兩個參數(shù)智末,參數(shù)1:片元著色器中uniform的入口;參數(shù)2:紋理ID徒河,默認為0系馆。
glUniform1i(glGetUniformLocation(self.myPrograme, "colorMap"), 0);

繪制

開始繪制,存儲到RenderBuffer顽照,從RenderBuffer將圖片顯示到屏幕上由蘑。

  • 調用glDrawArrays函數(shù),指定圖元連接方式進行繪制
  • context調用presentRenderbuffer函數(shù)將繪制好的圖片渲染到屏幕上顯示
glDrawArrays(GL_TRIANGLES, 0, 6);
[self.myContext presentRenderbuffer:GL_RENDERBUFFER];

至此代兵,使用GLSL加載紋理已經完成尼酿,完整代碼見Demo地址

從效果圖上看到奢人,圖片呈倒立顯示谓媒,這是因為OpenGL要求原點(0,0)位于圖片的左下角淆院,Y坐標從下往上增加何乎,而圖片紋理的原點(0,0)是位于圖片的左上角,Y坐標從上往下增加土辩。所以最后的照片呈上下倒置的效果支救。
以下是幾種解決方案:

  • 方案1:將頂點繞Y軸進行翻轉。這樣可以實現(xiàn)正常顯示拷淘。
    問題:如何實現(xiàn)繞Y軸翻轉
    解決:將頂點坐標與一個旋轉矩陣相乘各墨,得到的結果就是翻轉之后的頂點坐標。
    重點:在3D課程中用的是橫向量启涯,在OpenGL ES用的是列向量贬堵。頂點坐標是一個1行4列的矩陣,因此结洼,旋轉矩陣必須是4行4列黎做,這樣相乘之后才能得到新的1行4列的頂點坐標。另外松忍,要實現(xiàn)翻轉蒸殿,只需要將該方向的坐標數(shù)據(jù)進行反向,如當前需要沿X軸反向鸣峭,只需要將X軸的數(shù)據(jù)全部*-1宏所,即可將X軸的數(shù)據(jù)翻轉。
    代碼詳見方案1代碼
  • 方案2:可以解壓縮圖片的時候對圖片進行翻轉摊溶。
    解決:在context繪制的圖片爬骤,對圖片進行翻轉。
    重點:由于翻轉之后莫换,頂點數(shù)據(jù)的坐標會發(fā)生變化盖腕,超過繪制的區(qū)域赫冬,因此在翻轉之后需要將頂點移至繪制區(qū)域內。
    主要使用的函數(shù)有
//先平移至合適的位置溃列,也可以在翻轉之后再移至繪制區(qū)域內
CGContextTranslateCTM(context, 0, height);
//將Y軸翻轉
CGContextScaleCTM(context, 1, -1);

代碼詳見方案2代碼

  • 方案3:修改片元著色器紋理坐標劲厌,將片元著色器中的紋理坐標在Y軸方向翻轉。
    重點:如何獲取紋理坐標的Y軸方向數(shù)據(jù)听隐,通過'varyTextCoord.y'即可得到Y軸數(shù)據(jù)补鼻。將1.0-varyTextCoord.y即可實現(xiàn)翻轉。
vec2 newCoord = vec2(varyTextCoord.x, 1.0-varyTextCoord.y);
gl_FragColor = texture2D(colorMap, newCoord);

代碼詳見方案3代碼

  • 方案4:修改頂點著色器紋理坐標雅任,將頂點著色器的紋理坐標在Y軸方向翻轉风范。
    該方案原理同方案3一樣,只是在不同的著色器完成紋理坐標的翻轉沪么。
    代碼詳見方案4代碼

  • 方案5:修改源頂點數(shù)據(jù)中頂點坐標和紋理坐標的映射關系硼婿。
    原理同方案3、4一致禽车,只是直接在頂點數(shù)組中修改源數(shù)據(jù)寇漫。
    原頂點數(shù)據(jù)數(shù)組

GLfloat attrArr[] ={
    0.5f, -0.5f, -1.0f,     1.0f, 0.0f,
    -0.5f, 0.5f, -1.0f,     0.0f, 1.0f,
    -0.5f, -0.5f, -1.0f,    0.0f, 0.0f,

    0.5f, 0.5f, -1.0f,      1.0f, 1.0f,
    -0.5f, 0.5f, -1.0f,     0.0f, 1.0f,
    0.5f, -0.5f, -1.0f,     1.0f, 0.0f,
};

修改后的頂點數(shù)組

GLfloat attrArr[] ={
    0.5f, -0.5f, -1.0f,     1.0f, 1.0f,
    -0.5f, 0.5f, -1.0f,     0.0f, 0.0f,
    -0.5f, -0.5f, -1.0f,    0.0f, 1.0f,
        
    0.5f, 0.5f, -1.0f,      1.0f, 0.0f,
    -0.5f, 0.5f, -1.0f,     0.0f, 0.0f,
    0.5f, -0.5f, -1.0f,     1.0f, 1.0f,
};

代碼詳見方案5代碼

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市殉摔,隨后出現(xiàn)的幾起案子州胳,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異舔琅,居然都是意外死亡,警方通過查閱死者的電腦和手機瓤湘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恩尾,“玉大人弛说,你說我怎么就攤上這事√厮瘢” “怎么了剃浇?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猎物。 經常有香客問我虎囚,道長,這世上最難降的妖魔是什么蔫磨? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任淘讥,我火速辦了婚禮,結果婚禮上堤如,老公的妹妹穿的比我還像新娘蒲列。我一直安慰自己窒朋,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布蝗岖。 她就那樣靜靜地躺著侥猩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抵赢。 梳的紋絲不亂的頭發(fā)上欺劳,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音铅鲤,去河邊找鬼划提。 笑死,一個胖子當著我的面吹牛邢享,可吹牛的內容都是我干的鹏往。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼骇塘,長吁一口氣:“原來是場噩夢啊……” “哼伊履!你這毒婦竟也來了?” 一聲冷哼從身側響起绪爸,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤湾碎,失蹤者是張志新(化名)和其女友劉穎宙攻,沒想到半個月后奠货,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡座掘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年递惋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溢陪。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡萍虽,死狀恐怖,靈堂內的尸體忽然破棺而出形真,到底是詐尸還是另有隱情杉编,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布咆霜,位于F島的核電站邓馒,受9級特大地震影響,放射性物質發(fā)生泄漏蛾坯。R本人自食惡果不足惜光酣,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脉课。 院中可真熱鬧救军,春花似錦财异、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拷泽,卻和暖如春庆揩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背跌穗。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工订晌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蚌吸。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓锈拨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親羹唠。 傳聞我的和親對象是個殘疾皇子奕枢,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355