自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android OpenGL ES 2.0繪圖:繪制紋理

移動開發(fā) Android
android使用openGL提供了特殊的view作為基礎叫做GLSurfaceView。我們的view需要繼承GLSurfaceView。紋理,在OpenGL中,可以理解為加載到顯卡顯存中的圖片。

紋理,在OpenGL中,可以理解為加載到顯卡顯存中的圖片。

Android設備在 2.2開始支持OpenGL ES2.0,從前都是ES1.0 和 ES1.1的版本。

簡單來說,OpenGL ES是為了嵌入設備進行功能剪裁后的OpenGL版本。ES2.0是和1.x版本不兼容的,區(qū)別和兼容性參見android官方文檔。

首先,android使用openGL提供了特殊的view作為基礎叫做GLSurfaceView。我們的view需要繼承GLSurfaceView。

java代碼

  1. public class MyGLSurfaceView extends GLSurfaceView { 
  2.  
  3.   public MyGLSurfaceView(Context context) { 
  4.   super(context); 
  5.   setFocusableInTouchMode(true); 
  6.   // Tell the surface view we want to create an OpenGL ES 2.0-compatible 
  7.   // context, and set an OpenGL ES 2.0-compatible renderer. 
  8.   this.setEGLContextClientVersion(2); 
  9.   this.setRenderer(new MyRenderer()); 
  10.   } 
  11.   } 

并沒有什么特別之處,android view的渲染操作需要實現(xiàn)一個render接口,GLSurfaceView的渲染接口為android.opengl.GLSurfaceView.Renderer。我們需要實現(xiàn)接口的方法。

java代碼

  1. public class MyRenderer implements Renderer { 
  2.  
  3.   public void onDrawFrame(GL10 gl) {} 
  4.   public void onSurfaceChanged(GL10 gl, int width, int height) {} 
  5.   public void onSurfaceCreated(GL10 gl, EGLConfig config) {} 
  6.   } 

接口實現(xiàn)3個方法,對應繪制,繪制區(qū)域變化,區(qū)域創(chuàng)建。需要說明的是參數GL10 gl是OpenGL ES1.x版本的對象。這里我們不會使用到。還有一點就是,onDrawFrame方法的調用是有系統(tǒng)調用的,不需要手動調用。系統(tǒng)會以一定的頻率不斷的回調。

接下來我們進入ES2.0的使用,上代碼先:

java代碼

  1. public void onSurfaceCreated(GL10 gl, EGLConfig config) { 
  2.  
  3.   GLES20.glEnable(GLES20.GL_TEXTURE_2D); 
  4.   // Active the texture unit 0 
  5.   GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 
  6.   loadVertex(); 
  7.   initShader(); 
  8.   loadTexture(); 
  9.   } 

1、啟用2D紋理

繪制區(qū)域創(chuàng)建的時候,我們設置了啟用2D的紋理,并且激活了紋理單元unit0。什么意思呢,說起來話長,以后慢慢說。簡單說一下,記住OpenGL 是基于狀態(tài)的,就是很多狀態(tài)的設置和切換,這里啟用GL_TEXTURE_2D就是一個狀態(tài)的開啟,表明OpenGL可以使用2D紋理。

什么是激活紋理單元?這個和硬件有點關系,OpenGL要顯卡會劃分存儲紋理的存儲區(qū)域不止一個區(qū)域。這里是使用區(qū)域 unit 0,多重紋理繪制可以開啟多個,這個以后說。接下來,調用了三個函數,載入頂點,初始化著色器,載入紋理。

2、加載頂點

OpenGL繪制圖形是根據頂點以后鏈接起來的。為什么要這樣,其實這樣很強大是一種設計吧。頂點可以暫時簡單理解為含有位置信息的坐標點。

java代碼

  1. private void loadVertex() { 
  2.  
  3.   // float size = 4 
  4.   this.vertex = ByteBuffer.allocateDirect(quadVertex.length * 4
  5.   .order(ByteOrder.nativeOrder()) 
  6.   .asFloatBuffer(); 
  7.   this.vertex.put(quadVertex).position(0); 
  8.   // short size = 2 
  9.   this.index = ByteBuffer.allocateDirect(quadIndex.length * 2
  10.   .order(ByteOrder.nativeOrder()) 
  11.   .asShortBuffer(); 
  12.   this.index.put(quadIndex).position(0); 
  13.   } 
  14.   private FloatBuffer vertex; 
  15.   private ShortBuffer index; 
  16.   private float[] quadVertex = new float[] { 
  17.   -0.5f, 0.5f, 0.0f, // Position 0 
  18.   01.0f, // TexCoord 0 
  19.   -0.5f, -0.5f, 0.0f, // Position 1 
  20.   00// TexCoord 1 
  21.   0.5f , -0.5f, 0.0f, // Position 2 
  22.   1.0f, 0// TexCoord 2 
  23.   0.5f, 0.5f, 0.0f, // Position 3 
  24.   1.0f, 1.0f, // TexCoord 3 
  25.   }; 
  26.   private short[] quadIndex = new short[] { 
  27.   (short)(0), // Position 0 
  28.   (short)(1), // Position 1 
  29.   (short)(2), // Position 2 
  30.   (short)(2), // Position 2 
  31.   (short)(3), // Position 3 
  32.   (short)(0), // Position 0 
  33.   }; 

 FloatBuffer,ShortBuffer是封裝了本地數據結構的封裝對象。是 的,這個2個對象里面的數據不被java虛擬機管理,相當于C語言的存儲方式。quadVertex的數據就是一個矩形的坐標,和紋理坐標。一兩句話很難 解釋清楚,這里涉及到openGL的幾個經典的坐標系,下次說。概括的說,openGL的坐標是單位化的,都是0.0-1.0的浮點型,屏幕的中心點是 (0,0)。而紋理的坐標左下角是(0,0)。 這里的quadVertex是在屏幕中大概花了一個矩形貼了一個圖片, position0 是左上點,以后左下,右下,右上的順序,紋理坐標同理。

quadIndx是這剛才的這些頂點索引排列。這里一個矩形也就4個頂點,每個頂點3個位置坐標,2個紋理坐標。也就是說一個頂點有5個float數據。至于為什么頂點為什么這么排列下次說,是2個三角形合成了一個矩形,幾句話很難解釋清楚。

所以說,這段代碼就是把矩形的位置和紋理坐標,存儲到本地數據,準備后面使用而已。

3、初始化著色器

這個著色器就是ES2.0的特色,又叫可編程著色器,也是區(qū)別于ES1.x的本質。這里只做簡單的介紹。可編程著色器是一種腳本,語法類似C語言,腳本分為頂點著色器和片段著色器,分別對應了openGL不同的渲染流程。

頂點著色器:

java代碼

  1. uniform mat4 u_MVPMatrix; 
  2.  
  3.   attribute vec4 a_position; 
  4.   attribute vec2 a_texCoord; 
  5.   varying vec2 v_texCoord; 
  6.   void main() 
  7.   { 
  8.   gl_Position = a_position; 
  9.   v_texCoord = a_texCoord; 
  10.   } 

片段著色器:

java代碼

  1. precision lowp float
  2.  
  3.   varying vec2 v_texCoord; 
  4.   uniform sampler2D u_samplerTexture; 
  5.   void main() 
  6.   { 
  7.   gl_FragColor = texture2D(u_samplerTexture, v_texCoord); 
  8.   } 

這里記住一句話,頂點著色器,會在頂點上執(zhí)行;片段著色器會在像素點上執(zhí)行。剛才的矩形就有4個頂點,每個頂點都會應用這個腳本。也就是說,頂點是位置相關信息,片段是色彩紋理相關信息。

這個2段腳本都是文本,需要編譯,鏈接,等等一些操作才能被ES2.0所使用。過程就像C語言的編譯運行過程。openGL 提供了相關函數去做這些事情。

java代碼

  1. private void initShader() { 
  2.  
  3.   String vertexSource = Tools.readFromAssets("VertexShader.glsl"); 
  4.   String fragmentSource = Tools.readFromAssets("FragmentShader.glsl"); 
  5.   // Load the shaders and get a linked program 
  6.   program = GLHelper.loadProgram(vertexSource, fragmentSource); 
  7.   // Get the attribute locations 
  8.   attribPosition = GLES20.glGetAttribLocation(program, "a_position"); 
  9.   attribTexCoord = GLES20.glGetAttribLocation(program, "a_texCoord"); 
  10.   uniformTexture = GLES20.glGetUniformLocation(program, 
  11. "u_samplerTexture"); 
  12.   GLES20.glUseProgram(program); 
  13.   GLES20.glEnableVertexAttribArray(attribPosition); 
  14.   GLES20.glEnableVertexAttribArray(attribTexCoord); 
  15.   // Set the sampler to texture unit 0 
  16.   GLES20.glUniform1i(uniformTexture, 0); 
  17.   } 

可以看到,頂點和片段一起構成一個program,它可以被openGL所使用,是一個 編譯好的腳本程序,存儲在顯存。 GLES20.glGetAttribLocation 和 GLES20.glGetUniformLocation 這句話是神馬作用呢。簡單說就是,java程序和著色器腳本數據通信的。把就像參數的傳遞一樣,這樣腳本就能根據外界的參數變化,實時的改變openGL 流水線渲染的處理流程。

封裝的加載著色器的輔助方法:

java代碼

  1. public static int loadProgram(String vertexSource, String 
  2. fragmentSource) { 
  3.  
  4.   // Load the vertex shaders 
  5.   int vertexShader = GLHelper.loadShader(GLES20.GL_VERTEX_SHADER, 
  6. vertexSource); 
  7.   // Load the fragment shaders 
  8.   int fragmentShader = GLHelper.loadShader(GLES20.GL_FRAGMENT_SHADER, 
  9. fragmentSource); 
  10.   // Create the program object 
  11.   int program = GLES20.glCreateProgram(); 
  12.   if (program == 0) { 
  13.   throw new RuntimeException("Error create program."); 
  14.   } 
  15.   GLES20.glAttachShader(program, vertexShader); 
  16.   GLES20.glAttachShader(program, fragmentShader); 
  17.   // Link the program 
  18.   GLES20.glLinkProgram(program); 
  19.   int[] linked = new int[1]; 
  20.   // Check the link status 
  21.   GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linked, 0); 
  22.   if (linked[0] == 0) { 
  23.   GLES20.glDeleteProgram(program); 
  24.   throw new RuntimeException("Error linking program: " + 
  25. GLES20.glGetProgramInfoLog(program)); 
  26.   } 
  27.   // Free up no longer needed shader resources 
  28.   GLES20.glDeleteShader(vertexShader); 
  29.   GLES20.glDeleteShader(fragmentShader); 
  30.   return program; 
  31.   } 

java代碼

  1. public static int loadShader(int shaderType, String source) { 
  2.  
  3.   // Create the shader object 
  4.   int shader = GLES20.glCreateShader(shaderType); 
  5.   if (shader == 0) { 
  6.   throw new RuntimeException("Error create shader."); 
  7.   } 
  8.   int[] compiled = new int[1]; 
  9.   // Load the shader source 
  10.   GLES20.glShaderSource(shader, source); 
  11.   // Compile the shader 
  12.   GLES20.glCompileShader(shader); 
  13.   // Check the compile status 
  14.   GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 
  15.   if (compiled[0] == 0) { 
  16.   GLES20.glDeleteShader(shader); 
  17.   throw new RuntimeException("Error compile shader: " + 
  18. GLES20.glGetShaderInfoLog(shader)); 
  19.   } 
  20.   return shader; 
  21.   } 

為什么openGL的很多操作目標都是int類型的?因為openGL只會在顯存生成或綁定地址,返回id,以后用id相當于句柄去改變它的內部狀態(tài)。

4、加載紋理

就是把圖片的數據上傳到顯存,以后再使用它。請注意紋理圖片的長和寬***是2的N次方,不然不一定能繪制出來。

java代碼

  1. static int[] loadTexture(String path) { 
  2.  
  3.   int[] textureId = new int[1]; 
  4.   // Generate a texture object 
  5.   GLES20.glGenTextures(1, textureId, 0); 
  6.   int[] result = null
  7.   if (textureId[0] != 0) { 
  8.   InputStream is = Tools.readFromAsserts(path); 
  9.   Bitmap bitmap; 
  10.   try { 
  11.   bitmap = BitmapFactory.decodeStream(is); 
  12.   } finally { 
  13.   try { 
  14.   is.close(); 
  15.   } catch (IOException e) { 
  16.   throw new RuntimeException("Error loading Bitmap."); 
  17.   } 
  18.   } 
  19.   result = new int[3]; 
  20.   result[TEXTURE_ID] = textureId[0]; // TEXTURE_ID 
  21.   result[TEXTURE_WIDTH] = bitmap.getWidth(); // TEXTURE_WIDTH 
  22.   result[TEXTURE_HEIGHT] = bitmap.getHeight(); // TEXTURE_HEIGHT 
  23.   // Bind to the texture in OpenGL 
  24.   GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]); 
  25.   // Set filtering 
  26.   GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, 
  27. GLES20.GL_LINEAR); 
  28.   GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, 
  29. GLES20.GL_NEAREST); 
  30.   GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, 
  31. GLES20.GL_CLAMP_TO_EDGE); 
  32.   GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, 
  33. GLES20.GL_CLAMP_TO_EDGE); 
  34.   // Load the bitmap into the bound texture. 
  35.   GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 
  36.   // Recycle the bitmap, since its data has been loaded into OpenGL. 
  37.   bitmap.recycle(); 
  38.   } else { 
  39.   throw new RuntimeException("Error loading texture."); 
  40.   } 
  41.   return result; 
  42.   } 

這里使用了android的工具類吧bitmap直接轉換成openGL紋理需要的格式了。過程是,先生成一個紋理的id在顯卡上的,以后根據id上傳紋理數據,以后保存這個id就

可以操作這個紋理了。至于紋理的一些過濾特性設置,將來再說。

現(xiàn)在貌似就剩下繪制了,準備好了頂點信息,頂點對應的紋理坐標。初始化了著色器,上傳了紋理圖片。接下來就已把他們合起來繪制了。

java代碼

  1. public void onDrawFrame(GL10 gl) { 
  2.  
  3.   // clear screen to black 
  4.   GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 
  5.   GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 
  6.   GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); 
  7.   vertex.position(0); 
  8.   // load the position 
  9.   // 3(x , y , z) 
  10.   // (2 + 3 )* 4 (float size) = 20 
  11.   GLES20.glVertexAttribPointer(attribPosition, 
  12.   3, GLES20.GL_FLOAT, 
  13.   false20, vertex); 
  14.   vertex.position(3); 
  15.   // load the texture coordinate 
  16.   GLES20.glVertexAttribPointer(attribTexCoord, 
  17.   2, GLES20.GL_FLOAT, 
  18.   false20, vertex); 
  19.   GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, 
  20. index); 
  21.   } 

為了保持了代碼的簡單,OpenGL的基于狀態(tài)體現(xiàn),bind這個函數無處不在,這里 bindTexture就是通知openGL使用那個id的紋理圖片。接下來的操作就是針對bind的圖片的。繪制就需要讓openGL知道繪制神馬。所 以這里需要用到vertex這個本地數據容器,里面裝在的是頂點和紋理坐標信息。GLES20.glVertexAttribPointer就是把頂點數據,按照openGL喜歡的格式上傳到顯卡存儲。draw方法的調用,是在前面應 用了紋理id的情況下,所以繪制紋理坐標的時候,會使用上傳的紋理圖片。

每次都需要把數據上傳到OpenGL,畢竟顯存和內存不是同一個地方,OpenGL采用了CS模式。當然使用VBO等技術可以把數據緩存在顯存,以提高運行性能。

原文鏈接:http://www.eyeandroid.com/thread-16422-1-4.html

責任編輯:閆佳明 來源: bbs.9ria.
相關推薦

2014-04-29 14:16:54

2014-04-29 14:49:37

OpenGL ES 2Android應用投影

2013-04-26 10:26:08

2014-04-24 11:16:00

OpenGL ES 2入門

2014-04-24 13:26:24

OpenGL ES2.iOSiPhone

2014-04-24 14:00:35

OpenGL ES 2編程

2013-09-26 14:09:31

iOS開發(fā)OpenGL ES教程繪制矩形

2013-09-02 15:46:06

OpenGLWindows

2014-04-29 14:05:02

OpenGL ESAndroid添加動作

2013-09-26 13:43:13

iOS開發(fā)OpenGL ES教程圖元

2010-02-14 15:27:25

2014-04-24 13:35:11

OpenGL ES2.iOSAndroid

2014-04-29 13:58:11

OpenGL ESAndroid響應觸屏事件

2013-07-05 14:45:05

AndroidOpenGL ES開發(fā)

2013-04-26 11:17:48

2017-07-19 15:25:16

iOS開發(fā)ARKitOpen GL

2017-07-04 12:26:14

ARARKit

2011-08-12 11:08:45

iPhone繪圖QuartZ繪制

2011-08-12 11:01:09

iPhone繪圖QuartZ繪制

2014-04-29 14:08:40

OpenGL ESAndroid應用投影
點贊
收藏

51CTO技術棧公眾號