android openGL ES2 一切从绘制纹理開始
纹理。在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 0
GLES20.glActiveTexture(GLES20.GL_TEXTURE0); loadVertex();
initShader();
loadTexture();
}
绘制区域创建的时候。我们设置了启用2D的纹理,而且激活了纹理单元unit0。
什么意思呢。说起来话长,以后慢慢说。简单说一下。记住openGL是基于状态的。就是非常多状态的设置和切换,这里启用GL_TEXTURE_2D就是一个状态的开启,表明openGL能够使用2D纹理。
那神马是激活纹理单元,这个和硬件有点关系,openGL要显卡会划分存储纹理的存储区域不止一个区域。这里是使用区域 unit 0,多重纹理绘制能够开启多个。这个以后说。接下来,调用了三个函数。加载顶点,初始化着色器,加载纹理。
第一,加载顶点。openGL绘制图形是依据顶点以后链接起来的。
为什么要这样,事实上这样非常强大是一种设计吧。
顶点能够临时简单理解为含有位置信息的坐标点。
展开代码例如以下:
private void loadVertex() {
// float size = 4
this.vertex = ByteBuffer.allocateDirect(quadVertex.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer(); this.vertex.put(quadVertex).position(0); // short size = 2
this.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 0
0, 1.0f, // TexCoord 0 -0.5f, -0.5f, 0.0f, // Position 1
0, 0, // TexCoord 1 0.5f , -0.5f, 0.0f, // Position 2
1.0f, 0, // TexCoord 2 0.5f, 0.5f, 0.0f, // Position 3
1.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 program
program = GLHelper.loadProgram(vertexSource, fragmentSource); // Get the attribute locations
attribPosition = 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 0
GLES20.glUniform1i(uniformTexture, 0);
}
能够看到,顶点和片段一起构成一个program。它能够被openGL所使用,是一个编译好的脚本程序,存储在显存。 GLES20.glGetAttribLocation 和 GLES20.glGetUniformLocation 这句话是神马作用呢。简单说就是。java程序和着色器脚本数据通信的。把就像參数的传递一样。这样脚本就能依据外界的參数变化。实时的改变openGL流水线渲染的处理流程。
下面是我封装的加载着色器的辅助方法:
public static int loadProgram(String vertexSource, String fragmentSource) {
// Load the vertex shaders
int vertexShader = GLHelper.loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); // Load the fragment shaders
int fragmentShader = GLHelper.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); // Create the program object
int program = GLES20.glCreateProgram(); if (program == 0) {
throw new RuntimeException("Error create program.");
} GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader); // Link the program
GLES20.glLinkProgram(program); int[] linked = new int[1]; // Check the link status
GLES20.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 resources
GLES20.glDeleteShader(vertexShader);
GLES20.glDeleteShader(fragmentShader); return program;
}
public static int loadShader(int shaderType, String source) { // Create the shader object
int shader = GLES20.glCreateShader(shaderType); if (shader == 0) {
throw new RuntimeException("Error create shader.");
} int[] compiled = new int[1]; // Load the shader source
GLES20.glShaderSource(shader, source); // Compile the shader
GLES20.glCompileShader(shader); // Check the compile status
GLES20.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 object
GLES20.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_ID
result[TEXTURE_WIDTH] = bitmap.getWidth(); // TEXTURE_WIDTH
result[TEXTURE_HEIGHT] = bitmap.getHeight(); // TEXTURE_HEIGHT // Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]); // Set filtering
GLES20.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 black
GLES20.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) = 20
GLES20.glVertexAttribPointer(attribPosition,
3, GLES20.GL_FLOAT,
false, 20, vertex); vertex.position(3);
// load the texture coordinate
GLES20.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 一切从绘制纹理開始的更多相关文章
- opengl微开发之1-从零開始
对OpenGL有一点了解之后,如今開始真正编写代码. 今天的内容: 使用FreeGLUT创建OpenGL的上下文环境 初始化GLEW 创建一个OpenGL的的模板范例 第一步: 一个OpenGL的上下 ...
- Android OpenGL ES 开发:绘制图形
OpenGL 绘制图形步骤 上一篇介绍了 OpenGL 的相关概念,今天来实际操作,使用 OpenGL 绘制出图形,对其过程有一个初步的了解. OpenGL 绘制图形主要概括成以下几个步骤: 创建程序 ...
- Android OpenGL ES(八)----纹理编程框架
1.把纹理载入进OpenGL中 我们的第一个任务就是把一个图像文件的数据载入到一个OpenGL的纹理中. 作为開始.让我们又一次舍弃第二篇的框架.又一次创建一个程序,新建一个util工具包,在该包下创 ...
- Android +NDK+eclipse+opengl ES2.0 开启深度測试
參考:https://www.opengl.org/discussion_boards/showthread.php/172736-OpenGL-ES-Depth-Buffer-Problem 环境: ...
- OPENGL ES2.0如何不使用glActiveTexture而显示多个图片
https://www.oschina.net/question/253717_72107 用opengl es 2.0显示多个图片的话,我只会一种方式,先将图片生成纹理,然后用下面的方式渲染 // ...
- Android OpenGL ES 3.0 纹理应用
本文主要演示OpenGL ES 3.0 纹理演示.接口大部分和2.0没什么区别,脚本稍微有了点变化而已. 扩展GLSurfaceView package com.example.gles300; im ...
- 2.x最终照着教程,成功使用OpenGL ES 绘制纹理贴图,添加了灰度图
在之前成功绘制变色的几何图形之后,今天利用Openg ES的可编程管线绘制出第一张纹理. 学校时候不知道OpenGL的重要性,怕晦涩的语法.没有跟老师学习OpenGL的环境配置,现在仅仅能利用coco ...
- Eclipse中通过Android模拟器调用OpenGL ES2.0函数操作步骤
原文地址: Eclipse中通过Android模拟器调用OpenGL ES2.0函数操作步骤 - 网络资源是无限的 - 博客频道 - CSDN.NET http://blog.csdn.net/fen ...
- Android OpenGL ES 应用(二) 纹理
上一篇讲了基础入门 OpenGL (一) ,这一次主要学习OpenGL 纹理基本学习总结 要是做复杂的OpenGL应用程序,一定会用到纹理技术.纹理说白了就是把图片或者视频图像绘制到OpenGL空间中 ...
随机推荐
- vue-router的创建(1)
vue-router的创建 <!doctype html> <html lang="en"> <head> <meta charset=& ...
- CSLA框架的codesmith模板改造
一直有关注CSLA框架,最近闲来无事,折腾了下,在最新的r3054版本基础上修改了一些东西,以备自己用,有兴趣的园友可以下载共同研究 1.添加了默认的授权规则 如果是列表对象则生成列表权限,User的 ...
- 【hdu 6321】Dynamic Graph Matching
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] DP 设f[i][j]表示前i个操作,已经匹配了的点的状态集合为j的方案数 对于+操作 有两种情况. 1.这条边作为匹配的边 2.这 ...
- 【codeforces 508D】The Maths lecture
[题目链接]:http://codeforces.com/problemset/problem/507/D [题意] 让你找符合这样数字的数的个数: 1.有n个数码 2.某个后缀%k的值为0 3.大于 ...
- 我是怎么从项目中的lib加JAR更换为maven管理的
原来我对maven的使用应该还是去年的时候吧,当时对maven并不感冒(请不要吐槽哈),认为为什么一定要用maven来管理呢,我自己管理jar不是一样么,当时还认为自己管理jar还各种方便还对mave ...
- 深刻理解Docker镜像大小
都说容器大法好,可是假设没有Docker镜像,Docker该是多无趣啊. 是否还记得第一个接触Docker的时候,你从Docker Hub下拉的那个镜像呢?在那个处女镜像的基础上.你执行了容器生涯的处 ...
- android sudio 执行的中文是乱码解决方式
1.File-->Setings-->查找file encodings 例如以下图 2.将 IDE Encoding .Project Encoding.Default encoding ...
- 51nod 1435 位数阶乘 (手动计算)
题目: 1435 位数阶乘 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 X是一个n位数的正整数 (x=a0a1...an−1) ...
- javascript 精确加减乘除
最近一个项目中要使用 JS 实现自动计算的功能,本以为只是实现简单的加.减.乘.除就可以了,于是三下五除二做完了. 正当我窃喜的时候,发现问题了... 进行一些浮点数运算时,计算结果都是让我大跌眼镜啊 ...
- CentOS-1810系统DHCP服务器ISC DHCP软件配置说明
DHCP 全称Dynamic Host configuration protocol, 动态主机配置协议.是一个局域网的网络协议,使用UDP协议工作,它可以为客户机自动分配IP地址.子网掩码以及缺省网 ...