图像和动画
这些文章讲述教你怎么使用图像写出更有竞争力的app,如果你想要更好的用户体验以及用户更好的体验效果,下面的这些类将会帮助到你。
第一章 使用OpenGL ES 展示图像
为了使openGLES在你的app上运行,你需要写一个view 容器。最简单的方式就是实现GLSurfaceView和GLSurfaceView.Renderer.
GLSurfaceView是一个view容器opengl 可以画图像,GLSurfaceView.Renderer控制你在上面可以画什么图像。需要获取更多的信息,可以查看OpenGL ES开发手册。
GLSurfaceView只是一种你的应用和OpenGL ES打交道的方式,对于全屏幕或者基本上全屏幕的图像应用来说是可行的。开发者如果只是在他们的不居中显示很小的一部分opengl 则应该使用TextureView.还有一种就是开发者自己设计,它可以建立在SurfaceView之上,但是这需要写很多额外的代码。
这篇文章讲述如何使用GLSurfaceView 和GLSurceView.Renderer实现一个很小的app.
1. 在manifest文件中申明使用OpengGL ES
为了让你的app能够使用OpenGL 2.0,你必须加入下面的申明。
1<uses-feature android:glEsVersion="0x00020000" android:required="true" />
1
2<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" /> <supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />
2. 给OpenGL ES 创建一个Activity
android程序使用OpenGL ES使用activity就像其它的程序有自己的接口一样,和其它程序最大的不同就是你放在你布局文件中的东西。大部分的程序会用TextView, Button, ListView这些组件,但是如果你使用OpenGL 的话,你应该加入GLSurfaceView.
下面的代码示例展示了一个最小的实现,一个activity,使用一个GLSurfaceView作为它的基础view
1
2
3
4
5
6
7
8
9
10
11public class OpenGLES20Activity extends Activity { private GLSurfaceView mGLView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a GLSurfaceView instance and set it // as the ContentView for this Activity. mGLView = new MyGLSurfaceView(this); setContentView(mGLView); } }
一个GLSurfaceView指定了你在哪里可以画GL图像,它并没有做其它的什么事情。真正的画图是被GLSurfaceView.Renderer类控制的。实际上,这个对象的代码很少,你可以创建一个不能修改的GLSurfaceView实例,但是不要那么做。当你需要捕获触摸事件的时候你需要拓展这个类,这些会在Responding to Touch Event章节讲述。
1
2
3
4
5
6
7
8
9
10
11
12class MyGLSurfaceView extends GLSurfaceView { private final MyGLRenderer mRenderer; public MyGLSurfaceView(Context context){ super(context); // Create an OpenGL ES 2.0 context setEGLContextClientVersion(2); mRenderer = new MyGLRenderer(); // Set the Renderer for drawing on the GLSurfaceView setRenderer(mRenderer); } }
GLSurfaceView.RENDERMODE_WHEN_DIRTY
1
2// Render the view only when there is a change in the drawing data setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
4. 建立一个渲染类
这是GLSurfaceView.Renderer类的实现,这才是使用OpenGL ES功能的开始。这个类控制了你可以在GLSurfaceView中画什么东西。这里有三个方法会被系统调用,
onSurfaceCreated 当建立起来OpengGL Es的环境之后会调用一次
onDrawFrame() 每次重新绘制的时候会被调用
onSurfaceChanged() view改变的时候会被调用,比如每次设备的屏幕旋转之后会被调用
下面是一个OpenGL ES的基本实现,它没有做任何的事情,只是在GLSurfaceView中显示了一个黑色的背景。
1
2
3
4
5
6
7
8
9
10
11
12
13public class MyGLRenderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 unused, EGLConfig config) { // Set the background frame color GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } public void onDrawFrame(GL10 unused) { // Redraw background color GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); } public void onSurfaceChanged(GL10 unused, int width, int height) { GLES20.glViewport(0, 0, width, height); } }
当你使用ES 2.0的时候你有个疑问为什么这些方法有个GL10参数,那些方法就是简单地重新使用2.0api,保持android 框架的代码更简洁。
如果你已经使用过OpenGL ES 的api绘图,那么上面教的这些就可以让你开始写程序了,如果你还对OpengGL 不熟悉,可以继续看下面的内容。
一个最简单的Openg gl程序,绿色的背景
建立一个MainActivity,然后创建两个类,一个是SurfaceView类主要是显示布局用,一个是Render类,真正的绘图的地方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.example.www.graphictest; import android.opengl.GLSurfaceView; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { private GLSurfaceView mGLView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a GLSurfaceView instance and set it as the ContentView for this Activity mGLView = new MyGLSurfaceView(this); setContentView(mGLView); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.example.www.graphictest; import android.content.Context; import android.opengl.GLSurfaceView; /** * Created by wang on 17-8-26. */ class MyGLSurfaceView extends GLSurfaceView { private final MyGLRenderer mRenderer; public MyGLSurfaceView(Context context) { super(context); // Create a OpenGL ES 3.0 context setEGLContextClientVersion(3); mRenderer = new MyGLRenderer(); setRenderer(mRenderer); // Render the view only when there is a change in the drawing data setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package com.example.www.graphictest; import android.opengl.GLES30; import android.opengl.GLSurfaceView; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; /** * Created by wang on 17-8-26. */ class MyGLRenderer implements GLSurfaceView.Renderer{ @Override public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { // Set the background frame color GLES30.glClearColor(0.0f, 0.4f, 0.3f, 1.0f); } @Override public void onSurfaceChanged(GL10 gl10, int width, int height) { // Adjust the viewport based on geometry changes, such as screen rotaion GLES30.glViewport(0, 0, width, height); } @Override public void onDrawFrame(GL10 gl10) { // Draw background color GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT); } }
第二章 定义形状
在需要将形状画上opengl之前,你先需要创建你的形状。如果你在写opengl程序而对于opengl一无所知的话会有些尴尬,因为基本的原理都是使用的opengl。
这篇教程解释opengl的坐标系统,如何关联上android设备的屏幕,基本的形状的定义,形状的面,已经三角形和正方形也都会讲述。
1. 定义三角形
Opengl 允许你定义三维的图形,所以在你开始画三角形之前,你需要定义自己的坐标系。
在Opengl中,最典型的方式就是定义一个为这个坐标系定义一个顶点数组。为了最有效,你可以把这些数据写入一个ByteBuffer中,这个通过opengl的图形管道传输进去交给它们处理。
下面是定义了一个三角形
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class Triangle { private FloatBuffer vertexBuffer; // number of coordinates per vertex in this array static final int COORDS_PER_VERTEX = 3; static float triangleCoords[] = { // in counterclockwise order: 0.0f, 0.622008459f, 0.0f, // top -0.5f, -0.311004243f, 0.0f, // bottom left 0.5f, -0.311004243f, 0.0f // bottom right }; // Set color with red, green, blue and alpha (opacity) values float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f }; public Triangle() { // initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (number of coordinate values * 4 bytes per float) triangleCoords.length * 4); // use the device hardware's native byte order bb.order(ByteOrder.nativeOrder()); // create a floating point buffer from the ByteBuffer vertexBuffer = bb.asFloatBuffer(); // add the coordinates to the FloatBuffer vertexBuffer.put(triangleCoords); // set the buffer to read the first coordinate vertexBuffer.position(0); } }
更多的参考,请看opengl Es参考手册。
这个形状定义为逆时针,他们的绘画的先后顺序很重要,因为它定义了哪部分在形状的最前面,更多的关于opengl es隐藏和覆盖的信息,查看开发者手册。
2. 定义正方形
定义三角形很简单,但是如果你想更复杂一点,定义一个正方形呢?这里有集中常见的方法,但是最常用的方法就是画两个三角形拼成一个正方形。
如上图所示使用两个三角形画一个正方形。
再次说明,你应该定义两个三角形为逆时针,把他们的值放在一个ByreBuffer中,为了避免定义两个坐标形状,使用绘画list告诉opengl 管道怎么样绘制这些顶点。
这下面是个示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35public class Square { private FloatBuffer vertexBuffer; private ShortBuffer drawListBuffer; // number of coordinates per vertex in this array static final int COORDS_PER_VERTEX = 3; static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left -0.5f, -0.5f, 0.0f, // bottom left 0.5f, -0.5f, 0.0f, // bottom right 0.5f, 0.5f, 0.0f }; // top right private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices public Square() { // initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) squareCoords.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(squareCoords); vertexBuffer.position(0); // initialize byte buffer for the draw list ByteBuffer dlb = ByteBuffer.allocateDirect( // (# of coordinate values * 2 bytes per short) drawOrder.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(drawOrder); drawListBuffer.position(0); } }
第三章 绘制图像
你用OpenGL定义完成之后,你应该绘制它们。使用opengl绘制可能比你想的需要写更多的代码。因为api给予了你对于渲染管道更强的更细致的控制。
这篇文章讲述怎么使用opengl 2使用之前已经定义好的形状进行绘制图像。
1. 初始化图形
在你在进行任何绘制操作之前,你必须初始化和加载你需要绘制的图形。
你应该在onSurfaceCreated()方法中初始化它们,这样做更方便渲染和有更好的效率,除了你需要在程序运行的时候改变图形的形状。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class MyGLRenderer implements GLSurfaceView.Renderer { ... private Triangle mTriangle; private Square mSquare; public void onSurfaceCreated(GL10 unused, EGLConfig config) { ... // initialize a triangle mTriangle = new Triangle(); // initialize a square mSquare = new Square(); } ... }
绘制你定义好的图形还是需要很多的代码,因为你需要向图形渲染管道说明很多的细节。特别的,你必须定义下面这些:
Vertex Shader() -- opengl 渲染图形的定点
Fragment Shader -- opengl 用来渲染图像的表面,一般使用颜色或者纹理
Program -- opengl对象,它里面含有你绘制的图像。
你至少需要一个vertex shader去绘制图像的图形,你需要一个fragment shader去渲染图像的颜色。
这些shader必须被编译和加载进入opengl程序中,这个程序会使用它们来绘制图形。
这里有一个例子定义了基本的shader,你可以用它来绘制三角形。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class Triangle { private final String vertexShaderCode = "attribute vec4 vPosition;" + "void main() {" + " gl_Position = vPosition;" + "}"; private final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}"; ... }
为了编译上面的代码,你需要在你的渲染类中创建一个工具方法。
1
2
3
4
5
6
7
8
9public static int loadShader(int type, String shaderCode){ // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; }
到现在,你已经准备好将你增加真正的调用开始绘制你的图形,绘制opengl图像需要你指定几个参数告诉渲染管道你想绘制什么和你怎么绘制它。
因为绘制选项根据图形来说有多种多样,你可以让你的图形类包含自己的绘制方法。
在你的图形类中创建一个绘制的方法,下面的代码,设置了vertex shader的位置会fragment shader的颜色,然后执行绘制函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24private int mPositionHandle; private int mColorHandle; private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX; private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex public void draw() { // Add program to OpenGL ES environment GLES20.glUseProgram(mProgram); // get handle to vertex shader's vPosition member mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // Enable a handle to the triangle vertices GLES20.glEnableVertexAttribArray(mPositionHandle); // Prepare the triangle coordinate data GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); // get handle to fragment shader's vColor member mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // Set color for drawing the triangle GLES20.glUniform4fv(mColorHandle, 1, color, 0); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable vertex array GLES20.glDisableVertexAttribArray(mPositionHandle); }
还有三个小节做不出来,opengl技术有点难度,所以还是日后需要的时候再做打算,赶紧完成现在的部分才是真理。
小面几个小节的标题标注上
只是将背景设置成绿色成功,后面还有很多的内容都没有做。
加油
Applying Projection and Camera
Adding Motion
Responding to Touch Events
最后
以上就是孤独火龙果最近收集整理的关于Android开发培训(08)--使用openGL ES作图的全部内容,更多相关Android开发培训(08)--使用openGL内容请搜索靠谱客的其他文章。
发表评论 取消回复