如何用android opengl教程 es 加载图片并绘制到屏幕上

3.5.5 OpenGL如何把坐标映射到屏幕
本文所属图书&>&
《OpenGL ES应用开发实践指南:Android卷》分为两部分,共15章:第1章主要介绍开发环境的安装和配置,以及如何创建一个新的OpenGL项目和清空屏幕;第一部分(第2~9章)详细讲解创建一个简单的空气曲棍球游戏的触&&
目前为止,我们还没有解决的一个大问题就是:OpenGL是怎样把我们已经定义的坐标映射到屏幕上的实际物理坐标的?
这个问题的答案很复杂,随着后面章节的讲解,我们会了解到更多有关的内容;目前,我们只需要知道,无论是x还是y坐标,OpenGL都会把屏幕映射到[-1,1]的范围内。这就意味着屏幕的左边对应x轴的-1,而屏幕的右边对应+1;屏幕的底边会对应y轴的-1,而屏幕的顶边就对应+1,如图3-2所示。
不管屏幕是什么形状和大小,这个坐标范围都是一样的,如果我们需要在屏幕上显示任何东西,都需要在这个范围内绘制它们。让我们回到构造函数,把tableVertices-WithTriangles中定义的坐标更新为如下值:
让我们再次运行这个应用,我们应该看到与图3-3类似的屏幕:
这看起来好多了,但是木槌哪去了?事实证明,对于点来说,OpenGL需要我们指定在屏幕上所显示的点的大小,但我们还没指定这个大小呢。
您对本文章有什么意见或着疑问吗?请到您的关注和建议是我们前行的参考和动力&&
您的浏览器不支持嵌入式框架,或者当前配置为不显示嵌入式框架。android平台用OpenGL ES绘制图像的移动
[问题点数:40分,结帖人huxueyan521]
android平台用OpenGL ES绘制图像的移动
[问题点数:40分,结帖人huxueyan521]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
本帖子已过去太久远了,不再提供回复功能。android openGL ES2 一切从绘制纹理开始 - 推酷
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
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 FloatB
private ShortB
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_MVPM
attribute vec4 a_
attribute vec2 a_texC
varying vec2 v_texC
void main()
gl_Position = a_
v_texCoord
片段着色器:
varying vec2 v_texC
uniform sampler2D u_samplerT
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);
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));
为什么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 =
if (textureId[0] != 0) {
InputStream is = Tools.readFromAsserts(path);
bitmap = BitmapFactory.decodeStream(is);
} finally {
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();
throw new RuntimeException(&Error loading texture.&);
代码一目了然,这里使用了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采用了客户端-服务端的设计模式。当然使用VBO等技术可以把数据缓存在显存,提高运行性能。这个以后再说吧。
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
排版有问题
没有分页内容
视频无法显示
图片无法显示扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
OpenGL ES 2.0游戏与图形编程——适用于iOS 和 Android(试读)
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口Android 中使用OpenGL ES进行2D开发(绘制第一个三角形) | Imagination中文技术社区}

我要回帖

更多关于 android opengl教程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信