中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android中的OpenGL怎么配置使用

發布時間:2023-02-28 17:32:28 來源:億速云 閱讀:162 作者:iii 欄目:開發技術

這篇文章主要介紹“Android中的OpenGL怎么配置使用”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Android中的OpenGL怎么配置使用”文章能幫助大家解決問題。

介紹

Android 可通過開放圖形庫 OpenGL ES 來支持高性能 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)及更高版本的支持。

在 AndroidManifest.xml 中聲明 OpenGL ES 的版本

<uses-feature android:glEsVersion="0x00020000" android:required="true" />

GLSurfaceView

GLSurfaceViewSurfaceViewOpenGL實現,從 Android 1.5 開始加入,在 SurfaceView的基礎上添加了 EGL 的管理以及自帶的渲染線程 GLThread,其主要功能如下:

  • 管理一個Surface,這個Surface是一塊特殊的內存,可以組合到 Android 的 View 系統中,也就是可以和View一起使用。

  • 管理一個 EGL,這個EGL可以讓 OpenGL 渲染到這個Surface上,EGL是 Android 與 OpenGL之間的橋梁。

  • 支持用戶自定義渲染器Renderer對象。

  • 使用專用線程上進行渲染。

  • 支持按需渲染(on-demand)和連續渲染(continuous )。

  • Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.

EGL 窗口、OpenGL 表面、GL 表面含義都相同。

GLSurfaceView常用設置如下:

EGL配置

EGLConfigChooser的默認實現是SimpleEGLConfigChooser,默認情況下GLSurfaceView將選擇深度緩沖深度至少為 16 位的PixelFormat.RGB_888格式的 surface,默認的EGLConfigChooser實現是SimpleEGLConfigChooser,具體如下:

private class SimpleEGLConfigChooser extends ComponentSizeChooser {
    public SimpleEGLConfigChooser(boolean withDepthBuffer) {
        super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
    }
}

可以通過如下方式修改EGLConfig的默認行為:

// 設置默認EGLConfig的深度緩沖,true則為16位的深度緩沖
setEGLConfigChooser(boolean needDepth)
// 指定自定義的EGLConfigChooser
setEGLConfigChooser(android.opengl.GLSurfaceView.EGLConfigChooser configChooser)
// 指定各個分量的值
public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
            int alphaSize, int depthSize, int stencilSize)

渲染

通過setRenderer設置渲染器并啟動渲染線程GLThread,渲染模式有兩種如下:

  • RENDERMODE_CONTINUOUSLY:適合重復渲染的場景,默認的渲染模式。

  • RENDERMODE_WHEN_DIRTY:只有Surface被創建后渲染一次,只調用了requestRender才會繼續渲染。

渲染模式可以通過setRenderMode來進行設置,具體如下:

// 設置渲染器
public void setRenderer(Renderer renderer)
// 設置渲染模式,僅在setRenderer之后調用生效
public void setRenderMode(int renderMode)

setDebugFlags和setGLWrapper

setDebugFlags用于設置 Debug 標記,方便調試跟蹤代碼,可選值為DEBUG_CHECK_GL_ERRORDEBUG_LOG_GL_CALLSsetGLWrapper可以通過自定義GLWrapper來委托 GL 接口來添加一些自定義行為,具體如下:

// DEBUG_CHECK_GL_ERROR:每次GL調用都會檢查,如果出現glError則會拋出異常
// DEBUG_LOG_GL_CALLS:以TAG為GLSurfaceView將日志記錄在verbose級別的日志中
setDebugFlags(int debugFlags)
// 用于調試跟蹤代碼,可自定義GLWrapper包裝GL接口并返回GL接口,可在
setGLWrapper(android.opengl.GLSurfaceView.GLWrapper glWrapper)

渲染器Renderer

這部分在前面提到過,這里單獨說一下,要想在 GL 表面上執行渲染操作,需要實現Renderer對象完成實際渲染操作,通過如下方式給GLSurfaceView設置渲染器對象Renderer以及制定渲染模式,如下:

// 給GLSurfaceView設置渲染器對象Renderer
public void setRenderer(Renderer renderer)
// 設置渲染模式,僅在setRenderer之后調用生效
public void setRenderMode(int renderMode)

設置渲染器Renderer的時候,同時會創建獨立線程GLThread并開啟該線程,這個線程就是獨立于 UI 線程的渲染線程。

這里就涉及到兩個線程 UI 線程和渲染線程,自然涉及到線程之間的通信,可以使用 volatilesynchronized等實現線程之間的通信。

如果是在 UI 線程中調用渲染線程中的操作,可以使用GLSurfaceViewqueueEvent 方法來將該操作執行到渲染線程中,一般需要自定義GLSurfaceView的時候會用到,同樣如果在渲染線程可以通過runOnUiThread來將與 UI 相關的操作執行到 UI 線程。

下面看下渲染器Reander的基本實現:

public class GLES20Renderer implements Renderer {
    private static final String TAG = GLES20Renderer.class.getSimpleName();
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.i(TAG, "onSurfaceCreated");
        GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1);
    }
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.i(TAG, "onSurfaceChanged");
        GLES20.glViewport(0, 0, width, height);
    }
    public void onDrawFrame(GL10 gl) {
        Log.i(TAG, "onDrawFrame");
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    }
}

坐標映射

先來了解下 OpenGL 的世界坐標系和與之對應的 Android 上的紋理坐標系,如下圖所示:

Android中的OpenGL怎么配置使用

在 Android 中使用 OpenGL 就要進行相應坐標的轉換,下面看下 OpenGL 坐標系在 Android 屏幕中的映射關系,如下圖所示:

Android中的OpenGL怎么配置使用

如上圖所示,左側是默認的 OpenGL 坐標系,右側是 OpenGL 坐標系在 Android 屏幕上的映射,可以明顯看到圖中的三角形是變形了的,為了保證圖像比例就需要應用 OpenGL 投影模式和相機視圖來轉換坐標,這就涉及到投影矩陣和視圖矩陣,這部分內容會在后續的文章中介紹。

繪制三角形

通過以上內容,Android OpenGL 算是初步入門了,按照習慣來個小案例,這里使用 OpenGL 繪制一個三角形,如下Triangle是三角形數據封裝及著色器的的使用,后續渲染直接調用draw方法進行渲染繪制,如下:

// Triangle
class Triangle(context: Context) {
    companion object {
        // 坐標數組中每個頂點的坐標數
        private const val COORDINATE_PER_VERTEX = 3
    }
    private var programHandle: Int = 0
    private var positionHandle: Int = 0
    private var colorHandler: Int = 0
    private var vPMatrixHandle: Int = 0
    private var vertexStride = COORDINATE_PER_VERTEX * 4
    // 三角形的三條邊
    private var triangleCoordinate = floatArrayOf(     // 逆時針的順序的三條邊
        0.0f, 0.5f, 0.0f,      // top
        -0.5f, -0.5f, 0.0f,    // bottom left
        0.5f, -0.5f, 0.0f      // bottom right
    )
    // 顏色數組
    private val color = floatArrayOf(0.63671875f, 0.76953125f, 0.22265625f, 1.0f)
    private var vertexBuffer: FloatBuffer =
        // (number of coordinate values * 4 bytes per float)
        ByteBuffer.allocateDirect(triangleCoordinate.size * 4).run {
            // ByteBuffer使用本機字節序
            this.order(ByteOrder.nativeOrder())
            // ByteBuffer to FloatBuffer
            this.asFloatBuffer().apply {
                put(triangleCoordinate)
                position(0)
            }
        }
    init {
        // read shader sourceCode
        val vertexShaderCode = GLUtil.readShaderSourceCodeFromRaw(context, R.raw.vertex_shader_triangle_default)
        val fragmentShaderCode =
            GLUtil.readShaderSourceCodeFromRaw(context, R.raw.fragment_shader_triangle)
        if (vertexShaderCode.isNullOrEmpty() || fragmentShaderCode.isNullOrEmpty()) {
            throw RuntimeException("vertexShaderCode or fragmentShaderCode is null or empty")
        }
        // compile shader
        val vertexShaderHandler = GLUtil.compileShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShaderHandler =
            GLUtil.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
        // create and link program
        programHandle = GLUtil.createAndLinkProgram(vertexShaderHandler, fragmentShaderHandler)
    }
    /**
 	 *  繪制方法
 	 */
    fun draw(mvpMatrix: FloatArray) {
        GLES20.glUseProgram(programHandle)
        // 獲取attribute變量的地址索引
        // get handle to vertex shader's vPosition member
        positionHandle = GLES20.glGetAttribLocation(programHandle, "vPosition").also {
            // enable vertex attribute,默認是disable
            GLES20.glEnableVertexAttribArray(it)
            GLES20.glVertexAttribPointer(
                it, // 著色器中第一個頂點屬性的位置
                COORDINATE_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexStride, // 連續的頂點屬性組之間的間隔
                vertexBuffer
            )
        }
        // get handle to fragment shader's vColor member
        colorHandler = GLES20.glGetUniformLocation(programHandle, "vColor").also {
            GLES20.glUniform4fv(it, 1, color, 0)
        }
        // draw triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, triangleCoordinate.size / COORDINATE_PER_VERTEX)
        GLES20.glDisableVertexAttribArray(positionHandle)
    }
}

渲染器實現如下:

// 渲染器實現
class MRenderer(private var context: Context) : GLSurfaceView.Renderer {
    private val tag = MRenderer::class.java.simpleName
    private lateinit var triangle: Triangle
    private val vPMatrix = FloatArray(16) // 模型視圖投影矩陣
    private val projectionMatrix = FloatArray(16)
    private val viewMatrix = FloatArray(16)
    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
        // 創建Surface時調用,在渲染開始時調用,用來創建渲染開始時需要的資源
        Log.d(tag, "onSurfaceCreated")
        triangle = Triangle(context)
    }
    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        // Surface改變大小時調用,設置視口
        Log.d(tag, "onSurfaceChanged")
        GLES20.glViewport(0, 0, width, height)
    }
    override fun onDrawFrame(gl: GL10?) {
        // 繪制當前frame,用于渲染處理具體的內容
        Log.d(tag, "onDrawFrame")
        triangle.draw(vPMatrix)
    }
}

上面都是基本的繪制操作,沒啥好說的,其中著色器的使用流程會在后續文章中進行介紹,這里就不貼其他代碼了,感興趣的可以直接在文末查看源代碼。

繪制效果

上面的繪制沒有使用投影矩陣和相機視圖來進行坐標轉換,當橫豎屏切換到時候會到導致變形,這個會在下篇文章中進行修正,看下上述代碼繪制的效果圖,如下圖所示:

Android中的OpenGL怎么配置使用

關于“Android中的OpenGL怎么配置使用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武宁县| 淅川县| 南阳市| 精河县| 普兰店市| 孟津县| 岳阳市| 墨竹工卡县| 镇原县| 宁远县| 横峰县| 沈阳市| 柳州市| 运城市| 梧州市| 肃北| 昌乐县| 光泽县| 武川县| 崇仁县| 基隆市| 宝应县| 吐鲁番市| SHOW| 蚌埠市| 上林县| 楚雄市| 疏附县| 奈曼旗| 定州市| 海兴县| 黑河市| 施甸县| 永平县| 嘉善县| 红河县| 和平县| 丽水市| 左云县| 宜章县| 兴隆县|