Android OpenGL 基礎入門

  Android 自從2.2 版本以後以後開始支持OpenGL,在沒有支持OpenGL 的 GPU的狀況下,也可使用(經過軟件來模擬)。在Android上使用Opengl操做的對象是GLSurfaceView,這是一個繼承自View的擴展。函數

  在Android上Opengl是經過Vertex Shader 和 Fragment Shader 這兩種定點着色器程序來實現圖片的加載和渲染的,中文稱爲定點着色器和片斷着色器。一個完整的Opengl程序須要建立定點着色器和片斷着色器並將他們Link起來組成一個完整的OpenGL程序。學習

  頂點着色器的做用是爲每個頂點生成座標,所以每一個頂點都要運行一遍頂點着色器程序,一旦頂點座標計算出來以後,OpenGL就可以使用這些頂點來組成點,線,和三角形。全部任意的圖形都是由這三種基本元素來描述的。下圖是頂點着色器進行座標轉換的過程(稍微有點複雜):spa

  

  這個過程包含了從原始的對象座標通過模型視圖轉換生成眼座標,再通過投影轉換生成裁剪座標,再經過w座標的歸一化轉換成爲NDC(頂點座標由(x,y,z,w)構成,在shader程序中通常用一個四維向量vec4來描述),最終經過視口變換生成屏幕座標顯示在屏幕上。這一系列的轉換都是經過矩陣來完成的,轉換過程和原理是Opengl的精華內容,對於須要進入3D世界的同窗須要掌握。在2D世界中咱們只須要了解NDC就好了,這裏就把NDC叫作OpenGL 座標,加上Normalized 的修飾是由於這些座標的值都在(-1,1)之間,OpenGL座標表示見下圖:3d

  OpenGL座標原點在屏幕中央,左右座標範圍爲[-1,1],在2d環境中座標值只存在(x,y),而且座標範圍都只能在-1 到 1之間,屏幕中心座標爲(0,0)。所以若是須要指定一張圖片的顯示位置,指定座標須要根據這個座標系來,此外爲了保證圖片顯示比例(長寬比例),在portrait 到 landscape 之間變換的時候通常須要乘以一個aspectRatio (width / height) 來從新設定座標值。code

  瞭解了Opengl座標,在來了解一下屏幕座標(屏幕座標的座標原點在左上角)和紋理座標(紋理座標的座標原點在左下角):orm

屏幕座標系                對象

  紋理座標系blog

  片斷着色器的做用是爲點,線或者三角形的每個頂點的片斷(Fragment)生成渲染後的最終顏色。片斷就是一個小的單色矩形區域,能夠簡單的認爲是屏幕上的一個像素點。繼承

  以上基本知識基本上能夠處理Opengl實現2D圖像的繪製和處理。下面來簡單看一下Shader的寫法,在Android平臺上Shader程序通常以字符串的形式出現,或者在res/raw/目錄下以*.glsl的格式出現,若是是以單獨的文件出現,須要定義專門的文件讀入接口來加載Shader程序。這裏就介紹一下字符串的形式的Shader程序,來看下面簡單的Vertex Shader 和 Fragment Shader:接口

    private static final String VERTEX_SHADER =
        "attribute vec4 a_position;\n" +
        "attribute vec2 a_texcoord;\n" +
        "varying vec2 v_texcoord;\n" +
        "void main() {\n" +
        "  gl_Position = a_position;\n" +
        "  v_texcoord = a_texcoord;\n" +
        "}\n";

    private static final String FRAGMENT_SHADER =
        "precision mediump float;\n" +
        "uniform sampler2D tex_sampler;\n" +
        "varying vec2 v_texcoord;\n" +
        "void main() {\n" +
        "  gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" +
        "}\n";

  先看一下VERTEX_SHADER,定義了兩種類型的變量 atrribute 和 varying。

  其中 attribute變量是隻能在vertex shader中使用的變量。(它不能在fragment shader中聲明attribute變量,也不能被fragment shader中使用),通常用attribute變量來表示一些頂點的數據,這些頂點數據包含了頂點座標,法線,紋理座標,頂點顏色等。應用中通常用函數glBindAttribLocation()來綁定每一個attribute變量的位置,而後用函數glVertexAttribPointer()爲每一個attribute變量賦值。

  varying被稱爲易變量,通常用於從Vertex Shader 向 Fragment Shader傳遞數據,上面例子中在VertexShader中定義了attribute 類型的二維向量a_texcoord,並將該值賦值給varying類型的二維向量 v_texcoord。此外對於Vertex Shader 在main() 中必須將頂點座標賦值給系統變量gl_Position。

  看一下FRAGMENT_SHADER,定義了兩種類型的變量,uniform 和 varying。此外還多了一句 precision mediump float,這句話用於定義數據精度,Opengl中能夠設置三種類型的精度(lowp,medium 和 highp),對於Vertex Shader來講,Opengl使用的是默認最高精度級別(highp),所以沒有定義。

  uniform變量是APP序傳遞給(vertex和fragment)shader的變量。經過函數glUniform**()函數賦值的。 在(vertex和fragment)shader程序內部,uniform變量就像是C語言裏面的常量(const ),它不能被shader程序修改(shader只能用,不能改)。若是uniform變量在vertex和fragment二者之間聲明方式徹底同樣,則它能夠在vertex和fragment共享使用。 (至關於一個被vertex和fragment shader共享的全局變量)uniform變量通常用來表示:變換矩陣,材質,光照參數和顏色等信息。

  對於Fragment Shader也需對gl_FragColor賦值。

  如今對基本的Shader有了瞭解,來看一下Android怎麼使用Shader程序。先說明一下Opengl中的資源通常都是用一個句柄(handle)來引用,句柄通常由gl***接口返回,表明一個特定的資源。

  在Android上使用gl,須要用到一些列接口,這裏按照通常的調用順序來列一下基本的接口(暫不包含錯誤處理)

  1. 和建立Shader相關的:glCreateShader -> glShaderSource -> glCompileShader , 經過這幾個接口的調用最終返回一個Shader的句柄

  2. 和建立Shader程序相關的(每一個shader程序都必須包含Vertex Shader 和 Fragment Shader兩部分):glCreateProgram -> glAttachShader -> glLinkProgram,經過這些接口的調用最終返回個Program的句柄。

  3. 獲取Shader內部變量賦值和傳遞數據到gl,對於不一樣類型的數據使用不一樣的接口:GLES20.glGetUniformLocation(mProgram, "tex_sampler"),GLES20.glGetAttribLocation(mProgram, "a_texcoord"),以上兩個接口中mProgram 表明glCreateProgram返回的 Shader程序句柄,「a_texcoord」和"tex_sampler"是 Vertex Shader和 Fragment Shader中定義的變量,經過這兩個接口獲取了變量的句柄,便於向這些變量中傳入值。得到變量的句柄以後就要向其中傳值,通常經過glVertexAttribPointer()來完成,詳細參數這裏沒有列出,在實例代碼中能夠學習一下,傳值以後須要使glEnableVertexAttribArray才能將數據傳入到gl中,傳入數據使用glDrawArrays() 。

  這裏介紹了Opengl的基本知識,下一篇結合實例介紹一下Opengl在Android上的使用。

相關文章
相關標籤/搜索