纹理。在openGL中,能够理解为载入到显卡显存中的图片。Android设备在2.2開始支持openGL ES2.0。从前都是ES1.0 和 ES1.1的版本号。简单来说,openGL ES是为了嵌入设备进行功能剪裁后的openGL版本号。ES2.0是和1.x版本号不兼容的,差别和兼容性參见android 官方文档。

首先,android使用openGL提供了特殊的view作为基础叫做GLSurfaceView。我们的view须要继承GLSurfaceView。例如以下简单演示样例:

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

并没有什么特别之处。android view的渲染操作须要实现一个render接口。GLSurfaceView的渲染接口为android.opengl.GLSurfaceView.Renderer。

我们须要实现接口的方法。

public class MyRenderer implements Renderer {public void onDrawFrame(GL10 gl) {}public void onSurfaceChanged(GL10 gl, int width, int height) {}public void onSurfaceCreated(GL10 gl, EGLConfig config) {}}

接口实现3个方法,相应绘制。绘制区域变化,区域创建。须要说明的是參数GL10 gl是openGL es1.x版本号的对象。

这里我们不会使用到。另一点就是,onDrawFrame方法的调用是有系统调用的。不须要手动调用。系统会以一定的频率不断的回调。

接下来我们进入ES2.0的使用,先上代码:

public void onSurfaceCreated(GL10 gl, EGLConfig config) {GLES20.glEnable(GLES20.GL_TEXTURE_2D);// Active the texture unit 0GLES20.glActiveTexture(GLES20.GL_TEXTURE0);loadVertex();initShader();loadTexture();
}

绘制区域创建的时候。我们设置了启用2D的纹理,而且激活了纹理单元unit0。

什么意思呢。说起来话长,以后慢慢说。简单说一下。记住openGL是基于状态的。就是非常多状态的设置和切换,这里启用GL_TEXTURE_2D就是一个状态的开启,表明openGL能够使用2D纹理。

那神马是激活纹理单元,这个和硬件有点关系,openGL要显卡会划分存储纹理的存储区域不止一个区域。这里是使用区域 unit 0,多重纹理绘制能够开启多个。这个以后说。接下来,调用了三个函数。加载顶点,初始化着色器,加载纹理。

第一,加载顶点。openGL绘制图形是依据顶点以后链接起来的。

为什么要这样,事实上这样非常强大是一种设计吧。

顶点能够临时简单理解为含有位置信息的坐标点。

展开代码例如以下:

private void loadVertex() {// float size  = 4this.vertex = ByteBuffer.allocateDirect(quadVertex.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();this.vertex.put(quadVertex).position(0);// short size = 2this.index = ByteBuffer.allocateDirect(quadIndex.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer();this.index.put(quadIndex).position(0);
}private FloatBuffer vertex;
private ShortBuffer index;private float[] quadVertex = new float[] {-0.5f, 0.5f, 0.0f, // Position 00, 1.0f, // TexCoord 0-0.5f, -0.5f, 0.0f, // Position 10, 0, // TexCoord 10.5f , -0.5f, 0.0f, // Position 21.0f, 0, // TexCoord 20.5f, 0.5f, 0.0f, // Position 31.0f, 1.0f, // TexCoord 3
};private short[] quadIndex = new short[] {(short)(0), // Position 0(short)(1), // Position 1(short)(2), // Position 2(short)(2), // Position 2  (short)(3), // Position 3  (short)(0), // Position 0
};

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个三角形合成了一个矩形,几句话非常难解释清楚。

所以说,这段代码就是把矩形的位置和纹理坐标。存储到本地数据。准备后面使用而已。

第二。初始化着色器。这个着色器就是ES2.0的特色,又叫可编程着色器,也是差别于ES1.x的本质。这里仅仅做简单的介绍。

可编程着色器是一种脚本。语法类似C语言。脚本分为顶点着色器和片段着色器。分别相应了openGL不同的渲染流程。例如以下:

顶点着色器:

uniform mat4 u_MVPMatrix;attribute vec4 a_position;
attribute vec2 a_texCoord;varying vec2 v_texCoord;void main() {gl_Position = a_position;v_texCoord  = a_texCoord;   }

片段着色器:

precision lowp float;       varying vec2 v_texCoord;
uniform sampler2D u_samplerTexture;void main()
{                                                    gl_FragColor = texture2D(u_samplerTexture, v_texCoord);
}

这里记住一句话,顶点着色器,会在顶点上运行;片段着色器会在像素点上运行。

刚才的矩形就有4个顶点。每一个顶点都会应用这个脚本。也就是说。顶点是位置相关信息,片段是色彩纹理相关信息。

这个2段脚本都是文本。须要编译。链接。等等一些操作才干被ES2.0所使用。

过程就像C语言的编译执行过程。openGL 提供了相关函数去做这些事情。例如以下:

private void initShader() {String vertexSource   = Tools.readFromAssets("VertexShader.glsl");String fragmentSource = Tools.readFromAssets("FragmentShader.glsl");// Load the shaders and get a linked programprogram = GLHelper.loadProgram(vertexSource, fragmentSource);// Get the attribute locationsattribPosition = GLES20.glGetAttribLocation(program, "a_position");attribTexCoord = GLES20.glGetAttribLocation(program, "a_texCoord");uniformTexture = GLES20.glGetUniformLocation(program, "u_samplerTexture");GLES20.glUseProgram(program);GLES20.glEnableVertexAttribArray(attribPosition);GLES20.glEnableVertexAttribArray(attribTexCoord);// Set the sampler to texture unit 0GLES20.glUniform1i(uniformTexture, 0);
}

能够看到,顶点和片段一起构成一个program。它能够被openGL所使用,是一个编译好的脚本程序,存储在显存。 GLES20.glGetAttribLocation 和 GLES20.glGetUniformLocation 这句话是神马作用呢。简单说就是。java程序和着色器脚本数据通信的。把就像參数的传递一样。这样脚本就能依据外界的參数变化。实时的改变openGL流水线渲染的处理流程。

下面是我封装的加载着色器的辅助方法:

public static int loadProgram(String vertexSource, String fragmentSource) { // Load the vertex shadersint vertexShader = GLHelper.loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);// Load the fragment shadersint fragmentShader = GLHelper.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);// Create the program objectint program = GLES20.glCreateProgram();if (program == 0) {throw new RuntimeException("Error create program.");}GLES20.glAttachShader(program, vertexShader);GLES20.glAttachShader(program, fragmentShader);// Link the programGLES20.glLinkProgram(program);int[] linked = new int[1];// Check the link statusGLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linked, 0);if (linked[0] == 0) {GLES20.glDeleteProgram(program);throw new RuntimeException("Error linking program: " +  GLES20.glGetProgramInfoLog(program));}// Free up no longer needed shader resourcesGLES20.glDeleteShader(vertexShader);GLES20.glDeleteShader(fragmentShader);return program;
}
public static int loadShader(int shaderType, String source) { // Create the shader objectint shader = GLES20.glCreateShader(shaderType);if (shader == 0) {throw new RuntimeException("Error create shader.");}int[] compiled = new int[1];// Load the shader sourceGLES20.glShaderSource(shader, source);// Compile the shaderGLES20.glCompileShader(shader);// Check the compile statusGLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);if (compiled[0] == 0) {GLES20.glDeleteShader(shader);throw new RuntimeException("Error compile shader: " + GLES20.glGetShaderInfoLog(shader));}return shader;
}

为什么openGL的非常多操作目标都是int类型的,由于openGL仅仅会在显存生成或绑定地址。返回id,以后用id相当于句柄去改变它的内部状态。

第三。就是加载纹理了。加载纹理,就是把图片的数据上传到显存。以后在使用它。

请注意纹理图片的长和宽最好是2的N次方,不然不一定能绘制出来。

static int[] loadTexture(String path) {int[] textureId = new int[1];// Generate a texture objectGLES20.glGenTextures(1, textureId, 0);int[] result = null;if (textureId[0] != 0) {InputStream is = Tools.readFromAsserts(path);Bitmap bitmap;try {bitmap = BitmapFactory.decodeStream(is);} finally {try {is.close();} catch (IOException e) {throw new RuntimeException("Error loading Bitmap.");}}result = new int[3];result[TEXTURE_ID] = textureId[0]; // TEXTURE_IDresult[TEXTURE_WIDTH] = bitmap.getWidth(); // TEXTURE_WIDTHresult[TEXTURE_HEIGHT] = bitmap.getHeight(); // TEXTURE_HEIGHT// Bind to the texture in OpenGLGLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]);// Set filteringGLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);// Load the bitmap into the bound texture.GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);// Recycle the bitmap, since its data has been loaded into OpenGL.bitmap.recycle();} else {throw new RuntimeException("Error loading texture.");}return result;}

代码一目了然。这里使用了android的工具类吧bitmap直接转换成openGL纹理须要的格式了。过程是,先生成一个纹理的id在显卡上的,以后依据id上传纹理数据,以后保存这个id就能够操作这个纹理了。

至于纹理的一些过滤特性设置,下次再说。

如今貌似就剩下绘制了,准备好了顶点信息。顶点相应的纹理坐标。

初始化了着色器。上传了纹理图片。

接下来就已把他们合起来绘制了。

public void onDrawFrame(GL10 gl) {// clear screen to blackGLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);vertex.position(0);// load the position// 3(x , y , z)// (2 + 3 )* 4 (float size) = 20GLES20.glVertexAttribPointer(attribPosition, 3, GLES20.GL_FLOAT, false, 20, vertex);vertex.position(3);// load the texture coordinateGLES20.glVertexAttribPointer(attribTexCoord, 2, GLES20.GL_FLOAT,false, 20, vertex);GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, index);
}

我尽力保持了代码的简单,openGL的基于状态体现。bind这个函数无处不在,这里bindTexture就是通知openGL使用那个id的纹理图片。接下来的操作就是针对bind的图片的。

绘制就须要让openGL知道绘制神马。所以这里须要用到vertex这个本地数据容器,里面装在的是顶点和纹理坐标信息。 GLES20.glVertexAttribPointer就是把顶点数据。依照openGL喜欢的格式上传到显卡存储。

draw方法的调用。是在前面应用了纹理id的情况下,所以绘制纹理坐标的时候,会使用上传的纹理图片。

是的,每次都须要把数据上传到openGL。毕竟显存和内存不是同一个地方,openGL採用了client-服务端的设计模式。

当然使用VBO等技术能够把数据缓存在显存,提高执行性能。

这个以后再说吧。

android openGL ES2 一切从绘制纹理開始相关推荐

  1. Android OpenGL ES (八)纹理绘制

    基本原理 与渐变色接近,但有些区别: 渐变色:光栅化过程中,计算出颜色值,然后在片段着色器的时候可以直接赋值 纹理:光栅化过程中,计算出当前片段在纹理上的坐标位置,然后在片段着色器的中,根据这个纹理上 ...

  2. Android OpenGL ES 2.0绘制简单三角形

    实现步骤 l  实现一个工具类ShalderUtil,用于将着色器代码加载进显卡进行编译 l  实现一个三角形Triangle类 在该类中加载着色器.初始化顶点数据.初始化着色器以及绘制三角形方法 l ...

  3. opengl微开发之1-从零開始

    对OpenGL有一点了解之后,如今開始真正编写代码. 今天的内容: 使用FreeGLUT创建OpenGL的上下文环境 初始化GLEW 创建一个OpenGL的的模板范例 第一步: 一个OpenGL的上下 ...

  4. Android OpenGL ES 学习(十一) –渲染YUV视频以及视频抖音特效

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  5. Android OpenGL ES 学习(二) -- 图形渲染管线和GLSL

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  6. Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  7. Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  8. Android OpenGL ES 学习(九) – 坐标系统和实现3D效果

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  9. Android OpenGL ES 学习(五) -- 渐变色

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

最新文章

  1. Js打印表格时部分边框不显示(table 标签)
  2. linux怎么切换不同版本的r,在linux中用同一个版本的R 同时安装 Seurat2 和 Seurat3
  3. zoj 1406 Jungle Roads
  4. ffdshow神奇的功能:视频播放时显示运动矢量和QP
  5. Python爬上不得姐 并将段子写入数据库
  6. 单片机c语言双边拉幕灯,51单片机C语言入门教程
  7. 局域网唤醒(Wake On LAN)+树莓派实现远程设备唤醒
  8. BUUCTF Cipher
  9. 新浪小编为你介绍留声机的古与今
  10. Python 批量给图片添加水印小工具
  11. redis 修改默认端口号6379(Windows)
  12. 7-6 求一批整数中出现最多的个位数字 (20 分)
  13. ftp服务器文件传输,FTP服务器之间传输文件
  14. 使用ZXing扫描多个二维码,条形码
  15. 学生HTML个人网页作业作品 (苹果商城HTML+CSS)---苹果商城8页 带报告
  16. PB中关于GetChild的用法
  17. 关于计算机备份的管理规定,电脑管理制度网友投稿(备份存档)
  18. 画太极图辅助程序_Python精灵模块应用实例
  19. 在JointJS元素中使用html
  20. 270亿美元!Salesforce收购Slack,协同办公不再是一门好生意

热门文章

  1. java中elapseTime设置新时间,Java ApplicationLike.getApplicationStartElapsedTime方法代码示例...
  2. python 微信数据_python 处理微信对账单数据的实例代码
  3. python语法与java语法的区别_Python语言与java语法的异同之处
  4. java冒泡怎么写_java 冒泡 又一种写法
  5. python length从哪个包引入_python collections包
  6. Linux哈希表数组,开地址哈希表(Hash Table)的接口定义与实现分析
  7. oracle导入dmp文件数据,dmp文件导入Oracle数据库
  8. adams怎么做往复运动_关于HiFi | 在家里听音乐看电影,喇叭应该怎么选?
  9. redis value最大值_Redis基础知识整理
  10. 使用use strict指令的目的