Android OpenGL ES 翻譯

官方文檔地址

Android包含Open Graphics Library(OpenGL?)庫,特別是OpenGL ES API,以支持高性能的2D和3D圖形。OpenGL是一個跨平臺的圖形處理API, 為3D圖形處理硬件指定了一個標準的軟件接口。OpenGL ES是OpenGL專門針對嵌入式設備制定的規范。Android 支持幾個版本的OpenGL ES API:

  • OpenGL ES 1.0 和 1.1 - 這個API規范的版本在Android 1.0以及更高的版本支持。
  • OpenGL ES 2.0 - 這個API規范的版本在Android 2.2(API 級別 8)以及更高的版本支持。
  • OpenGL ES 3.0 - 這個API規范的版本在Android 4.3(API 級別 18)以及更高的版本支持。
  • OpenGL ES 3.1 - 這個API規范的版本在Android 5.0(API 級別 21)以及更高的版本支持。

警告:在設備上使用OpenGL ES 3.0 API需要實現設備制造商提供的此圖形管道。運行Android 4.3或更高版本的設備可能不支持OpenGL ES 3.0 API。有關在運行時檢查支持的OpenGL ES版本信息,請參閱檢查OpenGL ES版本

注意:Android框架提供的API類似于J2ME JSR239 OpenGL ES API,但不完全相同。如果您熟悉J2ME JSR239規范,請注意它們的變化。

OpenGL ES 1.0的投影和相機視圖

其他說明:

基礎

android框架APINDK(Native Development Kit)都支持OpenGL。這里主要關注的是Android框架接口。想了解更多關于NDK的信息,請參見Android NDK

Android框架中有兩個基礎類:GLSurfaceViewGLSurfaceView.Renderer,可讓您使用OpenGL ES API創建和操作圖形。如果你的目的是在你的安卓應用程序中使用OpenGL,那么弄明白如何在activity中實現這兩個類應該是你的首要目標

GLSurfaceView

這個類是一個view,你可以調用 OpenGL API 繪制、操作GLSurfaceView的對象,GLSurfaceView的功能和SurfaceView類似。使用GLSurfaceView需要創建GLSurfaceView的實例并向它添加一個Renderer。如果你想要獲取屏幕觸摸事件,你應該像教程響應觸摸事件中演示的那樣擴展GLSurfaceView類實現觸摸監聽。

GLSurfaceView.Renderer

此接口定義了在GLSurfaceView中繪制圖形所需的方法。你必須提供一個類實現這個接口,并且創建一個實例,然后使用GLSurfaceView.setRenderer()添加到你的GLSurfaceView上。

GLSurfaceView.Renderer接口需要您實現以下方法:

  • onSurfaceCreated():在創建GLSurfaceView時,系統會調用此方法一次。在這個方法執行僅需要執行一次的操作,例如設置OpenGL環境參數或初始化OpenGL圖形對象。
  • onDrawFrame():系統在每次重繪GLSurfaceView時調用此方法。這個方法主要用來繪制(和重繪)圖形。
  • onSurfaceChanged():當GLSurfaceView 形狀發生變化時調用此方法。包括GLSurfaceView的大小或者設備屏幕方向發生改變。例如:當設備由縱向更改為橫向時會調用此方法。使用這個方法響應GLSurfaceView容器的更改。

OpenGL ES packages

一旦你使用GLSurfaceViewGLSurfaceView.Renderer創建OpenGL ES容器后,你就可以使用下面的類調用OpenGLAPI

OpenGL ES 1.0/1.1 API Packages

OpenGL ES 2.0 API Class

  • android.opengl.GLES20 - 這個包提供OpenGL ES 2.0的接口。從Android 2.2 (API level 8)開始可用

OpenGL ES 3.0/3.1 API Packages

如果您想立即開始使用OpenGL ES構建應用程序,請查看使用OpenGL ES顯示圖形

聲明OpenGL要求

如果您的應用程序使用的OpenGL功能不是所有的設備都有,則必須在AndroidManifest.xml文件中包含這些要求。以下是最常見的OpenGL清單聲明:

  • OpenGL ES版本要求 - 如果您的應用程序需要特定版本的OpenGL ES,則必須像下面演示的一樣通過向清單文件添加以下設置來聲明該要求。

對于OpenGL ES 2.0:

<!-- Tell the system this app requires OpenGL ES 2.0. -->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

添加此聲明會導致Google Play限制您的應用程序安裝在不支持OpenGL ES 2.0的設備上。如果您的應用程序只能用于支持OpenGL ES 3.0的設備,您還可以在清單中指定:

對于OpenGL ES 3.0:

<!-- Tell the system this app requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />

對于OpenGL ES 3.1:

<!-- Tell the system this app requires OpenGL ES 3.1. -->
<uses-feature android:glEsVersion="0x00030001" android:required="true" />

注意: OpenGL ES 3.x API向后兼容2.0API,這意味著您可以更靈活地在應用程序中實現OpenGL ES。通過在清單中聲明OpenGL ES 2.0 API作為要求,您可以將該API版本用作默認值,在運行時檢查3.x API的可用性,如果設備支持則使用OpenGL ES 3.x。有關檢查設備支持的OpenGL ES版本的更多信息,請參閱檢查OpenGL ES版本

  • 紋理壓縮要求 - 如果您的應用程序使用紋理壓縮格式,則必須使用<supports-gl-texture>在清單文件中聲明應用程序支持的格式。有關可用紋理壓縮格式的詳細信息,請參閱紋理壓縮支持
    對于不支持任何一種你在清單文件中聲明的紋理壓縮格式的設備,Google play會對該用戶隱藏你的應用。
    在清單中聲明紋理壓縮要求會使用不支持至少一種聲明壓縮類型的設備的用戶隱藏應用程序。有關Google Play如何過濾用于紋理壓縮的詳細信息,請參Google Play的紋理壓縮過濾關于<supports-gl-texture>的文檔。

映射繪制對象的坐標

在安卓設備上現實圖形的一個基本問題是它們的屏幕大小和形狀都可能不同。OpenGL默認情況下采用方形,均勻的坐標系統,可以將這些圖形坐標繪制到普通的非方形屏幕上,就好像它是完美的正方形一樣。


image

圖1:默認的OpenGL坐標系統(左)映射到典型的安卓設備屏幕(右)

上圖顯示了左側OpenGL框架的統一坐標系統,以及這些坐標如何實際映射到右側橫向的典型設備屏幕。要解決此問題,您可以應用OpenGL投影模式和攝像機視圖來轉換坐標,以便您的圖形對象在任何顯示上都具有正確的比例。
為了應用投影和攝像機視圖,您可以創建投影矩陣和攝像機視圖矩陣,并將它們應用于OpenGL渲染管道。投影矩陣重新計算圖形的坐標,以便它們正確映射到Android設備屏幕。攝像機視圖矩陣創建一個從特定視角變換的渲染對象。

OpenGL ES 1.0中的投影和相機視圖

ES 1.0 API中,您可以通過創建每個矩陣然后將它們添加到OpenGL環境來應用投影和攝像機視圖。

  1. 投影矩陣 -
    使用設備屏幕的幾何圖形創建投影矩陣,以便重新計算對象坐標按照正確的比例繪制它們。以下示例代碼演示了如何修改GLSurfaceView.Renderer實現的onSurfaceChanged()方法,以根據屏幕的寬高比創建投影矩陣,并將其應用于OpenGL渲染環境。
override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
    gl.apply {
        glViewport(0, 0, width, height)

        // make adjustments for screen ratio
        val ratio: Float = width.toFloat() / height.toFloat()

        glMatrixMode(GL10.GL_PROJECTION)            // set matrix to projection mode
        glLoadIdentity()                            // reset the matrix to its default state
        glFrustumf(-ratio, ratio, -1f, 1f, 3f, 7f)  // apply the projection matrix
    }
}
  1. 相機變換矩陣 - 使用投影矩陣調整坐標系后,還必須應用攝像機視圖。以下示例代碼顯示如何修改GLSurfaceView.Renderer實現的onDrawFrame()方法以應用模型視圖并使用GLU.gluLookAt()實用程序去創建一個攝像機位置的模擬查看轉換。
override fun onDrawFrame(gl: GL10) {
    ...
    gl.apply {
        // Set GL_MODELVIEW transformation mode
        glMatrixMode(GL10.GL_MODELVIEW)
        glLoadIdentity()                     // reset the matrix to its default state
    }

    // When using GL_MODELVIEW, you must set the camera view
    GLU.gluLookAt(gl, 0f, 0f, -5f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
    ...
}

OpenGL ES 2.0及更高版本中的投影和相機視圖

ES 2.03.0 API中使用投影和攝像機視圖,首先將矩陣成員添加到圖形對象的頂點著色器。添加此矩陣成員后,您可以生成投影并且將投影和相機視圖矩陣應用于你的對象。

  1. 將矩陣添加到頂點著色器 - 創建一個投影矩陣變量,并將其包含為著色器位置的乘數。在以下示例頂點著色器代碼中,包含的uMVPMatrix成員允許您將投影和camera viewing矩陣應用于使用此著色器的對象的坐標。
private val vertexShaderCode =

    // This matrix member variable provides a hook to manipulate
    // the coordinates of objects that use this vertex shader.
    "uniform mat4 uMVPMatrix;   \n" +

    "attribute vec4 vPosition;  \n" +
    "void main(){               \n" +
    // The matrix must be included as part of gl_Position
    // Note that the uMVPMatrix factor *must be first* in order
    // for the matrix multiplication product to be correct.
    " gl_Position = uMVPMatrix * vPosition; \n" +

    "}  \n"

注意: 上面的示例在頂點著色器中定義了一個變換矩陣成員,您可以在其中應用projection矩陣和camera view矩陣。根據您的應用需求,您可能希望在頂點著色器中定義單獨的投影矩陣和camera viewing矩陣成員,以便您可以單獨更改它們。

  1. 訪問著色器矩陣 - 在頂點著色器中創建鉤子以應用投影和攝像機視圖后,您可以訪問該變量以應用投影和camera viewing矩陣。以下代碼顯示如何修改GLSurfaceView.Renderer實現的onSurfaceCreated()方法以訪問上面頂??點著色器中定義的矩陣變量。
override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
    ...
    muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
    ...
}
  1. 創建投影和camera viewing矩陣 - 生成要應用于圖形對象的投影和查看矩陣。以下示例代碼顯示如何修改GLSurfaceView.Renderer實現的onSurfaceCreated()onSurfaceChanged()方法,以根據設備的屏幕寬高比創建camera view矩陣和投影矩陣。
override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
    ...
    // Create a camera view matrix
    Matrix.setLookAtM(mVMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
}

override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
    GLES20.glViewport(0, 0, width, height)

    val ratio: Float = width.toFloat() / height.toFloat()

    // create a projection matrix from device screen geometry
    Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
}
  1. 應用投影和camera viewing矩陣 - 要應用投影和攝像機視圖變換,請將矩陣相乘,然后將它們設置為頂點著色器。以下示例代碼顯示如何修改GLSurfaceView.Renderer實現的onDrawFrame()方法,以組合在上面的代碼中創建的投影矩陣和camera view,然后將其應用于要由OpenGL渲染的圖形對象。
override fun onDrawFrame(gl: GL10) {
    ...
    // Combine the projection and camera view matrices
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0)

    // Apply the combined projection and camera view transformations
    GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0)

    // Draw objects
    ...
}

有關如何使用OpenGL ES 2.0應用投影和攝像機視圖的完整示例,請參閱使用OpenGL ES類顯示圖形

Shape faces and winding

在OpenGL中,形狀的面是由三維空間中的三個或更多個點定義的表面。一組三個或更多個三維點(在OpenGL中稱為頂點)具有正面和背面。你怎么知道哪個面朝前,哪個面朝后?好問題。答案與纏繞或者定義形狀的點的方向有關。


image

圖 1. 坐標列表的圖示,該列表展示逆時針繪圖順序。

在該示例中,三角形點的定義順序使得它們以逆時針方向繪制。繪制這些坐標的順序定義了形狀的纏繞方向。默認情況下,在OpenGL中,逆時針繪制的面是正面。圖1中所示的三角形您看到的是三角形的正面(由OpenGL解釋),另一面是背面。
為什么知道形狀的哪個面是正面很重要?答案與OpenGL的常用功能有關,稱為面部剔除。面部剔除是OpenGL環境的一個選項,它允許渲染管道忽略(不計算或繪制)形狀的背面,從而節省時間、內存和處理周期:

gl.apply {
    // enable face culling feature 
    glEnable(GL10.GL_CULL_FACE)
    // specify which faces to not draw
    glCullFace(GL10.GL_BACK)
}

如果您嘗試使用面部剔除功能而不知道形狀的哪一側是正面和背面,那么您的OpenGL圖形看起來會有點薄,或者可能根本不會顯示。所以,始終以逆時針的順序定義OpenGL形狀的坐標

注意:可以將OpenGL環境設置為將順時針面作為正面,但這樣做需要更多代碼,并且當您向經驗豐富的OpenGL開發人員尋求幫助時可能會使他們感到困惑。所以不要這樣做。

OpenGL 版本和設備兼容性

Android 1.0開始支持OpenGL ES1.0和1.1的規范。Android框架從2.2(lv8)開始支持OpenGL ES 2.0的API規范。大多數Android設備都支持OpenGL ES 2.0,建議新開發的使用OpenGL的應用程序使用OpenGL2.0開發。對于提供了OpenGL ES 3.0 API 實現的設備,Android 4.3(lv 18)以及更高的版本支持OpenGL ES 3.0 API。有關支持特定 OpenGL ES 版本的設備的比例信息,請參閱OpenGL ES平臺版本

使用OpenGL ES 1.0 / 1.1 API進行圖形編程與使用2.0及更高版本有很大不同。 API的1.x版本具有更多便利方法和固定圖形管道,而OpenGL ES 2.0和3.0 API通過使用OpenGL著色器提供更直接的管道控制。 您應該仔細考慮圖形要求,并選擇最適合您應用的API版本。 有關更多信息,請參閱選擇OpenGL API版本

OpenGL ES 3.0 API提供了比2.0 API更多的功能和更好的性能,并且也向后兼容。 這意味著您可以編寫針對OpenGL ES 2.0的應用程序,并有條件地包含OpenGL ES 3.0圖形功能(如果可用)。 有關檢查3.0 API可用性的更多信息,請參閱檢查OpenGL ES版本

紋理壓縮支持

紋理壓縮可以減少內存需求和更有效地利用內存帶寬來顯著提高OpenGL應用程序的性能。 Android框架提供對ETC1壓縮格式的支持,作為標準功能,包括ETC1Util實用程序類和etc1tool壓縮工具(位于Android SDK中:<sdk>/tools/)。 有關使用紋理壓縮的Android應用程序的示例,請參閱Android SDK中的CompressedTextureActivity代碼示例(<sdk>/samples/<version>/ApiDemos/src/com/example/android/apis/graphics/)。

注意:大多數Android設備都支持ETC1格式,但不保證可用。要檢查設備是否支持ETC1格式,請調用ETC1Util.isETC1Supported()方法。

注意:ETC1紋理壓縮格式不支持具有透明度(alpha通道)的紋理。如果您的應用程序需要具有透明度的紋理,則應調查目標設備上其他可用的紋理壓縮格式。

OpenGL ES 3.0 API 可用的一定支持ETC2/EAC紋理壓縮格式。這種紋理格式提供出色的壓縮比和高視覺質量,格式還支持透明度(alpha通道)

除了ETC格式之外,Android設備還支持基于GPU芯片組和OpenGL實現的紋理壓縮。 您應該調查要定位的設備上的紋理壓縮支持,以確定應用程序應支持的壓縮類型。 為了確定給定設備支持哪種紋理格式,您必須查詢設備并查看OpenGL擴展名,該名稱用于標識設備支持的紋理壓縮格式(以及其他OpenGL功能)。 一些常用的紋理壓縮格式如下:

  • ATITC (ATC) - ATI紋理壓縮(ATITC或ATC)可在各種設備上使用,并支持對帶有和不帶alpha通道的RGB紋理進行固定速率壓縮。該格式可以由幾個OpenGL擴展名表示, 例如:

    • GL_AMD_compressed_ATC_texture
    • GL_ATI_texture_compression_atitc
  • **PVRTC **- PowerVR紋理壓縮(PVRTC)可在各種設備上使用,并支持每像素2位和4位紋理,帶或不帶alpha通道。 此格式由以下OpenGL擴展名表示:

    • GL_IMG_texture_compression_pvrtc
  • S3TC (DXTn/DXTC) - S3紋理壓縮(S3TC)具有多種格式變體(DXT1至DXT5),并且不太廣泛可用。 該格式支持具有4位alpha或8位alpha通道的RGB紋理。 這些格式由以下OpenGL擴展名表示:

    • GL_EXT_texture_compression_s3tc
      有些設備僅支持DXT1格式變化;此有限支持由以下OpenGL擴展名表示:
    • GL_EXT_texture_compression_dxt1
  • 3DC - 3DC紋理壓縮(3DC)是一種不太廣泛使用的格式,支持帶有Alpha通道的RGB紋理。此格式由以下OpenGL擴展名表示:

    • GL_AMD_compressed_3DC_texture

警告:并非所有設備都支持這些紋理壓縮格式。對這些格式的支持因制造商和設備而異。有關如何確定特定設備上的紋理壓縮格式的信息,請參閱下一節。

注意:確定應用程序支持的紋理壓縮格式后,請確保使用<supports-gl-texture>在清單中聲明它們。使用此聲明可以通過Google Play等外部服務進行過濾,以便您的應用僅安裝在支持應用所需格式的設備上。有關詳細信息,請參閱OpenGL清單聲明

確定OpenGL擴展

在支持OpenGL ES API的擴展方面,OpenGL的實現因Android設備而異。這些擴展包括紋理壓縮,但通常還包括OpenGL功能集的其他擴展。

確定特定設備支持哪種紋理壓縮格式和其他OpenGL擴展:

  1. 在目標設備上運行以下代碼以確定支持的紋理壓縮格式:
var extensions = gl.glGetString(GL10.GL_EXTENSIONS)

警告:此調用的結果因設備型號而異!您必須在多個目標設備上運行此調用,以確定通常支持的壓縮類型。

  1. 查看此方法的輸出以確定設備支持哪些OpenGL擴展。

Android擴展包(AEP)

AEP確保您的應用程序支持OpenGL 3.1規范中描述的核心集之上和之外的標準化OpenGL擴展集。將這些擴展包裝在一起可以促進跨設備的一致功能集,同時允許開發人員充分利用最新的移動GPU設備。
AEP還改進了片段著色器中對圖像、著色器存儲緩沖區和原子計數器的支持。
為了使您的應用能夠使用AEP,應用的清單必須聲明需要AEP。此外,平臺版本必須支持它。
在清單中聲明AEP要求如下:

<uses feature android:name="android.hardware.opengles.aep"
              android:required="true" />

要驗證平臺版本是否支持AEP,請使用hasSystemFeature(String)方法,并傳入FEATURE_OPENGLES_EXTENSION_PACK作為參數。以下代碼段顯示了如何執行此操作的示例:

var deviceSupportsAEP: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_OPENGLES_EXTENSION_PACK)

如果方法返回true,則支持AEP。
有關AEP的更多信息,請訪問Khronos OpenGL ES Registry的頁面。

檢查OpenGL ES版本

Android設備上有幾個版本的OpenGL ES。您可以在清單中指定應用程序所需的API的最低版本,但您可能還希望同時利用較新API中的功能。例如,OpenGL ES 3.0 API向后兼容API的2.0版本,因此您可能希望編寫應用程序以使其使用OpenGL ES 3.0功能,但如果3.0 API不可用,則可以回退到2.0。
在使用高于應用程序清單中所需最低版本的OpenGL ES功能之前,應用程序應檢查設備上可用API的版本。您可以通過以下兩種方式之一完成此操作:

  1. 嘗試創建更高級別的OpenGL ES上下文(EGLContext)并檢查結果。
  2. 創建最低支持的OpenGL ES上下文并檢查版本值。
    以下示例代碼演示了如何通過創建EGLContext并檢查結果來檢查可用的OpenGL ES版本。此示例顯示如何檢查OpenGL ES 3.0版本:
private const val EGL_CONTEXT_CLIENT_VERSION = 0x3098
private const val glVersion = 3.0
private class ContextFactory : GLSurfaceView.EGLContextFactory {

    override fun createContext(egl: EGL10, display: EGLDisplay, eglConfig: EGLConfig): EGLContext {

        Log.w(TAG, "creating OpenGL ES $glVersion context")
        return egl.eglCreateContext(
                display,
                eglConfig,
                EGL10.EGL_NO_CONTEXT,
                intArrayOf(EGL_CONTEXT_CLIENT_VERSION, glVersion.toInt(), EGL10.EGL_NONE)
        ) // returns null if 3.0 is not supported
    }
}

如果上面顯示的createContext()方法返回null,那么您的代碼應該創建一個OpenGL ES 2.0上下文,然后僅使用該API。

以下代碼示例演示如何通過首先創建最小支持的上下文,然后檢查版本字符串來檢查OpenGL ES版本:

// Create a minimum supported OpenGL ES context, then check:
gl.glGetString(GL10.GL_VERSION).also {
    Log.w(TAG, "Version: $it")
}
 // The version format is displayed as: "OpenGL ES <major>.<minor>"
 // followed by optional content provided by the implementation.

使用此方法,如果您發現設備支持更高級別的API版本,則必須銷毀最低OpenGL ES上下文并使用更高的可用API版本創建新上下文。

選擇OpenGL API版本

OpenGL ES 1.0 API版本(和1.1擴展),版本2.0和版本3.0都提供高性能圖形界面,用于創建3D游戲,可視化和用戶界面。 OpenGL ES 2.0和3.0的圖形編程大致相似,版本3.0代表具有附加功能的2.0 API的超集。 OpenGL ES 1.0/1.1 API與OpenGL ES 2.0和3.0的編程有很大不同,因此在開始使用這些API進行開發之前,開發人員應仔細考慮以下因素:

  • 性能 - 通常,OpenGL ES 2.0和3.0提供比ES 1.0/1.1 API更快的圖形性能。但是,由于硬件制造商對OpenGL ES圖形管道的實現存在差異,性能差異可能因運行OpenGL應用程序的Android設備而異。
  • 設備兼容性 - 開發人員應考慮客戶可用的設備類型,Android版本和OpenGL ES版本。有關跨設備的OpenGL兼容性的更多信息,請參閱OpenGL版本和設備兼容性部分。
  • 編碼方便性 - OpenGL ES 1.0/1.1 API提供了固定的功能管道和便利功能,這些功能在OpenGL ES 2.0或3.0 API中不可用。不熟悉OpenGL ES的開發人員可以更快,更方便地找到1.0/1.1版的編碼。
  • 圖形控制 - OpenGL ES 2.0和3.0 API通過使用著色器提供完全可編程的管道,從而提供更高程度的控制。通過更直接地控制圖形處理管道,開發人員可以創建使用1.0/1.1 API非常難以生成的效果。
  • 紋理支持 - OpenGL ES 3.0 API對紋理壓縮具有最佳支持,因為它保證了ETC2壓縮格式的可用性,該格式支持透明度。 1.x和2.0 API實現通常包括對ETC1的支持,但是這種紋理格式不支持透明度,因此您通常必須提供您所定位的設備支持的其他壓縮格式的資源。 有關更多信息,請參閱紋理壓縮支持。

雖然性能,兼容性,便利性,控制和其他因素可能會影響您的決策,但您應該根據您認為為用戶提供的最佳體驗選擇OpenGL API版本。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容