官术网_书友最值得收藏!

3.1 OpenGL ES 3.x概述

隨著4G時代的到來,Android與iPhone已經(jīng)成為消費者購買智能手機的主要選擇。而由于基于Android的智能手機性能優(yōu)良、價格合適,因此Android智能手機得到了大多數(shù)用戶的青睞。

隨著Android系統(tǒng)版本及硬件水平的提升,OpenGL ES的版本也由原先支持自定義渲染管線的OpenGL ES 2.0逐漸升級為同樣是支持自定義渲染管線的OpenGL ES 3.x。OpenGL ES 3.x新特性的添加使渲染的3D場景光影效果更加真實,從而能夠創(chuàng)造全新的用戶體驗。

3.1.1 OpenGL ES 3.x簡介

現(xiàn)今較為知名的3D圖形API有OpenGL、DirectX以及OpenGL ES,其各自的應用領域如下。

? DirectX主要應用于Windows下游戲的開發(fā),在此領域基本上一統(tǒng)天下。

? OpenGL的應用領域較為廣泛,適用于UNIX、Mac OS、Linux以及Windows等幾乎所有的操作系統(tǒng),可以開發(fā)游戲、工業(yè)建模以及嵌入式設備應用。

? OpenGL ES是專門針對嵌入式設備而設計的,其實際是OpenGL的剪裁版本,去除了OpenGL中許多不是必須存在的特性,如GL_QUADS(四邊形)與GL_POLYGONS(多邊形)繪制模式以及glBegin(開始)/glEnd(結束)操作等。

經(jīng)過多年的發(fā)展,目前市面上使用的OpenGL ES主要分為兩個版本,基本情況如下。

? 一個是OpenGL ES 2.0,其采用的是可編程渲染管線,渲染能力大大提高。OpenGL ES 2.0要求設備中必須有相應的GPU硬件支持,不支持在設備上用軟件模擬實現(xiàn)(不用擔心,現(xiàn)在幾百元的低端設備也可以支持了)。如圖3-1所示的都市賽車6就是使用了OpenGL ES 2.0渲染技術。

▲圖3-1 都市賽車6場景

? 另一個是誕生不久的OpenGL ES 3.x(目前主要包括3.0、3.1、3.2),其采用的也是可編程渲染管線。其中OpenGL ES 3.0要求Android設備中必須有4.3或以上的Android版本和相應的GPU硬件支持,而OpenGL ES 3.1要求Android設備中必須有5.0或以上的Android版本和相應的GPU硬件支持。如圖3-2所示的狂野飆車8極速凌云采用的就是OpenGL ES 3.0渲染技術。

▲圖3-2 狂野飆車8場景

說明

圖3-1為使用OpenGL ES 2.0渲染的都市賽車6場景,從該圖中可以看出使用OpenGL ES 2.0可以渲染出比較真實的路面及光影效果。圖3-2為使用OpenGL ES 3.0渲染的狂野飆車8場景,在該圖可以看出使用OpenGL ES 3.0渲染出的游戲畫面更加逼真、細膩。

通過都市賽車6及狂野飆車8的效果圖上說明了OpenGL ES 2.0與OpenGL ES 3.x渲染能力的差異。接下來將通過一款足球系列游戲FIFA,再次比較OpenGL ES 2.0與OpenGL ES 3.x渲染能力的差異。使用OpenGL ES 2.0與OpenGL ES3.x渲染的FIFA效果分別如圖3-3和圖3-4所示。

▲圖3-3 OpenGL ES 2.0的渲染效果

▲圖3-4 OpenGL ES 3.x的渲染效果

說明

由上述兩幅渲染效果圖比較可以看出,在視覺上OpenGL ES 3.x渲染的3D游戲場景比OpenGL ES 2.0系列渲染的3D游戲更加逼真。

接著再介紹兩款使用OpenGL ES 3.x開發(fā)的3D游戲:聚爆(Implosion)與孤膽車神:維加斯,具體效果分別如圖3-5和圖3-6所示。這兩款3D游戲均是大場景游戲,畫面細膩、真實。

▲圖3-5 聚爆

▲圖3-6 孤膽車神:維加斯

說明

從圖3-5聚爆以及圖3-6孤膽車神:維加斯的效果圖中可以看出,由于OpenGL ES 3.x渲染技術有更多的緩沖區(qū)對象、增加了GLSL ES 3.x著色語言、增加計算著色器支持等,使游戲在細膩程度、真實感等方面均有了質的變化。

介紹完上述聚爆和孤膽車神:維加斯之后,不得不介紹大名鼎鼎的Gameloft旗下的兩款著名3D游戲,地牢獵手5和混沌與秩序2:救贖。這兩款3D游戲一經(jīng)發(fā)布便廣受游戲愛好者的好評,其效果分別如圖3-7和圖3-8所示。

▲圖3-7 地牢獵手5

▲圖3-8 混沌與秩序2:救贖

使用OpenGL ES 3.x不僅可以開發(fā)類似地牢獵手5和混沌與秩序2:救贖等大場景的3D游戲,還可以開發(fā)較為真實的人物動作類型游戲,例如由日本游戲廠商KONAMI推出的足球經(jīng)理新作—實況足球俱樂部經(jīng)理,如圖3-9所示。還有由美劇行尸走肉的版權方AMC電視臺聯(lián)合芬蘭游戲開發(fā)商Next Games一同打造的The Walking Dead: No Man's Land(《行尸走肉:無人地帶》),如圖3-10所示。

▲圖3-9 實況足球2016

▲圖3-10 行尸走肉:無人地帶

以前,想在智能手機上運行PC級的3D游戲,那簡直是奢望。但是現(xiàn)在,那種原來不可能實現(xiàn)的愿望已經(jīng)成為現(xiàn)實。隨著手機硬件水平的不斷提高、3D圖形API的逐漸完善等多方面的因素,一些只能在PC上玩的大型游戲在Android手機上也可以安裝并流暢運行了。

如EA的極品飛車,這款在PC上極其火爆的游戲,也已經(jīng)成功移植到Android手機上了,效果如圖3-11和圖3-12所示。

▲圖3-11 極品飛車13變速

▲圖3-12 極品飛車14

說明

圖3-11為EA開發(fā)的極品飛車13變速,圖3-12為EA開發(fā)的極品飛車14熱力追蹤。這兩款游戲均是以PC版的極品飛車13、14為原型,移植到Android手機中的。二者均使用的是OpenGL ES進行3D場景渲染。

3.1.2 初識OpenGL ES 3.0應用程序

前一小節(jié)簡單地介紹了OpenGL ES 3.x與OpenGL ES 2.0的能力差異,在本小節(jié)將通過一個旋轉三角形的簡單案例介紹如何使用OpenGL ES 3.0進行3D場景的開發(fā)。該案例的運行效果分別如圖3-13、圖3-14和圖3-15所示。

▲圖3-13 運行效果圖1

▲圖3-14 運行效果圖2

▲圖3-15 運行效果圖3

提示

請讀者特別注意,基于OpenGL ES 3.0的3D應用程序不能保證可以在模擬器上運行,所以建議讀者最好使用配置了GPU(GPU要求支持OpenGL ES 3.0)的真機(Android版本要求最低為4.3)運行本書中的案例。沒有真機的讀者想學習本書的內容可能需要購置或借用一部符合要求的真機了,關于GPU硬件的信息可以參考本章3.3節(jié)的相關內容。

說明

圖3-13為3D空間中三角形初始狀態(tài)的效果圖,圖3-14為3D空間中三角形繞x軸旋轉大約100°的效果圖,圖3-15為繞x軸旋轉大約180°的效果圖。

前面已經(jīng)介紹了本案例的運行效果,接下來將介紹本案例的具體開發(fā)步驟。

(1)在開發(fā)與本案例的功能直接相關的各種類之前,首先需要開發(fā)一個本書案例都需要用到的工具類ShaderUtil,其功能為將著色器(Shader)腳本加載進顯卡并編譯。最先開發(fā)的是該類中從著色器sh腳本中加載著色器內容的loadFromAssetsFile方法和檢查每一步操作是否有錯誤的checkGlError方法,實現(xiàn)的具體代碼如下。

代碼位置:見隨書中源代碼/第3章/Sample3_1/src/com/bn/Sample3_1目錄下的ShaderUtil.java。

      1     package com.bn.Sample3_1;                                           //聲明包名
      2     import java.io.ByteArrayOutputStream;                             //相關類的引入
      3     ……//此處省略了部分類的引入代碼,讀者可自行查看隨書的源代碼
      4     import android.util.Log;                            //相關類的引入
      5     public class ShaderUtil {                           //加載頂點與片元著色器的類
      6           public static int loadShader(int shaderType, String source ){}//加載指定著色器的方法
      7           //創(chuàng)建著色器程序的方法
      8               public static int createProgram(String vertexSource, String fragmentSource) { }
      9           public static void checkGlError(String op) {//檢查每一步操作是否有錯誤的方法
      10                int error;                                 // error變量
      11                     while  ((error  =  GLES30.glGetError())  ! =  GLES30.GL_NO_ERROR)  {
      12                      Log.e("ES30_ERROR", op + ": glError " + error); //后臺打印錯誤
      13                      throw new RuntimeException(op + ": glError " + error); //拋出異常
      14             }}
      15          //從sh腳本中加載著色器內容的方法
      16             public  static  String  loadFromAssetsFile(String  fname, Resources  r){
      17                String result=null;                 //聲明一個String類型的字符串變量
      18                     try{
      19                      InputStream in=r.getAssets().open(fname); //從assets文件夾中讀取信息
      20                      int ch=0;                       //定義一個int型變量
      21                              ByteArrayOutputStream  baos  =  new  ByteArrayOutputStream();
      22                              while((ch=in.read())! =-1){
      23                            baos.write(ch);                    //將獲取的信息寫入輸出流中
      24                              }
      25                              byte[]  buff=baos.toByteArray();
      26                      baos.close();                            //關閉輸出流
      27                              in.close();
      28                      result=new String(buff, "UTF-8");      //轉化為UTF-8編碼
      29                              result=result.replaceAll("\\r\\n", "\n");
      30                }catch(Exception e){                          //捕獲異常
      31                      e.printStackTrace();                    //打印異常
      32                     }
      33                return result;                                 //返回結果
      34     }}

? 第9-14行checkGlError方法的作用是在向GPU著色程序中加入頂點著色器或者片元著色器時,檢查每一步操作是否有錯誤。這是由于在開發(fā)著色器腳本文件中的代碼時,沒有一個開發(fā)環(huán)境實時地進行編譯、查錯,因此開發(fā)一個檢查錯誤的方法尤為重要。

? 第15-34行l(wèi)oadFromAssetsFile方法的作用為從項目根目錄的assets文件夾下加載著色器代碼腳本。其通過輸入流將腳本信息讀入,然后交給createProgram方法創(chuàng)建著色器程序。

(2)開發(fā)完加載著色器腳本內容的loadFromAssetsFile方法和檢查錯誤的checkGlError方法后,下面開發(fā)的是加載著色器編碼進入GPU并進行編譯的loadShader方法與創(chuàng)建著色器程序的createProgram方法,具體代碼如下。

代碼位置:見隨書中源代碼/第3章/Sample3_1/src/com/bn/Sample3_1目錄下的ShaderUtil.java。

      1     public static int loadShader(int shaderType, String source){//加載指定著色器的方法
      2           int shader = GLES30.glCreateShader(shaderType); //創(chuàng)建一個shader,并記錄其id
      3           if (shader ! = 0) {                                  //若創(chuàng)建成功則加載著色器
      4                 GLES30.glShaderSource(shader, source);     //加載著色器的源代碼
      5                 GLES30.glCompileShader(shader);             //編譯
      6                       int[]  compiled  =  new  int[1];
      7                 //獲取Shader的編譯情況
      8                       GLES30.glGetShaderiv(shader,  GLES30.GL_COMPILE_STATUS,  compiled,  0);
      9                 if (compiled[0] == 0) {            //若編譯失敗則顯示錯誤日志并刪除此shader
      10                              Log.e("ES30_ERROR", "Could not compile shader " + shaderType + ":");
      11                              Log.e("ES30_ERROR",  GLES30.glGetShaderInfoLog(shader));
      12                              GLES30.glDeleteShader(shader);
      13                              shader  =  0;
      14             }}
      15             return  shader;
      16     }
      17    //創(chuàng)建著色器程序的方法
      18     public  static  int  createProgram(String  vertexSource,  String  fragmentSource)  {
      19          //加載頂點著色器
      20             int  vertexShader  =  loadShader(GLES30.GL_VERTEX_SHADER,  vertexSource);
      21             if  (vertexShader  ==  0)  {return  0; }
      22          //加載片元著色器
      23             int  pixelShader  =  loadShader(GLES30.GL_FRAGMENT_SHADER,  fragmentSource);
      24             if  (pixelShader  ==  0)  {return  0; }
      25          int program = GLES30.glCreateProgram();               //創(chuàng)建程序
      26          if (program ! = 0) {        //若程序創(chuàng)建成功則向程序中加入頂點著色器與片元著色器
      27                GLES30.glAttachShader(program, vertexShader);  //向程序中加入頂點著色器
      28                     checkGlError("glAttachShader");
      29                GLES30.glAttachShader(program, pixelShader);   //向程序中加入片元著色器
      30                     checkGlError("glAttachShader");
      31                GLES30.glLinkProgram(program);         //鏈接程序
      32                int[] linkStatus = new int[1];         //存放鏈接成功program狀態(tài)值的數(shù)組
      33                     GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);
      34                if (linkStatus[0] ! = GLES30.GL_TRUE) {     //若鏈接失敗則報錯并刪除程序
      35                              Log.e("ES30_ERROR",  "Could  not  link  program:  ");
      36                              Log.e("ES30_ERROR",  GLES30.glGetProgramInfoLog(program));
      37                      GLES30.glDeleteProgram(program);      //刪除程序
      38                              program  =  0;
      39             }}
      40          return program;                                      //返回結果
      41     }

? 第1-16行加載指定著色器的loadShader方法中:第2行通過調用glCreateShader方法創(chuàng)建了一個著色器;如果著色器創(chuàng)建成功,第3-14行則加載著色器的源代碼,并編譯著色器,同時檢測編譯的情況。若編譯成功則返回著色器id,反之則刪除著色器并且打印錯誤信息。

? 第19-24行為通過調用loadShader方法,分別加載頂點著色器與片元著色器的源代碼進入GPU,并分別進行編譯的代碼。

? 第25-40行首先創(chuàng)建一個著色器程序,然后分別將相應的頂點與片元著色器添加到其中,最后將兩個著色器鏈接為一個整體的著色器程序。

提示

到目前為止,讀者可能還是對著色器一頭霧水,不用擔心,本章后面的內容將逐漸揭開著色器的面紗,這里讀者有一個簡單的印象即可。

(3)下面開始開發(fā)與本案例功能直接相關的類,首先需要開發(fā)的是本案例的主控制類Sample3_1Activity,該類繼承自Activity,在程序開始時執(zhí)行。該類的主要工作是創(chuàng)建MyTDView類的對象,然后調用setContentView方法跳轉到相關界面,其代碼如下。

代碼位置:見隨書中源代碼/第3章/Sample3_1/src/com/bn/Sample3_1目錄下的Sample3_1 Activity.java。

      1     package com.bn.Sample3_1;                                     //聲明包名
      2     ......//此處省略了導入類的代碼,讀者可自行查看隨書的源代碼
      3     public class Sample3_1Activity extends Activity{      //創(chuàng)建繼承Activity的主控制類
      4           MyTDView mview;                                      //聲明MyTDView類的引用
      5               @Override
      6           public void onCreate(Bundle savedInstanceState){//繼承Activity后重寫的方法
      7                 super.onCreate(savedInstanceState);         //調用父類
      8                 //設置為豎屏模式
      9                       setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
      10                mview=new MyTDView(this);                    //創(chuàng)建MyTDView類的對象
      11                mview.requestFocus();                         //獲取焦點
      12                mview.setFocusableInTouchMode(true);        //設置為可觸控
      13                setContentView(mview);                        //跳轉到相關界面
      14             }
      15             @Override
      16          public void onResume(){                //繼承Activity后重寫的onResume方法
      17                     super.onResume();
      18                mview.onResume();                 //通過MyTDView類的對象調用onResume方法
      19             }
      20             @Override
      21          public void onPause(){                 //繼承Activity后重寫的onPause方法
      22                     super.onPause();
      23                mview.onPause();                  //通過MyTDView類的對象調用onPause方法
      24     }}

? 第4行為本類的成員變量,主要是聲明MyTDView類的引用。

? 第6-14行為繼承Activity后重寫的onCreate方法,在該方法中主要是創(chuàng)建MyTDView類的對象,然后設置MyTDView獲得焦點以及可觸控,最后調用setContentView方法跳轉到相關界面。

? 第15-19行為繼承Activity后重寫的onResume方法,在該方法中首先調用父類的onResume方法,然后調用MyTDView類對象的onResume方法。

? 第20-24行為繼承Activity后重寫的onPause方法,該方法中首先調用父類的onPause方法,然后調用MyTDView類對象的onPause方法。

(4)開發(fā)完本案例的主控制類Sample3_1Activity后,接下來需要開發(fā)的是本案例中用于創(chuàng)建三角形的類Triangle。該類的主要功能為初始化頂點數(shù)據(jù)、初始化著色器、設置相應的平移矩陣及旋轉矩陣。首先介紹本類的基本框架,理解該框架有助于讀者對本類整體結構的理解,其代碼如下。

代碼位置:見隨書中源代碼/第3章/Sample3_1/src/com/bn/Sample3_1目錄下的Triangle.java。

      1     package com.bn.Sample3_1;                                     //聲明包名
      2     import java.nio.ByteBuffer;                                   //相關類的引入
      3     ……//此處省略了部分類的引入代碼,讀者可自行查看隨書的源代碼
      4     import android.opengl.Matrix;                                 //相關類的引入
      5       public  class  Triangle{
      6           public static float[] mProjMatrix = new float[16];  //4*4投影矩陣
      7           public static float[] mVMatrix = new float[16]; //攝像機位置朝向的參數(shù)矩陣
      8           public static float[] mMVPMatrix;                 //總變換矩陣
      9           int mProgram;                                        //自定義渲染管線著色器程序id
      10          int muMVPMatrixHandle;                              //總變換矩陣引用
      11          int maPositionHandle;                               //頂點位置屬性引用
      12          int maColorHandle;                                  //頂點顏色屬性引用
      13          String mVertexShader;                               //頂點著色器代碼腳本
      14          String mFragmentShader;                             //片元著色器代碼腳本
      15          static float[] mMMatrix = new float[16];         //具體物體的3D變換矩陣
      16          FloatBuffer mVertexBuffer;                         //頂點坐標數(shù)據(jù)緩沖
      17          FloatBuffer mColorBuffer;                          //頂點著色數(shù)據(jù)緩沖
      18          int vCount=0;                                        //頂點數(shù)量
      19          float xAngle=0;                                      //繞x軸旋轉的角度
      20          public Triangle(MyTDView mv){                      //構造器
      21                initVertexData();                 //調用初始化頂點數(shù)據(jù)的initVertexData方法
      22                intShader(mv);                    //調用初始化著色器的intShader方法
      23             }
      24          public void initVertexData(){                      //自定義的初始化頂點數(shù)據(jù)的方法
      25                vCount=3;                                       //頂點數(shù)量為3
      26                final float UNIT_SIZE=0.2f;                  //設置單位長度
      27                float vertices[]=new float[]{                //頂點坐標數(shù)組
      28                                  -4*UNIT_SIZE,0,0,0, -4*UNIT_SIZE,0,4*UNIT_SIZE,0,0};
      29                     ByteBuffer  vbb  =  ByteBuffer.allocateDirect(vertices.length*4);
      30                vbb.order(ByteOrder.nativeOrder());    //設置字節(jié)順序為本地操作系統(tǒng)順序
      31                mVertexBuffer = vbb.asFloatBuffer();  //轉換為浮點(Float)型緩沖
      32                mVertexBuffer.put(vertices);            //在緩沖區(qū)內寫入數(shù)據(jù)
      33                mVertexBuffer.position(0);              //設置緩沖區(qū)起始位置
      34                float colors[]=new float[]{             //頂點顏色數(shù)組
      35                                  1,1,1,0,0,0,1,0,0,1,0,0};
      36                     ByteBuffer  cbb  =  ByteBuffer.allocateDirect(colors.length*4);
      37                cbb.order(ByteOrder.nativeOrder());    //設置字節(jié)順序為本地操作系統(tǒng)順序
      38                mColorBuffer = cbb.asFloatBuffer();    //轉換為浮點(Float)型緩沖
      39                mColorBuffer.put(colors);               //在緩沖區(qū)內寫入數(shù)據(jù)
      40                mColorBuffer.position(0);               //設置緩沖區(qū)起始位置
      41             }
      42          ……//此處省略了初始化著色器以及繪制圖形的方法,將在下面介紹
      43          public static float[] getFinalMatrix(float[] spec){ //產(chǎn)生最終變換矩陣的方法
      44                mMVPMatrix=new float[16];               //初始化總變換矩陣
      45                Matrix.multiplyMM(mMVPMatrix,0, mVMatrix,0, spec,0);
      46                Matrix.multiplyMM(mMVPMatrix,0, mProjMatrix,0, mMVPMatrix,0);
      47                return mMVPMatrix;                       //返回總變換矩陣
      48     }}

? 第6-19行為本類所需成員變量的聲明,主要是聲明相關矩陣的引用、自定義渲染管線著色器程序的id、頂點位置和顏色屬性的引用、頂點著色器以及片元著色器代碼腳本字符串、頂點坐標和頂點著色數(shù)據(jù)緩沖、頂點的數(shù)量以及繞x軸旋轉的角度。

? 第20-23行為本類的構造器,在該構造器中主要是調用initVertexData方法初始化頂點相關的數(shù)據(jù),并且調用intShader方法初始化著色器。

? 第24-41行為初始化頂點數(shù)據(jù)的initVertexData方法。該方法中需要指定頂點的坐標數(shù)據(jù)以及頂點的顏色數(shù)據(jù),將數(shù)據(jù)寫入到對應的緩沖區(qū)中,并設置緩沖區(qū)的起始位置。

? 第43-48行為通過物體的3D變換矩陣、攝像機參數(shù)矩陣、投影矩陣計算產(chǎn)生最終總變換矩陣的方法。關于各種變換矩陣的問題讀者不用擔心,本書后面將有專門的章節(jié)詳細介紹。

(5)開發(fā)完Triangle類的基本框架后,接下來將繼續(xù)介紹上面省略的開發(fā)初始化著色器的方法initShader以及繪制三角形的方法drawSelf,具體代碼如下。

代碼位置:見隨書中源代碼/第3章/Sample3_1/src/com/bn/Sample3_1目錄下的Triangle.java。

      1     public void intShader(MyTDView mv){                     //創(chuàng)建并初始化著色器的方法
      2           //加載頂點著色器的腳本內容
      3           mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
      4           //加載片元著色器的腳本內容
      5           mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
      6           //基于頂點著色器與片元著色器創(chuàng)建程序
      7           mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
      8           //獲取程序中頂點位置屬性引用id
      9           maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");
      10          //獲取程序中頂點顏色屬性引用id
      11          maColorHandle= GLES30.glGetAttribLocation(mProgram, "aColor");
      12          //獲取程序中總變換矩陣引用id
      13          muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");
      14     }
      15    public void drawSelf(){                              //自定義的繪制三角形的方法
      16          GLES30.glUseProgram(mProgram);               //使用指定的著色器程序進行渲染
      17         Matrix.setRotateM(mMMatrix,0,0,0,1,0);       //初始化變換矩陣
      18         Matrix.translateM(mMMatrix,0,0,0,1);          //設置沿z軸正向平移
      19         Matrix.rotateM(mMMatrix,0, xAngle,1,0,0);     //設置繞x軸旋轉
      20         GLES30.glUniformMatrix4fv(muMVPMatrixHandle,1, false, Triangle.GetFinalMatrix
      21               (mMMatrix),0);                            //將變換矩陣傳入渲染管線
      22         GLES30.glVertexAttribPointer(                  //將頂點位置數(shù)據(jù)傳送進渲染管線
      23             maPositionHandle,3, GLES30.GL_FLOAT, false,3*4, mVertexBuffer);
      24         GLES30.glVertexAttribPointer(                  //將頂點顏色數(shù)據(jù)傳送進渲染管線
      25                     maColorHandle,4, GLES30.GL_FLOAT, false,4*4, mColorBuffer);
      26         GLES30.glEnableVertexAttribArray(maPositionHandle);  //啟用頂點位置數(shù)據(jù)
      27         GLES30.glEnableVertexAttribArray(maColorHandle);      //啟用頂點著色數(shù)據(jù)
      28         GLES30.glDrawArrays(GLES30.GL_TRIANGLES,0, vCount);  //執(zhí)行繪制
      29     }

? 第1-14行為初始化著色器的方法。在該方法中首先需要加載相應的著色器腳本,然后創(chuàng)建自定義的渲染管線著色器程序,并保留程序id于mProgram中。接著通過GLES30類調用相應的方法獲取著色器程序中頂點坐標數(shù)據(jù)的引用、頂點顏色數(shù)據(jù)的引用以及總變換矩陣的引用。

? 第16行通過GLES30類調用glUseProgram方法給出著色器程序id指定使用的著色器程序。

? 第17-21行功能為初始化變換矩陣,設置沿z軸正方向的平移值以及繞x軸旋轉的角度值。

? 第22-25行為通過調用GLES30類的glVertexAttribPointer方法,將頂點坐標數(shù)據(jù)以及頂點顏色數(shù)據(jù)傳送進渲染管線,以備渲染時在頂點著色器中使用。

? 第26-28行為通過調用GLES30類的glEnableVertexAttribArray方法啟用頂點位置數(shù)據(jù)以及啟用頂點顏色數(shù)據(jù),并通過調用GLES30類的glDrawArrays方法繪制三角形。

提示

本類中用到的有關著色器的知識、平移以及旋轉變換的相關知識,將在后面的章節(jié)中逐步進行詳細介紹。在本案例中由于知識不全,無法進行詳細解釋,讀者有一個簡單了解即可。

(6)開發(fā)完本案例的主控制類Sample3_1Activity以及三角形繪制類Triangle后,接下來將開發(fā)本案例中用于顯示3D場景的類MyTDView。該類繼承自GLSurfaceView,并且在該類中通過內部類的形式創(chuàng)建了場景渲染器,實現(xiàn)的具體代碼如下。

代碼位置:見隨書中源代碼/第3章/Sample3_1/src/com/bn/Sample3_1目錄下的MyTDView.java。

      1     package com.bn.Sample3_1;                           //聲明包名
      2     import javax.microedition.khronos.egl.EGLConfig; //相關類的引入
      3     ……//此處省略了部分類的引入代碼,讀者可自行查看隨書的源代碼
      4     import android.view.MotionEvent;                   //相關類的引入
      5     public class MyTDView extends GLSurfaceView{//創(chuàng)建繼承GLSurfaceView的MyTDView類
      6           final float ANGLE_SPAN = 0.375f;             //每次三角形旋轉的角度
      7           RotateThread rthread;                          //自定義線程類RotateThread的引用
      8           SceneRenderer mRenderer;                      //自定義渲染器的引用
      9           public MyTDView(Context context){            //構造器
      10                     super(context);
      11                this.setEGLContextClientVersion(3);    //使用OpenGL ES 3.0需設置該值為3
      12                mRenderer=new SceneRenderer();         //創(chuàng)建SceneRenderer類的對象
      13                this.setRenderer(mRenderer);            //設置渲染器
      14                     this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
      15             }
      16             private  class  SceneRenderer  implements  GLSurfaceView.Renderer{
      17                Triangle tle;                             //聲明Triangle類的引用
      18                public void onDrawFrame(GL10 gl){      //重寫的onDrawFrame方法
      19                      GLES30.glClear( GLES30.GL_DEPTH_BUFFER_BIT//清除顏色緩存和深度緩存
      20                                                                            |  GLES30.GL_COLOR_BUFFER_BIT);
      21                      tle.drawSelf();       //通過Triangle的對象調用drawSelf繪制三角形
      22                     }
      23                public void onSurfaceChanged(GL10 gl, int width, int height){
      24                      GLES30.glViewport(0,0, width, height);    //設置視口
      25                float ratio = (float) width / height; //計算屏幕的寬度和高度比例
      26                Matrix.frustumM(Triangle.mProjMatrix,0, -ratio, ratio, -1,1,1,10);
      27                Matrix.setLookAtM(Triangle.mVMatrix,0,0,0,3,0f,0f,0f,0f,1.0f,0.0f);
                                                              //設置攝像機
      28                     }
      29                public void onSurfaceCreated(GL10 gl, EGLConfig config){
      30                GLES30.glClearColor(0,0,0,1.0f);       //設置屏幕背景色
      31                tle=new Triangle(MyTDView.this);       //創(chuàng)建Triangle類的對象
      32                GLES30.glEnable(GLES30.GL_DEPTH_TEST); //打開深度檢測
      33                      rthread=new RotateThread();       //創(chuàng)建RotateThread類的對象
      34                      rthread.start();                    //開啟線程
      35             }}
      36          public class RotateThread extends Thread{        //自定義的內部類線程
      37                public boolean flag=true;                    //設置循環(huán)標志位
      38                     @Override
      39                public void run(){                            //重寫的run方法
      40                      while(flag){                             //while循環(huán)
      41                                      mRenderer.tle.xAngle=mRenderer.tle.xAngle+  ANGLE_SPAN;
      42                                      try{
      43                                  Thread.sleep(20);           //線程休眠20ms
      44                            }catch(Exception e){              //捕獲并打印異常信息
      45                                              e.printStackTrace();
      46     }}}}}

? 第6-8行為本類的成員變量,主要有每次旋轉的角度、旋轉線程類RotateThread對象的引用以及自定義渲染器SceneRenderer的引用。

? 第9-15行為本類的構造器,在該構造器中設置使用OpenGL ES 3.0版本,創(chuàng)建了SceneRenderer類的對象,設置了渲染器,并且設置渲染模式為主動渲染。

? 第18-22行為實現(xiàn)GLSurfaceView.Renderer接口后重寫的onDrawFrame方法,在該方法中首先需要清除深度緩存和顏色緩存,然后通過Triangle類的對象調用drawSelf方法繪制三角形。

? 第23-28行為實現(xiàn)GLSurfaceView.Renderer接口后重寫的onSurfaceChanged方法,在該方法中設置了視口、透視投影相關參數(shù)以及攝像機的位置。

? 第29-35行為實現(xiàn)GLSurfaceView.Renderer接口后重寫的onSurfaceCreated方法,在該方法中設置了屏幕的背景顏色、創(chuàng)建了Triangle類的對象、開啟了深度檢測、創(chuàng)建了RotateThread類的對象并啟動了該線程。

? 第36-46行為自定義的內部類線程,在該線程中通過while循環(huán)不斷更改Triangle類中xAngle的值,使得三角形以一定的角速度繞x軸轉動。

提示

本案例采用的用到變換矩陣時臨時生成變換矩陣的方式并不方便,在本書后面的章節(jié)中,這些矩陣將會被集合到一個MatrixState工具類當中進行管理,使得開發(fā)人員在應用時更加方便。

(7)完成了Java代碼的開發(fā)后,接著就可以用著色語言開發(fā)著色器了,著色器的代碼可以存儲在后綴名為“.sh”文件中,這些文件存放到項目的assets目錄下。首先開發(fā)的是頂點著色器,其主要作用為執(zhí)行頂點變換、紋理坐標變換等頂點的相關操作,具體代碼如下。

代碼位置:見隨書中源代碼/第3章/Sample3_1/assets目錄下的vertex.sh文件。

      1       #version  300  es
      2     uniform mat4 uMVPMatrix;                            //總變換矩陣
      3     layout (location = 0) in vec3 aPosition;          //頂點位置
      4     layout (location = 1) in vec4 aColor;             //頂點顏色
      5     out vec4 vColor;                                      //用于傳遞給片元著色器的out變量
      6       void  main(){
      7        gl_Position = uMVPMatrix * vec4(aPosition,1); //根據(jù)總變換矩陣計算此次繪制此頂點位置
      8        vColor = aColor;                                   //將接收的顏色值傳遞給片元著色器
      9       }

說明

關于著色語言以及頂點著色器的作用將會在后面的章節(jié)中逐步詳細介紹,這里對于頂點著色器中的代碼不進行詳細講解,讀者只簡單了解即可。

(8)完成了頂點著色器代碼的開發(fā)后,下面將開發(fā)的是片元著色器的代碼,其主要作用為執(zhí)行紋理的訪問、顏色的匯總等操作,具體代碼如下。

代碼位置:見隨書中源代碼/第3章/Sample3_1/assets目錄下的frag.sh文件。

      1       #version  300  es
      2     precision mediump float;                                  //設置浮點精度
      3     in vec4 vColor;                                            //接收從頂點著色器過來的參數(shù)
      4     out vec4 fragColor;                                       //輸出的片元顏色
      5       void  main(){
      6        fragColor = vColor;                                     //給此片元賦顏色值
      7       }

說明

關于著色語言以及片元著色器的作用將會在后面的章節(jié)中逐步詳細介紹,這里對于片元著色器中的代碼不進行詳細講解,讀者也只要簡單了解即可。另外,到這里為止讀者可能學得不是很明白,沒有關系,這個案例只是希望讀者對基于OpenGL ES 3.0的3D應用程序有一個簡單的認識,要想基本搞清楚至少需要耐著性子學完本書前5章的內容才可以。

OpenGL ES 3.0本身并不是基于任何特定操作系統(tǒng)平臺的,基于其開發(fā)的應用程序不但可以運行在Android系統(tǒng)上,也可以運行在iOS、Symbian以及BlackBerry等平臺上。

由于Android是目前市面上占有率最高的移動嵌入式平臺,因此本書中的案例大部分是借助于Android平臺來進行介紹的。但目標平臺不是Android的讀者也不用擔心,OpenGL ES 3.0本身在各個平臺上是通用的,直接相關于OpenGL ES 3.0的知識基于哪種平臺進行學習關系并不大,學成之后可以在所有支持其的平臺上使用。

還有一些讀者可能會有另外一個疑問,書中的案例很多是使用Java語言開發(fā)的,性能沒有影響嗎?其實不必擔心,原因如下:

? 本書的不少案例中在調用與OpenGL ES 3.0相關的API時雖然使用的是Java語言,但這部分API本質上并不是由Java實現(xiàn)的,Java調用時也是通過JNI直接調用的底層C庫,因此這部分代碼用Java開發(fā)還是用C開發(fā)性能上基本沒有什么差異。

? 由于本質上是直接調用的底層C庫,因此API與C版本的幾乎沒有差異,這樣本書案例中的很多代碼直接就可以在C版本的應用中使用。就算少量不一樣的部分,移植也很容易,因為相似度非常高。

提示

想學習Android下NDK平臺3D應用開發(fā)的讀者也不用擔心,本書后面有專門的章節(jié)介紹NDK下OpenGL ES 3.0 3D應用的開發(fā)。

3.1.3 OpenGL ES 3.1新特性簡介

上一小節(jié)詳細地介紹了使用OpenGL ES 3.0開發(fā)的3D應用程序,本小節(jié)將主要介紹OpenGL ES 3.x中關于OpenGL ES 3.1增加的部分新特性。要注意的是,OpenGL ES 3.1是3.0規(guī)范的小幅升級版,大部分支持3.0的硬件都可以支持3.1規(guī)范的新功能。

目前的OpenGL ES 3.0是基于OpenGL 3.x規(guī)范的子集,而OpenGL ES 3.1則是基于OpenGL 4.x規(guī)范的子集,同時向下兼容ES 3.0/2.0規(guī)范。OpenGL ES 3.1新特性主要包括如下幾個部分。

? 計算著色器(Compute Shaders)。

其是新版的支柱性功能,來自OpenGL 4.3。通過計算著色器,應用可使用GPU執(zhí)行通用目的的計算任務,并與圖形渲染緊密相連,將大大增強移動設備的計算能力。

? 獨立的著色器對象。

應用可為GPU的頂點、片元著色器階段獨立編程,無需明確的連接步驟即可將頂點、片元著色器程序混合匹配在一起。

? 增強的紋理功能。

主要包括多重采樣紋理、模版紋理、紋理聚集等。

? 著色語言的改進。

包含新的算法和位字段(bitfield)操作,還有現(xiàn)代方式的著色器編程等。

? 向下兼容。

能夠兼容OpenGL ES 2.0/3.0,編程人員可在已有基礎上增加3.1特性。

說明

本書中所開發(fā)及講解的案例無特別說明都是基于OpenGL ES 3.0的,當然,在本叢書的下卷會有專門的章節(jié)對OpenGL ES 3.1的新特性進行介紹。到那時讀者會發(fā)現(xiàn)將OpenGL ES 3.0的案例升級為OpenGL ES 3.1版本是非常容易的。

主站蜘蛛池模板: 乡宁县| 斗六市| 巧家县| 武鸣县| 秭归县| 商都县| 霍邱县| 姚安县| 罗江县| 平昌县| 万州区| 兴海县| 汽车| 兴城市| 双鸭山市| 横峰县| 永康市| 涡阳县| 新河县| 贵德县| 阳谷县| 清水河县| 泸水县| 福建省| 泊头市| 新营市| 汉川市| 无为县| 南木林县| 南召县| 多伦县| 苗栗市| 长沙市| 长丰县| 五华县| 兴隆县| 咸宁市| 蓝田县| 台中市| 奉贤区| 沂源县|