android平臺下OpenGL ES 3.0著色語言基礎知識(下)

OpenGL ES 3.0學習實踐

本篇整理自《OpenGL ES 3.0 編程指南第2版》

目錄

統(tǒng)一變量和屬性

統(tǒng)一變量(uniform)是存儲應用程序通過OpenGL ES 3.0 API傳遞給著色器的只讀常數(shù)值的變量。
統(tǒng)一變量被組合成兩類統(tǒng)一變量塊肝箱。

  • 第一類是命名統(tǒng)一變量塊屿聋,統(tǒng)一變量的值由所謂的統(tǒng)一變量緩沖區(qū)對象支持扛稽,命名統(tǒng)一變量塊被分配一個統(tǒng)一變量塊索引瓮孙。
uniform TransformBlock {
    mat4 matViewProj;
    mat3 matNormal;
    mat3 matTexGen;
};
  • 第二類是默認的統(tǒng)一變量塊浦楣,用于在命名統(tǒng)一變量塊之外聲明的統(tǒng)一變量。和命名統(tǒng)一變量塊不同火欧,默認統(tǒng)一變量塊沒有名稱或者統(tǒng)一變量塊索引棋电。
uniform mat4 matViewProj;
uniform mat3 matNormal;
uniform mat3 matTexGen;

獲取和設置統(tǒng)一變量

要査詢程序中活動統(tǒng)一變量的列表茎截,首先要用GL_ACTIVE_UNIFORMS參數(shù)苇侵,調用glGetProgramiv。這樣可以獲得程序中活動統(tǒng)一變量的數(shù)量企锌。這個列表包含命名統(tǒng)一變量塊中的統(tǒng)一變量榆浓、著色器代碼中聲明的默認統(tǒng)一變量塊中的統(tǒng)一變量以及著色器代碼中使用的內建統(tǒng)一變量。如果統(tǒng)一變量被程序使用撕攒,就認為它是"活動"的陡鹃。換言之烘浦,如果你在一個著色器中聲明了一個統(tǒng)一變量但是從未使用,鏈接程序可能會在優(yōu)化時將其去掉萍鲸,不在活動統(tǒng)一變量列表中返回闷叉。獲取到統(tǒng)一變量和存儲統(tǒng)一變量名稱所需的字符數(shù)之后,我們可以用glGetActiveUniformglGetActiveUniformsiv找出每個統(tǒng)一變量的細節(jié)脊阴。

使用glGetActiveUniform握侧,可以確定幾乎所有統(tǒng)一變量的屬性。你可以確定統(tǒng)一變量的名稱和類型嘿期。此外品擎,可以發(fā)現(xiàn)變量是不是數(shù)組以及數(shù)組中使用的最大元素。統(tǒng)一變量的名稱 對于找到統(tǒng)一變量的位置是必要的备徐,要知道如何加載統(tǒng)一變量的數(shù)據(jù)萄传,需要統(tǒng)一變量的類型和大小。一旦有了統(tǒng)一變量的名稱蜜猾,就可以用glGetUniformLocation找到它的位置秀菱。統(tǒng)一變量的位置是一個整數(shù)值,用于標識統(tǒng)一變量在程序中的位置(注意: 命名統(tǒng)一變量塊中的統(tǒng)一變量沒有指定位置)瓣铣。這個位置值用于加載統(tǒng)一變量值的后續(xù)調用(例如: glUniformlf)答朋。

public static native int glGetUniformLocation(
        int program,
        String name
);

這個函數(shù)將返回由name指定的統(tǒng)一變量的位置。如果這個統(tǒng)一變量不是程序中的活動統(tǒng)一變量棠笑,返回值將為-1梦碗。有了統(tǒng)一變量的位置及其類型和數(shù)組大小,我們就可以加載統(tǒng)一變量的值蓖救。加載統(tǒng)一變量值有許多不同的函數(shù)洪规,每種統(tǒng)一變量類型都對應不同的函數(shù)。

public static native void glUniform1f(int location,float x);
public static native void glUniform1fv(int location,int count,float[] v,int offset);
......

加載統(tǒng)一變量所需的函數(shù)根據(jù)glGetActiveUniform函數(shù)返回的type確定循捺。例如斩例,如果返回的類型是GL_FLOAT_VEC4, 那么可以使用glUniform4fglUnifomi4fv。如果gIGetActiveUniform返回的size大于 1, 則使用glUnifrom4fv在一次調用中加載整個數(shù)組念赶。如果統(tǒng)一變量不是數(shù)組,則可以使用glUniform4fglUniform4fv

注意: glUniform*調用不以程序對象句柄作為參數(shù)恰力。原因是: glUniform*總是在與glUseProgram綁定的當前程序上操作停局。統(tǒng)一變量值本身保存在程序對象中码倦。也就是說,一旦在程序對象中設置一個統(tǒng)一變量的值擒抛,即使你讓另一個程序處于活動狀態(tài)运提,該值仍然保留在原來的程序對象中。從這個意義上闻葵,我們可以說統(tǒng)一變量值是程序對象局部所有的民泵。

下面來實踐一下査詢程序對象中的統(tǒng)一變量信息的方法。

新建一個UniformRenderer.java文件:

/**
 * @anchor: andy
 * @date: 2018-11-02
 * @description:
 */
public class UniformRenderer implements GLSurfaceView.Renderer {

    private static final String TAG = "UniformRenderer";

    private int mProgram;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //設置背景顏色
        GLES30.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
        //編譯
        final int vertexShaderId = ShaderUtils.compileVertexShader(ResReadUtils.readResource(R.raw.vertex_uniform_shader));
        final int fragmentShaderId = ShaderUtils.compileFragmentShader(ResReadUtils.readResource(R.raw.fragment_uniform_shader));
        //鏈接程序片段
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES環(huán)境中使用程序片段
        GLES30.glUseProgram(mProgram);


        final int[] maxUniforms = new int[1];
        GLES30.glGetProgramiv(mProgram, GLES30.GL_ACTIVE_UNIFORM_MAX_LENGTH, maxUniforms, 0);
        final int[] numUniforms = new int[1];
        GLES30.glGetProgramiv(mProgram, GLES30.GL_ACTIVE_UNIFORMS, numUniforms, 0);

        Log.d(TAG, "maxUniforms=" + maxUniforms[0] + " numUniforms=" + numUniforms[0]);

        int[] length = new int[1];
        int[] size = new int[1];
        int[] type = new int[1];
        byte[] nameBuffer = new byte[maxUniforms[0] - 1];
        for (int index = 0; index < numUniforms[0]; index++) {
            GLES30.glGetActiveUniform(mProgram, index, maxUniforms[0], length, 0, size, 0, type, 0, nameBuffer, 0);
            String uniformName = new String(nameBuffer);
            int location = GLES30.glGetUniformLocation(mProgram, uniformName);

            Log.d(TAG, "uniformName=" + uniformName + " location=" + location + " type=" + type[0] + " size=" + size[0]);
        }
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES30.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
    }
}

頂點著色器

#version 300 es
uniform mat4 mMatrix4;
uniform mat3 mMatrix3;

layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec4 aColor;
out vec4 vColor;
void main() {
     gl_Position  = mMatrix4 * vPosition;
     gl_PointSize = 10.0;
     vColor = aColor;
}

片段著色器

#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 fragColor;
void main() {
     fragColor = vColor;
}

輸出如下日志:

11-07 11:46:22.099 28987-29005/? D/UniformRenderer: maxUniforms=9 numUniforms=1
11-07 11:46:22.099 28987-29005/? D/UniformRenderer: uniformName=mMatrix4 location=0 type=35676 size=1

為什么只輸出了mMatrix4,那mMatrix3呢? 剛剛也說到了厢钧,雖然我們在頂點著色器中聲明了mMatrix3,但是我們并沒有使用它早直,導致它被鏈接程序優(yōu)化掉了

統(tǒng)一變量塊

統(tǒng)一變量緩沖區(qū)對象可以通過一個緩沖區(qū)對象支持統(tǒng)一變量數(shù)據(jù)的存儲。統(tǒng)一變量緩沖區(qū)對象在某些條件下比單獨的統(tǒng)一變量有更多優(yōu)勢糕韧。利用統(tǒng)一變量緩沖區(qū)對象喻圃,統(tǒng)一變量緩沖區(qū)數(shù)據(jù)可以在多個程序中共享,但只需要設置一次斧拍。而且統(tǒng)一變量緩沖區(qū)對象一般可以存儲更大量的統(tǒng)一變量數(shù)據(jù)。最后愚墓,在統(tǒng)一緩沖區(qū)對象之間切換比一次單獨加載一個統(tǒng)一變最更高效昂勉。

統(tǒng)一緩沖區(qū)對象可以在OpenGL ES著色語言中通過應用統(tǒng)一變量塊使用。

uniform TransformBlock {
    mat4 matViewProj;
    mat3 matNormal;
    mat3 matTexGen;
};

上述聲明了一個名為TransformBlock且包含3個矩陣的統(tǒng)一變量塊硼啤。名稱TransformBlock將供應用程序使用议经,統(tǒng)一緩沖區(qū)對象函數(shù)glGetUniformBlocklndex中的blockName參數(shù)。統(tǒng)一變量塊聲明中的變量在著色器中都可以訪問谴返,就像常規(guī)形式聲明的變量一樣煞肾。

#version 300 es
uniform TransformBlock {
    mat4 matViewProj;
    mat3 matNormal;
    mat3 matTexGen;
};
layout(location = 0) in vec4 a_position;
void main{
     gl_Position = matViewProj * a_position;
}

布局限定符可用于指定支持統(tǒng)一變量塊的統(tǒng)一緩沖區(qū)對象在內存中的布局方式。布局限定符可以提供給單獨的統(tǒng)一變量塊嗓袱,或者用于所有統(tǒng)一變量塊籍救。在全局作用域內,為所有統(tǒng)一變量塊設置默認布局的方法如下:

layout(shared, column_major) uniform; // 如果未指定渠抹,則為默認
layout(packed, row_major) uniform;

單獨的統(tǒng)一變量塊也可以通過覆蓋全局作用域上的默認設置來設置布局蝙昙。此外統(tǒng)一變量塊中的單獨統(tǒng)一變量也可以指定布局限定符

layout(stdl40) uniform TransformBlock
{
mat4 matViewProj;
layout(row_major) mat3 matNormal;
mat3 matTexGen;
};

可以用于統(tǒng)一變量塊的所有布局限定符:

限定符 描述
shared shared限定符指定多個著色器或者多個程序中統(tǒng)一變量塊的內存布局相同。要使用這個限定符梧却,不同定義中的row_major/column_major值必須相等奇颠。覆蓋stdl40packed(默認)
packed packed布局限定符指定編譯器可以優(yōu)化統(tǒng)一變量塊的內存布局。使用這個限定符時必須查詢偏移位置放航,而且統(tǒng)一變量塊無法在頂點/片段著色器或者程序間共享烈拒。覆蓋stdl40shared
stdl40 sldl40布局限定符指定統(tǒng)一變童塊的布局基于OpenGL ES 3.0規(guī)范中定義的一組標準規(guī)則。覆蓋sharedpacked
row_major 矩陣在內存中以行優(yōu)先順序布局
column_major 矩陣在內存中以列優(yōu)先順序布局(默認)

頂點和片段著色器輸入/輸出

OpenGL ES著色器語言的另一個特殊變量類型是頂點輸入(或者屬性)變量广鳍。頂點輸入變量用于指定頂點著色器中每個頂點的輸入荆几,用in關鍵字指定。它們通常存儲位置赊时、法線吨铸、 紋理坐標和顏色這樣的數(shù)據(jù)。這里的關鍵是理解頂點輸入是為繪制的每個頂點指定的數(shù)據(jù)祖秒。

頂點著色器

#version 300 es
uniform mat4 u_matViewProjection;
//輸入
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec3 a_color;
out vec3 v_color;
void main{)
{
     gl_Position = u_matViewProjection * a_position;
     v_color = a_color;
}

片段著色器

#version 300 es
precision mediump float;
in vec4 vColor;
//輸出片段著色器
layout (location = 0) out vec4 fragColor;
void main() { 
     fragColor = vColor;
}

這個著色器的兩個頂點輸入變量a_position和a_color的數(shù)據(jù)由應用程序加載诞吱。本質上,應用程序將為每個頂點創(chuàng)建一個頂點數(shù)組竭缝,該數(shù)組包含位置和顏色狐胎。注意上面的例子中頂點輸入變量之前使用了layout限定符握巢。這種情況下的布局限定符用于指定頂點屬性的索引暴浦。布局限定符是可選的歌焦,如果沒有指定,鏈接程序將自動為頂點輸入變量分配位置

統(tǒng)一變量—樣卵史,底層硬件通常在可輸入頂點著色器的屬性變量數(shù)目上有限制以躯。OpenGL ES實現(xiàn)支持的最大屬性數(shù)量由內建變量gl_MaxVertexAttribs給出(也可以使用glGetlntegerv査詢GL_MAX_VERTEX_ATTRIBS得到)忧设。OpenGL ES 3.0實現(xiàn)可支持的最小屬性為16個址晕。 不同的實現(xiàn)可以支持更多變量斩箫,但是如果想要編寫保證能在任何OpenGL ES 3.0實現(xiàn)上運行的著色器乘客,則應該將屬性限制為不多于16個易核。

來自頂點著色器的輸出變量out關鍵字指定牡直。上面的示例代碼中碰逸,v_color變量被聲明為輸出變量饵史,其內容從a_color輸入變量中復制而來。每個頂點著色器將在一個或者多個輸出變量中輸出需要傳遞給片段著色器的數(shù)據(jù)夭织。然后尊惰,這些變量也會在片段著色器中聲明為in變量 (相符類型),在光柵化階段中對圖元進行線性插值鞋诗。

片段著色器中與頂點著色器頂點輸出v_Color相匹配的輸入聲明如下:

in vec3 v_color;

注意: 與頂點著色器輸入不同担孔,頂點著色器輸出/片段著色器輸入變量不能有布局限定符OpenGL ES實現(xiàn)自動選擇位置啄育,與統(tǒng)一變量和頂點輸入屬性相同,底層硬件通常限制頂點著色器輸出/片段著色器輸入(在硬件上墩崩,這些變量通常被稱作插值器)的數(shù)量铝阐。 OpenGL ES實現(xiàn)支持的頂點著色器輸出的數(shù)量由內建變量gl_MaxVertexOutputVectors給出(用glGetlntegerv査詢GL_MAX_VERTEX_OUTPUT_COMPONENTS將提供總分量值數(shù)量徘键,而非向量數(shù)量)吹害。OpenGLES 3.0實現(xiàn)可以支持的最小頂點輸出向量數(shù)為16它呀。與此類似钟些,OpenGL ES 3.0實現(xiàn)支持的片段著色器輸入的數(shù)量由gl_MaxFragmentInputVectors給出(用glGetlntegerv查詢GL_MAX_FRAGMENT_INPUT_COMPONENTS將提供總分量值數(shù)量,而非向量數(shù)量)篙耗。OpenGL ES 3.0實現(xiàn)可以支持的最小片段輸入向量數(shù)為15

上述的片段著色器將輸出一個或者多個顏色宗弯。在正常情況下辕棚,我們只渲染到一個顏色緩沖區(qū)逝嚎,在這種時候补君,布局限定符是可選的(假定輸出變量進入位置0)挽铁。但是,當渲染到多個渲染目標(MRT)時够掠,我們可以使用布局限定符指定每個輸出前往的渲染目標疯潭。對于這種情況,在片段著色器中會有個輸出變量脊僚,該值將是傳遞給管線逐片段操作部分的輸出顏色增淹。

插值限定符

上面的示例中虑润,我們聲明了自己的頂點著色器輸出和片段著色器輸入拳喻,沒有使用任何限定符钦勘。在沒有限定符時彻采,默認的插值行為是執(zhí)行平滑著色肛响。也就是說终惑,來自頂點著色器的輸出變量在圖元中線性插值,片段著色器接收線性插值之后的數(shù)值作為輸入臼寄。我們可以明確地請求平滑著色,在這種情況下留攒,輸出/輸入如下:

// 頂點著色器輸出
smooth out vec3 v_color;
// 片段著色器輸入
smooth in vec3 v_color;

OpenGL ES 3.0還引入了另一種插值——平面著色。在平面著色中剪侮,圖元中的值沒有進行插值杰标,而是將其中一個頂點視為驅動頂點(Provoking Vertex腔剂,取決于圖元類型)驻仅,該頂點的值被用于圖元中的所有片段噪服。我們可以聲明如下的平面著色輸出/ 輸入:

// 頂點著色器輸出
flat out vec3 v_color;
// 片段著色器輸入
flat in vec3 v_color;

最后,可以用centroid關鍵字在插值器中添加另一個限定符呻顽。使用多重采樣渲染時雹顺,centroid關鍵字可用于強制插值發(fā)生在被渲染圖元內部(否則,在圖元的邊緣可能出現(xiàn)偽像)廊遍。

質心采樣的輸出/輸入變量的方法嬉愧。

// 頂點著色器輸出
smooth centroid out vec3 v_color
// 頂點著色器輸出
smooth centroid in vec3 v_color

統(tǒng)一變量和插值器打包

底層硬件中可用于每個變量存儲的資源是固定的。統(tǒng)一變量通常保存在所謂的"常量存儲"中喉前,這可以看作向量的物理數(shù)組没酣。頂點著色器輸出/片段著色器輸入一般保存在插值器中,這通 常也保存為一個向量數(shù)組卵迂。著色器可能聲明各種類型的統(tǒng)一變量和著色器輸入/輸出见咒,包括標量视事、各種向量分量和矩陣犬性。但是鹤耍,這些變量聲明如何映射到硬件上的可用物理空間呢族购?換言之瑟幕,如果一個OpenGL ES 3.0實現(xiàn)支持16個頂點著色器輸出向量排吴,那 么物理存儲實際上是如何使用的呢扯键?

在OpenGL ES 3.0中厉亏,這個問題通過打包規(guī)則處理窝趣,該規(guī)則定義插值器和統(tǒng)一變量映射到物理存儲空間的方式。打包規(guī)則基于物理存儲空間被組織為一個每個存儲位置4列(每個向量分量一列)和1行的網格的概念儒将。打包規(guī)則尋求打包變量,使生成代碼的復雜度保持不變刚操。換言之,打包規(guī)則不進行重排序操作叭喜,而是試圖在不對運行時性能產生負面影響的情況下盯腌,優(yōu)化物理地址空間的使用甚淡。

uniform mat3 m;
uniform float f[6];
uniform vec3 v;

如果完全不進行打包,許多常量存儲空間將被浪費契吉。矩陣m將占據(jù)3行佩憾,數(shù)組f占據(jù)6行鬼廓,向量v占據(jù)1行,共需要10行才能存儲這些變量萎河。

未打包的統(tǒng)一變量存儲

位置 Y Z W
0 m[0].x m[0].y m[0].z m[0].w
1 m[1].x m[1].y m[1].z m[1].w
2 m[2].x m[2].y m[2].z m[2].w
3 f[0]
4 f[1]
5 f[2]
6 f[3]
7 f[4]
8 f[5]
9 v.x v.y v.z -6

打包的統(tǒng)一變量存儲

位置 Y Z W
0 m[0].x m[0].y m[0].z f[0]
1 m[1].x m[1].y m[1].z f[1]
2 m[2].x m[2].y m[2].z f[2]
3 v.x v.y v.z f[3]
4 f[4]
5 f[5]

在使用打包規(guī)則時,只需使用6個物理常量位置述呐。數(shù)組f的元素會跨越行的邊界惩淳,原因是GPU通常會按照向量位置索引對常量存儲進行索引。打包必須使數(shù)組跨越行邊界乓搬,這樣索引才能夠起作用思犁。
所有打包對OpenGL ES著色語言的用戶都是完全透明的,除了一個細節(jié):打包影響統(tǒng)一變量和頂點著色器輸出/片段著色器輸入的計數(shù)方式进肯。如果想要編寫保證能夠在所有OpenGL ES 3.0實現(xiàn)上運行的著色器激蹲,就不應該使用打包之后超過最小運行存儲大小的統(tǒng)一變量或者插值器。

精度限定符

梢度限定符使著色器創(chuàng)作者可以指定著色器變量的計算精度江掩。變量可以聲明為低学辱、中或者高精度。這些限定符用于提示編譯器允許在較低的范圍和精度上執(zhí)行變量計算环形。在較低的精度上策泣,有些OpenGLES實現(xiàn)在運行著色器時可能更快,或者電源效率更高抬吟。
當然萨咕,這種效率提升是以精度為代價的,在沒有正確使用精度限定符時可能造成偽像火本。

精度限定符可以用于指定任何基于浮點數(shù)或者整數(shù)的變量的精度危队。指定精度的關鍵字是lowp、mediump和highp钙畔。

highp vec4 position;
varying lowp vec4 color;
mediump float specularExp;

如果變量聲明時沒有使用精度限定符茫陆,它將擁有該類型的默認精度。默認精度限定符在頂點或者片段著色器的開頭指定:

precision highp float;
precision mediump int;

float類型指定的精度將用作所有基于浮點值的變量的默認精度擎析。同樣簿盅,為int類型指定的精度將用作所有基于整數(shù)的變量的默認精度。

在頂點著色器中叔锐,如果沒有指定默認精度挪鹏,則int和float的默認精度都為highp。也就是說愉烙,頂點著色器中所有沒用精度限定符聲明的變量都使用最高的精度。片段著色器的規(guī)則與此不同解取。在片段著色器中步责,浮點值沒有默認的精度值,每個著色器必須聲明一個默認的float精度,或者為每個float變量指定精度蔓肯。

不變性

OpenGL ES著色語言中引入的invariant關鍵字可以用于任何可變的頂點著色器輸出遂鹊。
由于著色器需要編譯,而編譯器可能進行導致指令重新排序的優(yōu)化蔗包。這種指令重排意味著兩個著色器之間的等價計算不能保證產生完全相同的結果秉扑。這種不一致性在多遍著色器特效時尤其可能成為問題,在這種情況下调限,相同的對象用Alpha混合繪制在自身上方舟陆。如果用于計算輸出位置的數(shù)值的精度不完全一樣,精度差異就會導致偽像耻矮。

因為編譯器需要保證不變性秦躯,所以可能限制它所做的優(yōu)化。因此裆装,invariant限定符應該只在必要時使用踱承,否則可能導致性能下降。

項目地址:
https://github.com/byhook/opengles4android

參考:
《OpenGL ES 3.0 編程指南第2版》

?著作權歸作者所有,轉載或內容合作請聯(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

推薦閱讀更多精彩內容