千家信息网

Android OpenGL ES 2.0如何进行绘制纹理

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章将为大家详细讲解有关Android OpenGL ES 2.0如何进行绘制纹理,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。纹理,在OpenG
千家信息网最后更新 2026年11月02日Android OpenGL ES 2.0如何进行绘制纹理

这篇文章将为大家详细讲解有关Android OpenGL ES 2.0如何进行绘制纹理,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

纹理,在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。

java代码

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。我们需要实现接口的方法。

java代码

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的使用,上代码先:

java代码

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(); }

1、启用2D纹理

绘制区域创建的时候,我们设置了启用2D的纹理,并且激活了纹理单元unit0。什么意思呢,说起来话长,以后慢慢说。简单说一下,记住OpenGL 是基于状态的,就是很多状态的设置和切换,这里启用GL_TEXTURE_2D就是一个状态的开启,表明OpenGL可以使用2D纹理。

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

2、加载顶点

OpenGL绘制图形是根据顶点以后链接起来的。为什么要这样,其实这样很强大是一种设计吧。顶点可以暂时简单理解为含有位置信息的坐标点。

java代码

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

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

3、初始化着色器

这个着色器就是ES2.0的特色,又叫可编程着色器,也是区别于ES1.x的本质。这里只做简单的介绍。可编程着色器是一种脚本,语法类似C语言,脚本分为顶点着色器和片段着色器,分别对应了openGL不同的渲染流程。

顶点着色器:

java代码

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; }

片段着色器:

java代码

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 提供了相关函数去做这些事情。

java代码

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 流水线渲染的处理流程。

封装的加载着色器的辅助方法:

java代码

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; }

java代码

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相当于句柄去改变它的内部状态。

4、加载纹理

就是把图片的数据上传到显存,以后再使用它。请注意纹理图片的长和宽***是2的N次方,不然不一定能绘制出来。

java代码

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就

可以操作这个纹理了。至于纹理的一些过滤特性设置,将来再说。

现在貌似就剩下绘制了,准备好了顶点信息,顶点对应的纹理坐标。初始化了着色器,上传了纹理图片。接下来就已把他们合起来绘制了。

java代码

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采用了CS模式。当然使用VBO等技术可以把数据缓存在显存,以提高运行性能。

关于Android OpenGL ES 2.0如何进行绘制纹理就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

纹理 顶点 代码 坐标 数据 就是 图片 脚本 区域 显存 矩形 存储 信息 方法 片段 状态 接下来 接口 显卡 版本 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网络科技 上海挚品互联网科技有限公司 苏州找大状互联网科技有限公司 北京路客互联网科技有限公司 美国互联网保险保险科技 互联网巨头反垄断金融科技 互联网巨头科技公司 科技服务与互联网 互联网科技公司发展 互联网科技论文 互联网科技logo设计 青海省互联网新科技 汉立互联网科技有限公司联系电话 佳云科技和互联网广告 民营互联网公司国防科技 互联网与数字科技关系 江苏互联网科技小贷 东营紫格互联网科技 2021世界互联网领先科技成果发布 杭州科技互联网公司有哪些 广州为想互联网科技有限公司 科技互联网项目 厦门中拓互联网络科技有限公司 互联网巨头反垄断金融科技 期待互联网科技 壹账通互联网科技 四川狮子座互联网科技 哈尔滨屹滨互联网科技有限公司 中国互联网十大科技股排名 国大互联网教育科技有限公司工作
0