Android OpenGL ES(三)----编程框架(一)

简介: Android OpenGL ES(三)----编程框架(一)

首先当然是创建Android项目,你可以选择最新的Android Studio也可以选择eclipse都是一样的。我们重点讲解开发OpenGL ES的流程


1.定义顶点着色器和片段着色器


第一节我们讲解的已经很细致了,为了便于理解在这里在详细的说明一下。并且换一种方式定义着色器。


我们知道第一篇定义的顶点的坐标和颜色是分开的,这样可以但如果把它们放在一起会方便许多。


假设我们要绘制一个长方形和两条直线,二个定点,我们就需要这样来定义这个数组。

float[] tableVerticesWithTriangles = {
//两个三角形和三角形的颜色分量
                0f, 0f, 1f, 1f, 1f,
                -0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
                0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
                0.5f, 0.5f, 0.7f, 0.7f, 0.7f,
                -0.5f, 0.5f, 0.7f, 0.7f, 0.7f,
                -0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
//两条直线和直线的颜色分量
                -0.5f, 0f, 1f, 0f, 0f,
                0.5f, 0f, 1f, 0f, 0f,
//两个顶点和顶点的颜色分量
                0f, -0.25f, 0f, 0f, 1f,
                0f, 0.25f, 1f, 0f, 0f
        };


前面的两个顶点代表坐标,后面三个顶点代表颜色分别为:红色,绿色和蓝色。


接着必须对应的建立对应的顶点着色器,假设raw文件夹下的顶点着色器的文件名是

simple_vertex_shader.glsl:
attribute vec4 a_Position;    
 attribute vec4 a_Color;
 varying vec4 v_Color;
 void main()                    
 {
     v_Color=a_Color;
     gl_Position = a_Position;
     gl_PointSize = 10.0;
 }


我们加入了一个新的属性a_Color,也加入了一个叫做v_Color的新varying。上一篇已经讲过varying是一个特殊的变量类型,它把给它的值进行混合并把这些混合的值发送给片段着色器。


我们把varying也加入片段着色器,在raw文件夹下创建simple_fragment_shader.glsl:

precision mediump float; 
 varying vec4 v_Color;
 void main()                     
 {                               
     gl_FragColor = v_Color;
 }


我们用varying变量v_Color替换了原来代码中的uniform。如果这个片段属于一条直线,那个OpenGL就会用构成那条直线的两个顶点计算其混合后的颜色。


2.加载着色器


在项目中创建一个新的Java源代码包,命名为util把,至于前缀得看你项目的名称或者你自己的爱好。


在这个包下创建一个名为“TextResourceReader”的新类

public class TextResourceReader {
     public static String readTextFileFromResource(Context context, int resourceId) {
         StringBuilder body = new StringBuilder();
         try {
             InputStream is = context.getResources().openRawResource(resourceId);
             InputStreamReader reader = new InputStreamReader(is);
             BufferedReader bufferedReader = new BufferedReader(reader);
             String nextLine;
             while ((nextLine = bufferedReader.readLine()) != null) {
                 body.append(nextLine);
                 body.append("\n");
             }
         } catch (IOException e) {
             throw new RuntimeException(
                     "Could not open resource: " + resourceId, e);
         } catch (Resources.NotFoundException nfe) {
             throw new RuntimeException("Resource not found: " + resourceId, nfe);
         }
         return body.toString();
     }
 }


至于这段代码我就不过多的解释了这属于JAVA基础也可以说是Android基础,本文重点讲解OpenGL,这段代码的作用是加载着色器。



3.初始化OpenGL


定义两个成员变量:

private GLSurfaceView glSurfaceView;
private boolean rendererSet=false;


在Activity的OnCreate()方法里面初始化glSurfaceView:

this.glSurfaceView = new GLSurfaceView(this);

检查系统是否支持OpenGL ES 2.0:

final ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
         final ConfigurationInfo configurationInfo=activityManager.getDeviceConfigurationInfo();
         final boolean supportsEs2=configurationInfo.reqGlEsVersion>=0x20000;

首先我们需要Android ActivityManager的一个引用,用它来获取设备的配置信息,然后,取出reqGlEsVersion变量检查OpenGL ES版本号。如果版本号为0*20000或后续版本,我们就可以使用OpenGL ES2.0的API了。


为OpenGL ES2.0配置渲染表面

if(supportsEs2){
             this.glSurfaceView.setEGLContextClientVersion(2);
             this.glSurfaceView.setRenderer(new LYJRenderer(this));
             this.rendererSet=true;
         }else{
             Toast.makeText(this,"bu zhi chi gai banben ",Toast.LENGTH_SHORT).show();
             return;
         }

如果设备支持OpenGL ES2.0,我们就通过调用setEGLContextClientVersion(2)配置这个surface视图,然后调用setReader()传进自定义的Renderer类的一个新实例,其实如果设备不支持OpenGL ES2.0,公开发布的应用在这个设备的应用程序市场中被隐藏起来,至于隐藏,后续讲到,当然这也是清单文件的基础知识。rendererSet记住GLSurfaceView是否处于有效状态。


setContentView(this.glSurfaceView);

相信大家都知道上面的作用,就是把GLSurfaceView加入到Activity中。并把它显示到屏幕上。


当然我们还需要利用Activity生命周期释放资源,如果没有下面的代码,应用程序可能会崩溃。

@Override
     protected void onResume() {
         super.onResume();
         if(this.rendererSet){
             this.glSurfaceView.onResume();
         }
     }
     @Override
     protected void onPause() {
         super.onPause();
         if(this.rendererSet){
             this.glSurfaceView.onPause();
         }
     }


创建Renderer类


让我们看一下这个接口的方法:


onSurfaceCreated(GL10 glUnused,EGLConfig config)


当Surface被创建的时候,GLSurfaceView会调用这个方法;这发生在应用程序第一次运行的时候,并且,当设备被唤醒或者用户从其他Activity切换回来时,这个方法可能会被调用。在实践中,这意味着,当应用程序运行时,本方法可能会被调用多次。

onSurfaceChanged(GL10 glUnused,int width,int height)


在Surface被创建后,每次Surface尺寸变化时,这个方法都会被GLSurfaceView调用到,在横屏,竖屏来回切换的时候,Surface尺寸会发生变化。

onDrawFrame(GL10 glUnused)

当绘制一帧时,这个方法会被GLSurfaceView调用。在这个方法中,我们一定要绘制一些东西,即使只是清空屏幕;因为,在这个方法返回后,渲染缓冲区会被交换并显示在屏幕上,如果什么都没画,可以会看到糟糕的闪烁效果。


观察这些方法,可能细心的会发现都有一个GL10参数,这是OpenGL1.0遗留下来的,如果在1.0的设备上就会用到,当时对于OpenGL ES2.0,GLES20类提供了静态方法存取。


新建渲染器LYJRenderer:

public class LYJRenderer implements GLSurfaceView.Renderer {
public LYJRenderer(Context context) {
         this.context = context;
this.vertexData = ByteBuffer.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
         this.vertexData.put(tableVerticesWithTriangles);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    GLES20.glViewport(0, 0, width, height);
}
    public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }

首先调用在onSurfaceCreated调用glClearColor设置清空屏幕,前三个参数对应颜色,后一个参数是透明度。这里设置为黑色。


下一步就是设置视口的尺寸了也就是glViewport,这就是告诉OpenGL可以用来渲染的surface的大小。


在onDrawFrame中调用glClear清空屏幕,这会擦除屏幕上所有颜色,并调用之前的glClearColor()调用定义的颜色填充整个屏幕。


在Renderer类中读入着色器,在onSurfaceCreated()的结尾除加入如下代码:

String vertexShaderSource = TextResourceReader.readTextFileFromResource(this.context, R.raw.simple_vertex_shader);
        String fragmentShaderSource = TextResourceReader.readTextFileFromResource(this.context, R.raw.simple_fragment_shader);


4.编译着色器


我们把着色器源码从文件中读取出来,下一步就是编译每个着色器了。我们要创建一个新的辅助类,它可以创建新的OpenGL着色器对象,编译着色器并且返回代表那段着色器的对象。一旦写出这个样板代码,在未来的项目中可以重用了。


创建一个名为ShaderHelper类,并添加如下代码:

public class ShaderHelper {
     public static final String TAG = "ShaderHelper";
     public static int compileVertexShader(String shaderCode) {
         return compileShader(GLES20.GL_VERTEX_SHADER, shaderCode);
     }
     public static int compileFragmentShader(String shaderCode) {
         return compileShader(GLES20.GL_FRAGMENT_SHADER, shaderCode);
     }
public static int compileShader(int type, String shaderCode) {


这些代码作为辅助类的基础。下面就要创建新的着色器对象并检查创建是否成功,下面都是在构建compileShader方法。

final int shaderObjectId = GLES20.glCreateShader(type);
         if (shaderObjectId == 0) {
             if (LoggerConfig.ON) {
                 Log.w(TAG, "Counld not create new shader");
             }
             return 0;
         }

这里,用glCreateShader()调用创建了一个新的着色器对象,并把这个对象的ID存入变量shaderObjectId。这个type可以代表定点着色器的GL_VERTEX_SHADER,或者是代表片段着色器的GL_FRAGMENT_SHADER。剩下的代码也用同样的方式。


记住我们是如何创建对象并检查它是否有效的;这个模式将在OpenGL里广泛使用:


1.首先使用一个如glCreateShader()一样的调用创建一个对象,这个调用会返回一个整型值。


2.这个整型值就是OpenGL对象的一个引用。无论后面什么时候要引用这个对象,就要把这个整型值传回 OpenGL。


3.返回值0表示这个对象创建失败,它类似于Java代码返回的null值。


上传和编译着色器源代码

GLES20.glShaderSource(shaderObjectId, shaderCode);

这个调用是告诉OpenGL读入字符串shaderCode定义的源代码,并把它与shaderObjectId所引用的着色器对象关联起来。然后调用下面方法编译着色器:

GLES20.glCompileShader(shaderObjectId);

这个调用告诉OpenGL编译上传到shaderObjectId的源代码。


取出编译状态,加入如下代码:

final int[] compileStatus = new int[1];
         GLES20.glGetShaderiv(shaderObjectId, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

为了检查编译失败还是成功,首先要创建新的长度为1的int数组,称为compileStatus;然后调用glGetShaderiv。这就告诉OpenGL读取与shaderObjectId关联的编译状态,并把它写入compileStatus的第0个元素。


这是Android平台上的OpenGL的另一个通用模式。 为了取出一个值,我们通常会使用一个长度为1的数组,并把这个数组传进OpenGL调用。在一个调用中,我们告诉OpenGL把结果存进数组的第一个元素。


验证编译状态并返回着色器对象ID,代码如下:

if (compileStatus[0] == 0) {
             GLES20.glDeleteShader(shaderObjectId);
             if (LoggerConfig.ON) {
                 Log.w(TAG, "Compilation of shader failed");
             }
             return 0;
         }

如果编译成功返回shaderObjectId:

return shaderObjectId;


在Renderer类中编译着色器,在OnSurfaceView()结尾处加入如下代码:

int vertexShader = ShaderHelper.compileVertexShader(vertexShaderSource);
         int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);


相关文章
|
6月前
|
Java 调度 Android开发
构建高效Android应用:探究Kotlin多线程编程
【2月更文挑战第17天】 在现代移动开发领域,性能优化一直是开发者关注的焦点。特别是在Android平台上,合理利用多线程技术可以显著提升应用程序的响应性和用户体验。本文将深入探讨使用Kotlin进行Android多线程编程的策略与实践,旨在为开发者提供系统化的解决方案和性能提升技巧。我们将从基础概念入手,逐步介绍高级特性,并通过实际案例分析如何有效利用Kotlin协程、线程池以及异步任务处理机制来构建一个更加高效的Android应用。
|
6月前
|
Ubuntu 网络协议 Java
【Android平板编程】远程Ubuntu服务器code-server编程写代码
【Android平板编程】远程Ubuntu服务器code-server编程写代码
|
6月前
|
存储 Java Android开发
OpenCV3 安卓应用编程:1~6 全
OpenCV3 安卓应用编程:1~6 全
43 0
|
6月前
|
存储 算法 开发工具
OpenCV 安卓编程示例:1~6 全
OpenCV 安卓编程示例:1~6 全
150 0
|
6月前
|
Java 调度 数据库
Android 性能优化: 如何进行多线程编程以提高应用性能?
Android 性能优化: 如何进行多线程编程以提高应用性能?
111 0
|
4月前
|
移动开发 开发工具 Android开发
探索安卓与iOS开发的差异:平台特性与编程实践
【7月更文挑战第8天】在移动开发的广阔天地中,安卓和iOS这两大操作系统各自占据着半壁江山。它们在用户界面设计、系统架构及开发工具上展现出截然不同的特色。本文将深入探讨这两个平台在技术实现和开发生态上的关键差异,并分享一些实用的开发技巧,旨在为跨平台开发者提供有价值的见解和建议。
|
5月前
|
安全 IDE Android开发
探索Android与iOS开发的差异:平台特性与编程实践
【6月更文挑战第17天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据半壁江山。它们在用户群体、系统架构以及开发环境上的差异,为开发者带来了不同的挑战和机遇。本文深入探讨了这两个平台在技术实现、界面设计、性能优化等方面的主要区别,并提供了实用的开发建议,旨在帮助开发者更好地理解各自平台的特性,从而创造出更加优秀的移动应用。
|
6月前
|
Java Android开发 Dart
50家大厂面试万字精华总结android编程基础学习
50家大厂面试万字精华总结android编程基础学习
|
6月前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
151 1
Android开发之使用OpenGL实现翻书动画
|
6月前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
75 1
Android开发之OpenGL的画笔工具GL10