Android OpenGL ES(三)----編程框架(Android studio)

      網友投稿 1008 2025-03-31

      首先當然是創建Android項目,你可以選擇最新的Android Studio也可以選擇eclipse都是一樣的。我們重點講解開發Opengl es的流程


      1.定義頂點著色器和片段著色器

      第一節我們講解的已經很細致了,為了便于理解在這里在詳細的說明一下。并且換一種方式定義著色器。

      我們知道第一篇定義的頂點的坐標和顏色是分開的,這樣可以但如果把它們放在一起會方便許多。

      假設我們要繪制一個長方形和兩條直線,二個定點,我們就需要這樣來定義這個數組。

      float[] tableVerticesWithTriangles = {

      //兩個三角形和三角形的顏色分量

      0f, 0f, 1f, 1f, 1f,

      -0.5f, -0.5f, 0.7f, 0.7f, 0.7f,

      0.5f, -0.5f, 0.7f, 0.7f, 0.7f,

      0.5f, 0.5f, 0.7f, 0.7f, 0.7f,

      -0.5f, 0.5f, 0.7f, 0.7f, 0.7f,

      -0.5f, -0.5f, 0.7f, 0.7f, 0.7f,

      //兩條直線和直線的顏色分量

      -0.5f, 0f, 1f, 0f, 0f,

      0.5f, 0f, 1f, 0f, 0f,

      //兩個頂點和頂點的顏色分量

      0f, -0.25f, 0f, 0f, 1f,

      0f, 0.25f, 1f, 0f, 0f

      };

      前面的兩個頂點代表坐標,后面三個頂點代表顏色分別為:紅色,綠色和藍色。

      接著必須對應的建立對應的頂點著色器,假設raw文件夾下的頂點著色器的文件名是simple_vertex_shader.glsl:

      attribute vec4 a_Position;

      attribute vec4 a_Color;

      varying vec4 v_Color;

      void main()

      {

      v_Color=a_Color;

      gl_Position = a_Position;

      gl_PointSize = 10.0;

      }

      我們加入了一個新的屬性a_Color,也加入了一個叫做v_Color的新varying。上一篇已經講過varying是一個特殊的變量類型,它把給它的值進行混合并把這些混合的值發送給片段著色器。

      我們把varying也加入片段著色器,在raw文件夾下創建simple_fragment_shader.glsl:

      precision mediump float;

      varying vec4 v_Color;

      void main()

      {

      gl_FragColor = v_Color;

      }

      我們用varying變量v_Color替換了原來代碼中的uniform。如果這個片段屬于一條直線,那個OpenGL就會用構成那條直線的兩個頂點計算其混合后的顏色。

      2.加載著色器

      在項目中創建一個新的Java源代碼包,命名為util把,至于前綴得看你項目的名稱或者你自己的愛好。

      在這個包下創建一個名為“TextResourceReader”的新類

      public class TextResourceReader {

      public static String readTextFileFromResource(Context context, int resourceId) {

      StringBuilder body = new StringBuilder();

      try {

      InputStream is = context.getResources().openRawResource(resourceId);

      InputStreamReader reader = new InputStreamReader(is);

      BufferedReader bufferedReader = new BufferedReader(reader);

      String nextLine;

      while ((nextLine = bufferedReader.readLine()) != null) {

      body.append(nextLine);

      body.append("\n");

      }

      } catch (IOException e) {

      throw new RuntimeException(

      "Could not open resource: " + resourceId, e);

      } catch (Resources.NotFoundException nfe) {

      throw new RuntimeException("Resource not found: " + resourceId, nfe);

      }

      return body.toString();

      }

      }

      至于這段代碼我就不過多的解釋了這屬于JAVA基礎也可以說是Android基礎,本文重點講解OpenGL,這段代碼的作用是加載著色器。

      3.初始化OpenGL

      定義兩個成員變量:

      private GLSurfaceView glSurfaceView;

      private boolean rendererSet=false;

      在Activity的OnCreate()方法里面初始化glSurfaceView:

      this.glSurfaceView = new GLSurfaceView(this);

      檢查系統是否支持Opengl es 2.0:

      final ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);

      final ConfigurationInfo configurationInfo=activityManager.getDeviceConfigurationInfo();

      Android OpenGL ES(三)----編程框架(android studio)

      final boolean supportsEs2=configurationInfo.reqGlEsVersion>=0x20000;

      首先我們需要Android ActivityManager的一個引用,用它來獲取設備的配置信息,然后,取出reqGlEsVersion變量檢查OpenGL ES版本號。如果版本號為0*20000或后續版本,我們就可以使用OpenGL ES2.0的API了。

      為OpenGL ES2.0配置渲染表面

      if(supportsEs2){

      this.glSurfaceView.setEGLContextClientVersion(2);

      this.glSurfaceView.setRenderer(new LYJRenderer(this));

      this.rendererSet=true;

      }else{

      Toast.makeText(this,"bu zhi chi gai banben ",Toast.LENGTH_SHORT).show();

      return;

      }

      如果設備支持OpenGL ES2.0,我們就通過調用setEGLContextClientVersion(2)配置這個surface視圖,然后調用setReader()傳進自定義的Renderer類的一個新實例,其實如果設備不支持OpenGL ES2.0,公開發布的應用在這個設備的應用程序市場中被隱藏起來,至于隱藏,后續講到,當然這也是清單文件的基礎知識。rendererSet記住GLSurfaceView是否處于有效狀態。

      setContentView(this.glSurfaceView);

      相信大家都知道上面的作用,就是把GLSurfaceView加入到Activity中。并把它顯示到屏幕上。

      當然我們還需要利用Activity生命周期釋放資源,如果沒有下面的代碼,應用程序可能會崩潰。

      @Override

      protected void onResume() {

      super.onResume();

      if(this.rendererSet){

      this.glSurfaceView.onResume();

      }

      }

      @Override

      protected void onPause() {

      super.onPause();

      if(this.rendererSet){

      this.glSurfaceView.onPause();

      }

      }

      創建Renderer類

      讓我們看一下這個接口的方法:

      onSurfaceCreated(GL10 glUnused,EGLConfig config)

      當Surface被創建的時候,GLSurfaceView會調用這個方法;這發生在應用程序第一次運行的時候,并且,當設備被喚醒或者用戶從其他Activity切換回來時,這個方法可能會被調用。在實踐中,這意味著,當應用程序運行時,本方法可能會被調用多次。

      onSurfaceChanged(GL10 glUnused,int width,int height)

      在Surface被創建后,每次Surface尺寸變化時,這個方法都會被GLSurfaceView調用到,在橫屏,豎屏來回切換的時候,Surface尺寸會發生變化。

      onDrawFrame(GL10 glUnused)

      當繪制一幀時,這個方法會被GLSurfaceView調用。在這個方法中,我們一定要繪制一些東西,即使只是清空屏幕;因為,在這個方法返回后,渲染緩沖區會被交換并顯示在屏幕上,如果什么都沒畫,可以會看到糟糕的閃爍效果。

      觀察這些方法,可能細心的會發現都有一個GL10參數,這是OpenGL1.0遺留下來的,如果在1.0的設備上就會用到,當時對于OpenGL ES2.0,GLES20類提供了靜態方法存取。

      新建渲染器LYJRenderer:

      public class LYJRenderer implements GLSurfaceView.Renderer {

      public LYJRenderer(Context context) {

      this.context = context;

      this.vertexData = ByteBuffer.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();

      this.vertexData.put(tableVerticesWithTriangles);

      }

      public void onSurfaceCreated(GL10 gl, EGLConfig config) {

      GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

      public void onSurfaceChanged(GL10 gl, int width, int height) {

      GLES20.glViewport(0, 0, width, height);

      }

      public void onDrawFrame(GL10 gl) {

      GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

      }

      首先調用在onSurfaceCreated調用glClearColor設置清空屏幕,前三個參數對應顏色,后一個參數是透明度。這里設置為黑色。

      下一步就是設置視口的尺寸了也就是glViewport,這就是告訴OpenGL可以用來渲染的surface的大小。

      在onDrawFrame中調用glClear清空屏幕,這會擦除屏幕上所有顏色,并調用之前的glClearColor()調用定義的顏色填充整個屏幕。

      在Renderer類中讀入著色器,在onSurfaceCreated()的結尾除加入如下代碼:

      String vertexShaderSource = TextResourceReader.readTextFileFromResource(this.context, R.raw.simple_vertex_shader);

      String fragmentShaderSource = TextResourceReader.readTextFileFromResource(this.context, R.raw.simple_fragment_shader);

      4.編譯著色器

      我們把著色器源碼從文件中讀取出來,下一步就是編譯每個著色器了。我們要創建一個新的輔助類,它可以創建新的OpenGL著色器對象,編譯著色器并且返回代表那段著色器的對象。一旦寫出這個樣板代碼,在未來的項目中可以重用了。

      創建一個名為ShaderHelper類,并添加如下代碼:

      public class ShaderHelper {

      public static final String TAG = "ShaderHelper";

      public static int compileVertexShader(String shaderCode) {

      return compileShader(GLES20.GL_VERTEX_SHADER, shaderCode);

      }

      public static int compileFragmentShader(String shaderCode) {

      return compileShader(GLES20.GL_FRAGMENT_SHADER, shaderCode);

      }

      public static int compileShader(int type, String shaderCode) {

      這些代碼作為輔助類的基礎。下面就要創建新的著色器對象并檢查創建是否成功,下面都是在構建compileShader方法。

      final int shaderObjectId = GLES20.glCreateShader(type);

      if (shaderObjectId == 0) {

      if (LoggerConfig.ON) {

      Log.w(TAG, "Counld not create new shader");

      }

      return 0;

      }

      這里,用glCreateShader()調用創建了一個新的著色器對象,并把這個對象的ID存入變量shaderObjectId。這個type可以代表定點著色器的GL_VERTEX_SHADER,或者是代表片段著色器的GL_FRAGMENT_SHADER。剩下的代碼也用同樣的方式。

      記住我們是如何創建對象并檢查它是否有效的;這個模式將在OpenGL里廣泛使用:

      1.首先使用一個如glCreateShader()一樣的調用創建一個對象,這個調用會返回一個整型值。

      2.這個整型值就是OpenGL對象的一個引用。無論后面什么時候要引用這個對象,就要把這個整型值傳回 OpenGL。

      3.返回值0表示這個對象創建失敗,它類似于Java代碼返回的null值。

      上傳和編譯著色器源代碼

      GLES20.glShaderSource(shaderObjectId, shaderCode);

      這個調用是告訴OpenGL讀入字符串shaderCode定義的源代碼,并把它與shaderObjectId所引用的著色器對象關聯起來。然后調用下面方法編譯著色器:

      GLES20.glCompileShader(shaderObjectId);

      這個調用告訴OpenGL編譯上傳到shaderObjectId的源代碼。

      取出編譯狀態,加入如下代碼:

      final int[] compileStatus = new int[1];

      GLES20.glGetShaderiv(shaderObjectId, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      為了檢查編譯失敗還是成功,首先要創建新的長度為1的int數組,稱為compileStatus;然后調用glGetShaderiv。這就告訴OpenGL讀取與shaderObjectId關聯的編譯狀態,并把它寫入compileStatus的第0個元素。

      這是Android平臺上的OpenGL的另一個通用模式。 為了取出一個值,我們通常會使用一個長度為1的數組,并把這個數組傳進OpenGL調用。在一個調用中,我們告訴OpenGL把結果存進數組的第一個元素。

      驗證編譯狀態并返回著色器對象ID,代碼如下:

      if (compileStatus[0] == 0) {

      GLES20.glDeleteShader(shaderObjectId);

      if (LoggerConfig.ON) {

      Log.w(TAG, "Compilation of shader failed");

      }

      return 0;

      }

      如果編譯成功返回shaderObjectId:

      return?shaderObjectId;

      在Renderer類中編譯著色器,在OnSurfaceView()結尾處加入如下代碼:

      int vertexShader = ShaderHelper.compileVertexShader(vertexShaderSource);

      int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);

      5.把著色器一起鏈接進OpenGL的程序

      既然我們已經加載并編譯了一個頂點著色器和一個片段著色器,下一步就是把它們綁定在一起放入一個單個的程序里。

      5.1理解OpenGL的程序:

      簡單來說,一個OpenGL程序就是把一個頂點著色器和一個片段著色器鏈接在一起變成單個對象。頂點著色器和片段著色器總是一起工作的。沒有片段著色器,OpenGL就不知道怎么繪制那些組成的每個點,直線和三角形片段;如果沒有頂點著色器,OpenGL就不知道在哪里繪制這些片段。

      雖然頂點著色器和片段著色器總是要一起工作的,但并不意味著它們必須是一對一匹配的,我們可以同時在多個程序中使用同一個著色器。

      讓我們打開ShaderHelper,并在類的末尾加入如下代碼:

      public static int linkProgram(int vertexShaderId, int fragmentShaderId) {

      下面我們將構建這個方法。

      新建程序并附著上著色器,我們要做的第一件事就是調用glCreateProgram()新建程序對象,并把那個對象的ID存進programObjectId。如下:

      final int programObjectId = GLES20.glCreateProgram();

      if (programObjectId == 0) {

      if (LoggerConfig.ON) {

      Log.w(TAG, "Could not create new Program");

      }

      return 0;

      }

      和上面的代碼類似就不過多的解釋了,下一步就是附上著色器:

      GLES20.glAttachShader(programObjectId, vertexShaderId);

      GLES20.glAttachShader(programObjectId, fragmentShaderId);

      使用glAttachShader()方法把頂點著色器和片段著色器附加到程序對象上。

      5.2鏈接程序

      現在準備把這些著色器聯合起來了,為此,將調用glLickProgram(programObjectId):

      GLES20.glLinkProgram(programObjectId);

      為了檢查這個鏈接是成功還是失敗,我們遵循編譯著色器時所使用的步驟:

      final int[] linkStatus = new int[1];

      GLES20.glGetProgramiv(programObjectId, GLES20.GL_LINK_STATUS, linkStatus, 0);

      最后驗證鏈接狀態并返回程序對象ID,代碼如下:

      if (linkStatus[0]==0) {

      GLES20.glDeleteProgram(programObjectId);

      if(LoggerConfig.ON){

      Log.w(TAG,"linking of program failed");

      }

      return 0;

      }

      return programObjectId;

      給渲染類LYJRenderer加入代碼,先定義成員變量:

      private int program;

      然后在onSurfaceCreated()結尾處加入如下代碼把著色器鏈接起來:

      program = ShaderHelper.linkProgram(vertexShader, fragmentShader);

      6.最后的拼接

      6.1驗證OpenGL程序的對象

      在開始使用OpenGL的程序之前,我們首先應該驗證一下它,看看這個程序對于當前的OpenGL狀態是不是有效的。根據OpenGL ES2.0的文檔,它也給OpenGL提供了一種方法讓我們知道為什么當前程序可能是低效率的,無法運行的,等等。

      讓我們在ShaderHelper加入如下代碼:

      public static boolean validateProgram(int programObjectId){

      GLES20.glValidateProgram(programObjectId);

      final int[] validateStatus=new int[1];

      GLES20.glGetProgramiv(programObjectId,GLES20.GL_VALIDATE_STATUS,validateStatus,0);

      Log.v(TAG,GLES20.glGetProgramInfoLog(programObjectId));

      return validateStatus[0]!=0;

      }

      這段代碼與上面驗證類似就不過多的闡述了,然后在onSurfaceView()結尾處加入如下代碼:

      ShaderHelper.validateProgram(program);

      然后調用glUseProgram(program);

      調用glUseProgram()告訴OpenGL在繪制任何東西到屏幕上的時候要使用這個定義的程序。

      獲取屬性的位置

      在使用屬性之前我們要獲取它們的位置。我們可以讓OpenGL自動給這些屬性分配位置編號,或者在著色器被鏈接到一起之前,可以通過調用glBindAttrribLocation()由我們自己給它們分配位置編號。我們要讓OpenGL自動分配這些位置,因為它使代碼容易管理。

      在LYJRenderer頂部加入如下定義:

      private static final String A_POSITION="a_Position";

      private int aPositionLocation;

      private static final int POSITION_COMPONENT_COUNT = 2;

      一旦著色器被鏈接起來了,我們就只需要加入一些代碼去獲取屬性位置。在onSurfaceCreated() 結尾處加入如下代碼:

      this.aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);

      調用glGetAttribLocation()獲取屬性的位置。 有了這個位置,就能告訴OpenGL到哪里去找到這個屬性對應的數據了。

      6.2關聯屬性與頂點數據的數組

      下一部就是要告訴OpenGL到哪里找到屬性a_Position對應的數據。

      this.vertexData.position(0);

      GLES20.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT, false, STRIDE, this.vertexData);

      在我們告訴OpenGL從這個緩沖區中讀取數據之前,需要確保它會從開頭處開始讀取數據,而不是中間或者結尾處。每個緩沖區都有一個內部指針,可以通過調用position(int)移動它,并且當OpenGL從緩沖區讀取時,它會從這個位置開始讀取。

      下面是參數的解析:

      aPositionLocation:這個是屬性位置。

      POSITION_COMPONENT_COUNT:這個屬性有多少分量

      GLES20.GL_FLOAT:這是數據的類型

      false:只有使用整型數組時候,這個數據才有意義。

      STRIDE:多于一個屬性時候,就要告訴取下個數據要跳過多少分量。

      vertexData:這個參數告訴OpenGL去哪里讀取數據。

      盡管我們已經把數據屬性鏈接起來了,在開始繪制之前,我們還需要調用glEnableVertexAttribArray()使用這個屬性。代碼如下:

      GLES20.glEnableVertexAttribArray(aPositionLocation);

      同理顏色分量的代碼如下:

      private static final int COLOR_COMPONENT_COUNT=3;

      private static final String A_COLOR = "a_Color";

      private int aColorLocation;

      private static final int STRIDE=(POSITION_COMPONENT_COUNT+COLOR_COMPONENT_COUNT)*BYTES_PER_FLOAT;

      this.vertexData.position(POSITION_COMPONENT_COUNT);

      GLES20.glVertexAttribPointer(aColorLocation, COLOR_COMPONENT_COUNT, GLES20.GL_FLOAT, false, STRIDE, this.vertexData);

      GLES20.glEnableVertexAttribArray(aColorLocation);

      7.在屏幕上繪制

      在onDrawFrame() 方法的結尾添加如下代碼:

      GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 6);

      GLES20.glDrawArrays(GLES20.GL_LINES, 6, 2);

      GLES20.glDrawArrays(GLES20.GL_POINTS, 8, 1);

      GLES20.glDrawArrays(GLES20.GL_POINTS, 9, 1);

      第一個語句告訴OpenGL繪制三角扇形,什么是三角扇形后面講解,第二個語句告訴OpenGL繪制直線,第三,四條語句告訴OpenGL繪制點。

      這七個步驟就是開發OpenGL程序的基本流程。如圖

      Android Elasticsearch OpenGL

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:關于安全生產視頻監控系統的信息
      下一篇:excel表頭固定(excel表頭固定不動)
      相關文章
      国产AV无码专区亚洲AV毛网站| 亚洲日韩在线中文字幕综合| 亚洲AV无码乱码在线观看性色扶 | 久久亚洲最大成人网4438 | 日本中文一区二区三区亚洲| 亚洲AV综合色区无码一二三区| 中文字幕在线观看亚洲日韩| 亚洲国产成a人v在线观看| 亚洲影视自拍揄拍愉拍| 亚洲av永久无码精品天堂久久| 亚洲国产人成在线观看| 亚洲一卡2卡4卡5卡6卡残暴在线| 亚洲国产电影在线观看| 精品亚洲AV无码一区二区| 亚洲一区欧洲一区| 亚洲欧美国产欧美色欲| 大桥未久亚洲无av码在线| 国产精品亚洲片在线花蝴蝶| 日韩精品电影一区亚洲| 精品国产日韩亚洲一区| 亚洲色偷偷偷鲁综合| 亚洲国产成人一区二区精品区| 亚洲国产综合专区在线电影| 91亚洲精品第一综合不卡播放| 亚洲经典在线观看| 国产精品久久亚洲不卡动漫| 在线观看日本亚洲一区| 久久亚洲AV成人无码国产电影| 国产亚洲漂亮白嫩美女在线| AV在线播放日韩亚洲欧| 国产偷v国产偷v亚洲高清| 亚洲色图在线观看| 亚洲一级视频在线观看| 亚洲狠狠婷婷综合久久蜜芽| 妇女自拍偷自拍亚洲精品| 亚洲成a人片在线观看久| 亚洲日韩一页精品发布| 亚洲精品免费视频| 亚洲AV成人影视在线观看| 无码不卡亚洲成?人片| 亚洲乱码日产一区三区|