OpenGl-ES2.0 For Android 讀書筆記(三)

一惶凝、開始

在上篇文章中吼虎,我們已經(jīng)讓桌面的顏色有了變化,也讓桌面適配了屏幕的比例苍鲜,讓手機在旋轉(zhuǎn)的時候桌面也不會產(chǎn)生變形思灰。但現(xiàn)在看到的桌面就像是在紙上畫的一樣,一點都沒有立體感混滔。所以現(xiàn)在我們要學(xué)習(xí)的就是3D的去展示我們的桌面洒疚,這個只是第一點,為了讓我們的桌面更好看一些坯屿,我們準(zhǔn)備了一張圖片拳亿,我們也會學(xué)習(xí)如何讓OpenGL去渲染我們要展示的圖片,總的來說我們要學(xué)習(xí)兩點:
1.3D展示桌面
2.OpenGL渲染圖片
最終我們做出來的效果應(yīng)該是這個樣子的:

效果圖.png

二愿伴、3D展示桌面

為了用3D效果展示桌面,我們先要知道一個gl_Position是如何轉(zhuǎn)換成屏幕上顯示的點的坐標(biāo)的:

坐標(biāo)轉(zhuǎn)換流程圖.png

大致流程就像上圖所示电湘,書中講的很詳細(xì)隔节,我這里簡單講下,大家感興趣的可以自己去看書或者查資料寂呛,我們制造出3D效果主要實在Perspective Division(透視除法)這一步怎诫,一個點的坐標(biāo)應(yīng)該是(x , y , z , w)在Clip Space(裁剪空間)這個里面,這個時候x,y,z的取值范圍是在-w到w之間的贷痪,然后會經(jīng)過Perspective Division轉(zhuǎn)換幻妓,這個時候的坐標(biāo)就應(yīng)該是(x/w , y/w , z/w)。
然后我們現(xiàn)在就用代碼去實現(xiàn)了劫拢,這個時候我們一個點的坐標(biāo)就應(yīng)該有4個參數(shù)了肉津,所以先修改如下代碼:

private static final int POSITION_COMPONENT_COUNT = 4;

然后我們需要修改我們的數(shù)據(jù)

private final float[] mData = new float[]{
            // Order of coordinates: X, Y, Z, W, R, G, B

            // Triangle Fan
               0f,    0f, 0f, 1.5f,   1f,   1f,   1f,
            -0.5f, -0.8f, 0f,   1f, 0.7f, 0.7f, 0.7f,
             0.5f, -0.8f, 0f,   1f, 0.7f, 0.7f, 0.7f,
             0.5f,  0.8f, 0f,   2f, 0.7f, 0.7f, 0.7f,
            -0.5f,  0.8f, 0f,   2f, 0.7f, 0.7f, 0.7f,
            -0.5f, -0.8f, 0f,   1f, 0.7f, 0.7f, 0.7f,

            //線
            -0.5f, 0f, 0f, 1.5f, 1f, 0f, 0f,
             0.5f, 0f, 0f, 1.5f, 1f, 0f, 0f,

            //點
            0f, -0.4f, 0f, 1.25f, 0f, 0f, 1f,
            0f,  0.4f, 0f, 1.75f, 1f, 0f, 0f
    };

運行下看看,是不是有點3D的感覺了舱沧!但是這樣我們要硬編碼w參數(shù)妹沙,這樣明顯是不好的,當(dāng)然我們的前人也不會這么傻熟吏,當(dāng)然有別的方法去處理距糖,我們可以做移動玄窝,縮放,旋轉(zhuǎn)等操作悍引,讓我們把之前的代碼還原恩脂,重新開始吧。
這個時候我們又要回到矩陣的操作了趣斤,這次我們要同時處理屏幕適配俩块,和視野的處理,書中講了很多原理唬渗,大致就是講透視投影是怎么一回事典阵,有興趣的可以自己去看看,我們就是要用這個東西去做出我們的3D效果镊逝。

投影矩陣.png

這個是書中給出的投影矩陣的說明壮啊,這個投影矩陣可以同時適配屏幕和調(diào)整視野角度具體的參數(shù)說明都有說明,a,f,n可能需要看書中對透視投影的講解才知道撑蒜,大家可以google下歹啼,個人覺得不理解的話,寫完代碼之后也大概知道是什么了座菠,所以就不講解了狸眼,接下來我們定義一個這樣的矩陣。
書中是這樣告訴我們的

Android’s Matrix class contains two methods for this, frustumM() and perspectiveM(). Unfortunately, frustumM() has a bug that affects some types of projections,3 and perspectiveM() was only introduced in Android Ice Cream Sandwich and is not available on earlier versions of Android.

意思就是說浴滴,騷年拓萌,自己寫吧!
所以我們就自己來寫吧升略!我們在util包下新建一個類叫MatrixHelper.java類微王,并聲明如下方法:

public static void perspectiveM(float[] m, float yFovInDegrees, float aspect, float n, float f) {
        final float angleInRadians = (float) (yFovInDegrees * Math.PI / 180.0);
        final float a = (float) (1.0 / Math.tan(angleInRadians / 2.0));

        m[0] = a / aspect;
        m[1] = 0f;
        m[2] = 0f;
        m[3] = 0f;

        m[4] = 0f;
        m[5] = a;
        m[6] = 0f;
        m[7] = 0f;

        m[8] = 0f;
        m[9] = 0f;
        m[10] = -((f + n) / (f - n));
        m[11] = -1f;

        m[12] = 0f;
        m[13] = 0f;
        m[14] = -((2f * f * n) / (f - n));
        m[15] = 0f;
    }

這樣我們就可以生成一個上面的矩陣了,現(xiàn)在讓我們刪掉onSurfacedChanged()方法中除了glViewport()之外的東西品嚣,然后調(diào)用上面的方法:

MatrixHelper.perspectiveM(mProjectionMatrix , 45 , (float)width / (float)height , 1 , 10);

運行一遍之后炕倘,你會發(fā)現(xiàn),WTF翰撑,怎么成黑的了罩旋,小伙子,不要著急眶诈,這是正常的涨醋,因為我們n,f的值分別為1逝撬,10东帅,就是說我們只能看到z軸上-1到-10之間的東西,這個時候我們可以用移動矩陣在z軸上移動一下球拦,就能看到了靠闭。
我們先定義一個Model矩陣:

private float[] mModelMatrix = new float[16];

然后我們就要算出一個Model矩陣了帐我,在onSurfacedChanged()中添加如下代碼:

setIdentityM(mModelMatrix, 0);
translateM(mModelMatrix, 0, 0f, 0f, -2f);

第一行代碼是設(shè)置成單位矩陣,第二行代碼是生成我們要的移動矩陣愧膀。這個時候我們就遇到一個問題拦键,難道我們又要給gl_Point設(shè)置一個轉(zhuǎn)換矩陣了么?答案是不需要檩淋,我們可以只使用一個矩陣就可以解決了芬为,只不過這個時候需要把兩個矩陣乘一下,但是順序要特別注意蟀悦,不能顛倒:
vertexclip = ProjectionMatrix * ModelMatrix * vertexmodel
onSurfacedChanged()方法中再添加如下方法:

final float[] temp = new float[16];
multiplyMM(temp, 0, mProjectionMatrix, 0, mModelMatrix, 0);
System.arraycopy(temp, 0, mProjectionMatrix, 0, temp.length);

我們用一個臨時的變量去儲存兩個矩陣相乘的結(jié)果媚朦,然后再把臨時變量里面的內(nèi)容copy到mProjectionMatrix變量傳遞給OpenGL,這樣我們就可以同時看到兩個矩陣作用的效果了日戈,運行下询张,看看吧,是不是能看到了浙炼。
然后我們可以再加上一個旋轉(zhuǎn)的效果就能看到之前做出來的3D效果了份氧,旋轉(zhuǎn)需要選擇繞著X,Y,Z那個軸旋轉(zhuǎn),根據(jù)不同的軸旋轉(zhuǎn)矩陣也不一樣弯屈,書中有說明蜗帜,這里就不講解了,我們在代碼中只需要調(diào)用一個方法就可以了资厉,在translateM(mModelMatrix, 0, 0f, 0f, -2f);添加如下代碼

rotateM(mModelMatrix, 0 , -60 , 1 , 0 , 0);

同時把移動的z軸的值改成-2.5厅缺,這樣可以讓桌面離我們遠(yuǎn)一點。
我們調(diào)用方法宴偿,讓桌面繞著x軸旋轉(zhuǎn)了-60度湘捎,這里可能涉及到坐標(biāo)系的問題,書中有講解酪我,可以自行了解。再運行一遍且叁,看看效果是不是就出來了都哭。

三、用Texture渲染圖片

現(xiàn)在我們就可以開始想辦法讓我們的桌面更好看一些了逞带。首先我們要知道Texture是個什么東西

Textures in OpenGL can be used to represent images, pictures, and even fractal data that are generated by a mathematical algorithm.

這是原文的解釋欺矫,就是用來展示圖片的,甚至可以展示數(shù)學(xué)算法制作出來的數(shù)據(jù)展氓。所以我們就是用這個東西去渲染我們要渲染的圖片的穆趴。
首先我們要做的事情就是把圖片加載到Texture上,我們在util包下建一個類TextureHelper.java遇汞,并實現(xiàn)如下代碼:

public static int loadTexture(Context context , int resourceId){

        final int[] textureObjectId = new int[1];
        glGenTextures(1 , textureObjectId , 0);

        if (textureObjectId[0] == 0){
            Logger.debug(TAG , "create texture fail.");
            return 0;
        }

        return textureObjectId[0];
    }

我們聲明了一個loadTexture()方法來加載texture未妹,我們先創(chuàng)建了一個Texture的對象簿废,接下來我們要把圖片加載到texture上,我們繼續(xù)添加如下代碼:

final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
final Bitmap bitmap = BitmapFactory.decodeResource(
                context.getResources(), resourceId, options);
if (bitmap == null) {
    Logger.debug(TAG, "Resource ID " + resourceId + " could not be decoded.");
    glDeleteTextures(1, textureObjectId, 0);
    return 0;
}

glBindTexture(GL_TEXTURE_2D , textureObjectId[0]);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();

glGenerateMipmap(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D , 0);

首先我們把圖片資源轉(zhuǎn)換成了bitmap络它,然后我們讓OpenGL綁定了我們之前創(chuàng)建的Texture對象族檬,并設(shè)置了兩個參數(shù)GL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER化戳,這兩個參數(shù)在書中有詳細(xì)解釋单料,大家可以自己去了解,大概就是設(shè)置圖片的清晰度点楼,同時如果考慮到效率的話有多種選擇扫尖。然后我們就可以把圖片加載到我們創(chuàng)建的Texture對象上了,最后就是bitmap的回收掠廓,解除Texture的綁定了换怖。
加載圖片資源到Texture的問題解決了,接下來我們需要知道OpenGL如何去繪制Texture了却盘,這個跟之前只畫顏色類似狰域,我們需要新建一組.glsl文件,我們先創(chuàng)建一個texture_vertex_shader.glsl文件黄橘,實現(xiàn)如下代碼:

uniform mat4 u_Matrix;

attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;

varying vec2 v_TextureCoordinates;

void main() {

    gl_Position = u_Matrix * a_Position;
    v_TextureCoordinates = a_TextureCoordinates;

}

大家注意到了兆览,我們聲明了一個有兩個參數(shù)的向量的變量a_TextureCoordinates,這個Texture是有一個范圍為(0,0)到(1,1)的坐標(biāo)空間的塞关,它的坐標(biāo)系有兩種ST抬探、UV,具體解釋書中是有講解的帆赢,在這里不做過多講解了小压。
接下來我們創(chuàng)建texture_fragment_shader.glsl文件,實現(xiàn)如下代碼:

precision mediump float;

uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;

void main() {
     gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
}

大家可以這樣理解椰于,u_TextureUnit就相當(dāng)于我們要繪制的圖片的每個點的顏色怠益,然后v_TextureCoordinates就是告訴我們這些顏色要畫在什么位置的坐標(biāo),最后通過texture2D()方法獲得我們在當(dāng)前點要繪制的顏色瘾婿。
書中講到這里是重構(gòu)了一遍代碼蜻牢,其實如果看過前面兩篇文章的大概應(yīng)該是知道如何去渲染了圖片了,為了后面一篇文章做準(zhǔn)備偏陪,我還是把書中的重構(gòu)過程講一遍吧抢呆。
首先我們把繪制的對象的架構(gòu)改成下圖的樣子:

對象架構(gòu).png

我們創(chuàng)建一個data包,然后創(chuàng)建VertexArray.java類實現(xiàn)如下代碼:

public class VertexArray {

    private final FloatBuffer mFloatBuffer;

    public VertexArray(float[] data){
        mFloatBuffer = ByteBuffer
                .allocateDirect(Constants.BYTE_PRE_FLOAT * data.length)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        mFloatBuffer.put(data);
    }

    public void setVertexAttribPointer(int dataOffset, int attributeLocation, int componentCount, int stride){
        mFloatBuffer.position(dataOffset);

        glVertexAttribPointer(attributeLocation, componentCount, GL_FLOAT,
                false, stride, mFloatBuffer);
        glEnableVertexAttribArray(attributeLocation);

        mFloatBuffer.position(0);
    }

}

在我們創(chuàng)建Mallet和Table之前笛谦,我們要先創(chuàng)建Shader類抱虐,因為我們想在Mallet和Table中實現(xiàn)繪制功能,所以我們先把Shader實現(xiàn)這部分代碼抽離出來饥脑,整體架構(gòu)如下:

Shader架構(gòu).png

我們先在ShaderHelper.java中添加如下代碼:

public static int buildProgram(String vertexShaderCode , String fragmentShaderCode){

        int vertexShaderObjectId = compileVertexShader(vertexShaderCode);
        int fragmentShaderObjectId = compileFragmentShader(fragmentShaderCode);

        int program = linkProgram(vertexShaderObjectId , fragmentShaderObjectId);

        vaildProgram(program);

        return program;
    }

然后新建一個包programs并創(chuàng)建類ShaderProgram.java實現(xiàn)如下代碼:

public class ShaderProgram {

    // Uniform constants
    protected static final String U_MATRIX = "u_Matrix";
    protected static final String U_TEXTURE_UNIT = "u_TextureUnit";
    // Attribute constants
    protected static final String A_POSITION = "a_Position";
    protected static final String A_COLOR = "a_Color";
    protected static final String A_TEXTURE_COORDINATES = "a_TextureCoordinates";
    // Shader program
    protected final int mProgram;
    protected ShaderProgram(Context context, int vertexShaderResourceId,
                            int fragmentShaderResourceId) {
        // Compile the shaders and link the program.
        mProgram = ShaderHelper.buildProgram(
                TextResouceReader.readTextFileFromResource(context, vertexShaderResourceId),
                TextResouceReader.readTextFileFromResource(context, fragmentShaderResourceId));
    }

    public void useProgram() {
        // Set the current OpenGL shader program to this program.
        glUseProgram(mProgram);
    }

}

在該包下繼續(xù)創(chuàng)建TextureShaderProgram.java類實現(xiàn)如下代碼:

public class TextureShaderProgram extends ShaderProgram{

    // Uniform locations
    private final int mUMatrixLocation;
    private final int mUTextureUnitLocation;
    // Attribute locations
    private final int mAPositionLocation;
    private final int mATextureCoordinatesLocation;

    public TextureShaderProgram(Context context) {
        super(context, R.raw.texture_vertex_shader, R.raw.texture_fragment_shader);

        mUMatrixLocation = glGetUniformLocation(mProgram , U_MATRIX);
        mUTextureUnitLocation = glGetUniformLocation(mProgram , U_TEXTURE_UNIT);

        mAPositionLocation = glGetAttribLocation(mProgram , A_POSITION);
        mATextureCoordinatesLocation = glGetAttribLocation(mProgram , A_TEXTURE_COORDINATES);
    }

    public void setUniforms(float[] matrix, int textureId) {
        //把矩陣傳遞給Shader程序
        glUniformMatrix4fv(mUMatrixLocation, 1, false, matrix, 0);
        // Set the active texture unit to texture unit 0.
        glActiveTexture(GL_TEXTURE0);
        // Bind the texture to this unit.
        glBindTexture(GL_TEXTURE_2D, textureId);
        //告訴Texture Uniform Sampler用這個位置的Texture去渲染恳邀,從0開始
        glUniform1i(mUTextureUnitLocation, 0);
    }

    public int getPositionAttributeLocation() {
        return mAPositionLocation;
    }
    public int getTextureCoordinatesAttributeLocation() {
        return mATextureCoordinatesLocation;
    }


}

主要注意setUniforms()這塊的代碼懦冰,這塊代碼就是用Texture去渲染的方法了。我們再在該包下面創(chuàng)建ColorShaderProgram.java實現(xiàn)如下代碼:

public class ColorShaderProgram extends ShaderProgram{

    // Uniform locations
    private final int mUMatrixLocation;
    // Attribute locations
    private final int mAPositionLocation;
    private final int mAColorLocation;

    public ColorShaderProgram(Context context) {
        super(context, R.raw.simple_vertex_shader, R.raw.simple_fragment_shader);

        mUMatrixLocation = glGetUniformLocation(mProgram , U_MATRIX);

        mAColorLocation = glGetAttribLocation(mProgram , A_COLOR);
        mAPositionLocation = glGetAttribLocation(mProgram , A_POSITION);
    }


    public void setUniforms(float[] matrix) {
        // 把矩陣傳遞給渲染程序
       glUniformMatrix4fv(mUMatrixLocation, 1, false, matrix, 0);
    }

    public int getPositionAttributeLocation() {
        return mAPositionLocation;
    }
    public int getColorAttributeLocation() {
        return mAColorLocation;
    }
}

這部分代碼就不用多做解釋了轩娶,現(xiàn)在我們就可以去創(chuàng)建我們的Table儿奶、Mallet了,
先創(chuàng)建Table.java鳄抒,實現(xiàn)如下代碼:

public class Table {

    private final VertexArray mVertexData;

    private static final int POSITION_COMPONENT_COUNT = 2;
    private static final int TEXTURE_COORDINATES_COMPONENT_COUNT = 2;
    private static final int STRIDE = (POSITION_COMPONENT_COUNT
            + TEXTURE_COORDINATES_COMPONENT_COUNT) * Constants.BYTE_PRE_FLOAT;

    private static final float[] VERTEX_DATA = new float[]{
            // Order of coordinates: X, Y, S, T

            // Triangle Fan
            0f,    0f, 0.5f, 0.5f,
            -0.5f, -0.8f,   0f, 0.9f,
            0.5f, -0.8f,   1f, 0.9f,
            0.5f,  0.8f,   1f, 0.1f,
            -0.5f,  0.8f,   0f, 0.1f,
            -0.5f, -0.8f,   0f, 0.9f
    };

    public Table(){
        mVertexData = new VertexArray(VERTEX_DATA);
    }

    public void bindData(TextureShaderProgram program){
        mVertexData.setVertexAttribPointer(0 ,
                program.getPositionAttributeLocation() ,
                POSITION_COMPONENT_COUNT , STRIDE);
        mVertexData.setVertexAttribPointer(POSITION_COMPONENT_COUNT ,
                program.getTextureCoordinatesAttributeLocation() ,
                TEXTURE_COORDINATES_COMPONENT_COUNT , STRIDE);
    }

    public void draw(){
        glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
    }

}

再創(chuàng)建Mallet.java闯捎,實現(xiàn)如下代碼:

public class Mallet {

    private static final int POSITION_COMPONENT_COUNT = 2;
    private static final int COLOR_COMPONENT_COUNT = 3;
    private static final int STRIDE = (POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT)
            * Constants.BYTE_PRE_FLOAT;

    private static final float[] VERTEXT_DATA = new float[]{
            // Order of coordinates: X, Y, R, G, B
            0f, -0.4f, 0f, 0f, 1f,
            0f, 0.4f, 1f, 0f, 0f
    };

    private VertexArray mVertexData;

    public Mallet(){
        mVertexData = new VertexArray(VERTEXT_DATA);
    }

    public void bindData(ColorShaderProgram program){
        mVertexData.setVertexAttribPointer(0 ,
                program.getPositionAttributeLocation() ,
                POSITION_COMPONENT_COUNT , STRIDE);
        mVertexData.setVertexAttribPointer(
                POSITION_COMPONENT_COUNT ,
                program.getColorAttributeLocation(),
                COLOR_COMPONENT_COUNT , STRIDE);
    }

    public void draw(){
        glDrawArrays(GL_POINTS, 0, 2);
    }

}

準(zhǔn)備工作終于做好了,我們可以開始繪制了许溅,因為在Renderer中基本要刪掉所有代碼瓤鼻,所以我們直接新建一個好了叫做TextrueRenderer.java,并實現(xiàn)如下代碼:

public class TextureRenderer implements GLSurfaceView.Renderer{

    private final Context mContext;

    private float[] mProjectionMatrix = new float[16];
    private float[] mModelMatrix = new float[16];

    private Table mTable;
    private Mallet mMallet;

    private TextureShaderProgram mTextureShaderProgram;
    private ColorShaderProgram mColorShaderProgram;

    private int mTexture;

    public TextureRenderer(Context context){
        mContext = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

        mTable = new Table();
        mMallet = new Mallet();

        mTextureShaderProgram = new TextureShaderProgram(mContext);
        mColorShaderProgram = new ColorShaderProgram(mContext);

        mTexture = TextureHelper.loadTexture(mContext , R.drawable.air_hockey_surface);
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
        glViewport(0 , 0 , width , height);

        MatrixHelper.perspectiveM(mProjectionMatrix , 45 , (float)width / (float)height , 1f , 10f);

        setIdentityM(mModelMatrix, 0);
        translateM(mModelMatrix, 0, 0f, 0f, -2.5f);
        rotateM(mModelMatrix, 0 , -60 , 1 , 0 , 0);

        final float[] temp = new float[16];
        multiplyMM(temp, 0, mProjectionMatrix, 0, mModelMatrix, 0);
        System.arraycopy(temp, 0, mProjectionMatrix, 0, temp.length);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        glClear(GL_COLOR_BUFFER_BIT);

        mTextureShaderProgram.useProgram();
        mTextureShaderProgram.setUniforms(mProjectionMatrix , mTexture);
        mTable.bindData(mTextureShaderProgram);
        mTable.draw();

        mColorShaderProgram.useProgram();
        mColorShaderProgram.setUniforms(mProjectionMatrix);
        mMallet.bindData(mColorShaderProgram);
        mMallet.draw();

    }
}

然后現(xiàn)在運行一遍贤重,你就能看到我們效果圖的效果了茬祷。

項目代碼在這里:https://github.com/KevinKmoo/AirHockey3DWithTexture

能力有限,自己讀書的學(xué)習(xí)所得并蝗,有錯誤請指導(dǎo)祭犯,輕虐!
轉(zhuǎn)載請注明出處滚停。----by kmoo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沃粗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子键畴,更是在濱河造成了極大的恐慌最盅,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件起惕,死亡現(xiàn)場離奇詭異涡贱,居然都是意外死亡,警方通過查閱死者的電腦和手機惹想,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門问词,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嘀粱,你說我怎么就攤上這事激挪。” “怎么了草穆?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵灌灾,是天一觀的道長搓译。 經(jīng)常有香客問我悲柱,道長,這世上最難降的妖魔是什么些己? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任豌鸡,我火速辦了婚禮嘿般,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涯冠。我一直安慰自己炉奴,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布蛇更。 她就那樣靜靜地躺著瞻赶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪派任。 梳的紋絲不亂的頭發(fā)上砸逊,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音掌逛,去河邊找鬼师逸。 笑死,一個胖子當(dāng)著我的面吹牛豆混,可吹牛的內(nèi)容都是我干的篓像。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼皿伺,長吁一口氣:“原來是場噩夢啊……” “哼员辩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起心傀,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤屈暗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后脂男,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體养叛,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年宰翅,在試婚紗的時候發(fā)現(xiàn)自己被綠了弃甥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡汁讼,死狀恐怖淆攻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嘿架,我是刑警寧澤瓶珊,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站耸彪,受9級特大地震影響伞芹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一唱较、第九天 我趴在偏房一處隱蔽的房頂上張望琅绅。 院中可真熱鬧劫灶,春花似錦冰蘑、人聲如沸厌秒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纸镊。三九已至,卻和暖如春概疆,著一層夾襖步出監(jiān)牢的瞬間薄腻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工届案, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留庵楷,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓楣颠,卻偏偏與公主長得像尽纽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子童漩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

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