android OpenGL ES2.0编程初学

 

[置顶] 一步一步学android OpenGL ES2.0编程(1)

分类: android开发2013-01-23 11:01 10295人阅读 评论(9) 收藏 举报

androidAndroidANDROIDOpenGLopenglopenGLOPENGLOpenglOpenGl

目录(?)[+]

(欢迎大家加入android技术交流QQ群:209796692

 

建立OpenGL ES环境

 

为了能在你的Android应用中使用OpenGLES绘画,你必须创建一个view作为容器。而最直接的方式就是从GLSurfaceViewGLSurfaceView.Renderer分别派生一个类。GLSurfaceView作为OpenGL绘制所在的容器,而实际的绘图动作都是在GLSurfaceView.Renderer里面发生的。

使用GLSurfaceView几乎是整合OpenGLES到你的应用的唯一方式。对于一个全屏或近全屏的graphicsview,它是最好的选择。如果只是在某个小部分显示OpenGLES图形则可以考虑TextureView。当然如果你比较变态,你完全可以使用SurfaceView创建一个OpenGLES view

本教程演示如何完成一个最小实现的OpenGLES2.0应用。

Manifest中声明使用OpenGLES

 

为了能使用OpenGLES 2.0 API,你必须在你的manifest中添加以下声明:

如果你的应用要使用纹理压缩功能,你必须还要声明设备需要支持什么样的压缩格式:

OpenGLES Graphics创建一个Activity

 

这个Activity与其它类型应用的Activity并无不同,要说不同,也仅仅是放到ActivitylayoutView不一样,你需要放入的是一个GLSurfaceView

下面的代码演示了使用GLSurfaceView作为主视图的Acitivity的最少代码实现:

public class OpenGLES20 extends Activity {     private GLSurfaceView mGLView;     @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);         //创建一个GLSurfaceView实例然后设置为activityContentView.        mGLView = new MyGLSurfaceView(this);        setContentView(mGLView);    }}

:OpenGL ES 2.0需要Android2.2 (API Level 8) 及以上版本。

构键一个GLSurfaceView对象

 

GLSurfaceView中其实不需要做太多工作,实际的绘制任务都在GLSurfaceView.Renderer中了。所以GLSurfaceView中的代码也非常少,你甚至可以直接使用GLSurfaceView,但是,然而,别这样做。因为你需要扩展这个类来响应触摸事件啊孩子。

扩展GLSurfaceView的类像这样写就可以了:

class MyGLSurfaceView extends GLSurfaceView {     public MyGLSurfaceView(Context context){        super(context);         //设置RendererGLSurfaceView        setRenderer(new MyRenderer());    }}

当使用OpenGLES 2.0时,你必须在GLSurfaceView构造器中调用另外一个函数,它说明了你将要使用2.0版的API

// 创建一个OpenGL ES 2.0 contextsetEGLContextClientVersion(2);

另一个可以添加的你的GLSurfaceView实现的可选的操作是设置render模式为只在绘制数据发生改变时才绘制view。使用GLSurfaceView.RENDERMODE_WHEN_DIRTY

// 只有在绘制数据改变时才绘制viewsetRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

此设置会阻止绘制GLSurfaceView的帧,直到你调用了requestRender(),这样会非常高效。

构建一个Renderer

 

此类控制向GLSurfaceView的绘制工作。它有三个方法被Android系统调用来计算在GLSurfaceView上画什么以及如何画。

· 

onSurfaceCreated()仅调用一次,用于设置viewOpenGLES环境。

· 

· 

onDrawFrame()每次View被重绘时被调用。

· 

· 

onSurfaceChanged()如果view的几和形状发生变化了就调用,例如当竖屏变为横屏时。

· 

下面是一个OpenGLES renderer的最基本的实现,它仅在GLSurfaceView画了一个灰色的背景:

public class MyGL20Renderer implements GLSurfaceView.Renderer {     public void onSurfaceCreated(GL10 unused, EGLConfig config) {        //设置背景的颜色        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);    }     public void onDrawFrame(GL10 unused) {        // 重绘背景色        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);    }     public void onSurfaceChanged(GL10 unused, int width, int height) {        GLES20.glViewport(0, 0, width, height);    }}

以上就是所有需要做的东西!上面的代码们创建了一个简单的Android应用,它使用OpenGL显示了一个灰色的屏幕。但这段代码并没有做什么有趣的事情,只是为使用OpenGL绘图做好了准备。

:你可以不明白为什么这些方法们都具有一个GL10参数,但你使用的却是OpengGLES 2.0 API们。这其实是为了使Android框架能简单的兼容各OpenGLES版本而做的。

如果你对OpenGLES API很熟悉,你其实现在已经可以开始进行绘图了。然而,如果你熟悉,就继续学习下一章吧。

 

[置顶] 一步一步学android OpenGL ES2.0编程(2)

分类: android开发2013-01-31 14:42 11972人阅读 评论(6) 收藏 举报

androidAndroidANDROIDOpenGLopenglopenGLOPENGLOpenglOpenGl

目录(?)[+]

(欢迎大家加入android技术交流QQ群:209796692

 

定义形状

会定义在OpenGLES view上所绘制的形状,是你创建高端图形应用杰作的第一步。如果你不懂OpenGLES定义图形对象的一些基本知识,使用OpenGLES可能有一点棘手。

本文解释OpenGLES相对于Android设备屏幕的坐标系统、定义一个形状的基础知识、形状的外观、以及如何定义三角形和正方形。

定义一个三角形

 

OpenGLEs允许你使用坐本在三个维度上定义绘制对象。所以,在你可以绘制一个三角形之前,你必须定义它的坐标。在OpenGL中,典型的方式是为坐标定义一个浮点类型的顶点数组。为了最高效,你应把这些坐标都写进一个ByteBuffer,它会被传到OpenGLES图形管线以进行处理。

class Triangle {    private FloatBuffer vertexBuffer;    // 数组中每个顶点的坐标数    static final int COORDS_PER_VERTEX = 3;    static float triangleCoords[] = { // 按逆时针方向顺序:         0.0f,  0.622008459f, 0.0f,   // top        -0.5f, -0.311004243f, 0.0f,   // bottom left         0.5f, -0.311004243f, 0.0f    // bottom right    };    // 设置颜色,分别为red, green, blue alpha (opacity)    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };    public Triangle() {        // 存放形状的坐标,初始化顶点字节缓冲        ByteBuffer bb = ByteBuffer.allocateDirect(                // (坐标数 * 4)float占四字节                triangleCoords.length * 4);        // 设用设备的本点字节序        bb.order(ByteOrder.nativeOrder());        // ByteBuffer创建一个浮点缓冲        vertexBuffer = bb.asFloatBuffer();        // 把坐标们加入FloatBuffer        vertexBuffer.put(triangleCoords);        // 设置buffer,从第一个坐标开始读        vertexBuffer.position(0);    }}

缺省情况下,OpenGLES 假定[0,0,0](X,Y,Z) GLSurfaceView 帧的中心,[1,1,0]是右上角,[-1,-1,0]是左下角。

注意此形状的坐标是按逆时针方向定义的。绘制顺序很重要,因为它定义了哪面是形状的正面,哪面是反面,使用OpenGLES cullface特性,你可以只画正面而不画反面。

定义一个正方形

 

OpenGL中定义正方形是十分容易的,有很多方法能做的,但是典型的做法是使用两个三角形:

1.使用两个三角形画一个正方形

你要为两个三角形都按逆时针方向定义顶点们,并且将这些坐标值们放入一个ByteBuffer中。为了避免分别为两个三角形定义两个坐标数组,我们使用一个绘制列表来告诉OpenGLES图形管线如果画这些顶点们。下面就是这个形状的代码:

class Square {    private FloatBuffer vertexBuffer;    private ShortBuffer drawListBuffer;    // 每个顶点的坐标数    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 }; // 顶点的绘制顺序    public Square() {        // initialize vertex byte buffer for shape coordinates        ByteBuffer bb = ByteBuffer.allocateDirect(        // (坐标数 * 4)                squareCoords.length * 4);        bb.order(ByteOrder.nativeOrder());        vertexBuffer = bb.asFloatBuffer();        vertexBuffer.put(squareCoords);        vertexBuffer.position(0);        // 为绘制列表初始化字节缓冲        ByteBuffer dlb = ByteBuffer.allocateDirect(        // (对应顺序的坐标数 * 2)short2字节                drawOrder.length * 2);        dlb.order(ByteOrder.nativeOrder());        drawListBuffer = dlb.asShortBuffer();        drawListBuffer.put(drawOrder);        drawListBuffer.position(0);    }}

本例让你见识了用OpenGL如何创建更复杂的形状。通常,你都是使用一群小三(三角形)来绘制对象。下一章,你将学会如何将这些形状画到屏幕上。

 

[置顶] 一步一步学android OpenGL ES2.0编程(3)

分类: android开发2013-02-20 10:02 13677人阅读 评论(13) 收藏 举报

OpenGLES

目录(?)[+]

绘制形状

你定义了要绘制的形状后,你就要画它们了。使用OpenGLES 2.0会形状会有一点点复杂,因为API提供了大量的对渲染管线的控制能力。

本文讲解如何绘制你在前文中定义的那些形状们。

初始化形状

 

在你做任何绘制之前,你必须初始化形状然后加载它。除非形状的结构(指原始的坐标们)在执行过程中发生改变,你都应该在你的Renderer的方法onSurfaceCreated()中进行内存和效率方面的初始化工作。

public void onSurfaceCreated(GL10 unused, EGLConfig config) {    ...    // 初始化一个三角形    mTriangle = new Triangle();    // 初始化一个正方形    mSquare = new Square();}

绘制一个形状

 

使用OpenGLES 2.0画一个定义好的形状需要一大坨代码,因为你必须为图形渲染管线提供一大堆信息。典型的,你必须定义以下几个东西:

· 

VertexShader-用于渲染形状的顶点的OpenGLES 图形代码。

· 

· 

FragmentShader-用于渲染形状的外观(颜色或纹理)的OpenGLES 代码。

· 

· 

Program-一个OpenGLES对象,包含了你想要用来绘制一个或多个形状的shader

· 

你至少需要一个vertexshader来绘制一个形状和一个fragmentshader来为形状上色。这些形状必须被编译然后被添加到一个OpenGLES program中,program之后被用来绘制形状。下面是一个展示如何定义一个可以用来绘制形状的基本shader的例子:

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

Shader们包含了OpenGLShading Language (GLSL)代码,必须在使用前编译。要编译这些代码,在你的Renderer类中创建一个工具类方法:

public static int loadShader(int type, String shaderCode){    // 创建一个vertex shader类型(GLES20.GL_VERTEX_SHADER)    // fragment shader类型(GLES20.GL_FRAGMENT_SHADER)    int shader = GLES20.glCreateShader(type);    // 将源码添加到shader并编译之    GLES20.glShaderSource(shader, shaderCode);    GLES20.glCompileShader(shader);    return shader;}

为了绘制你的形状,你必须编译shader代码,添加它们到一个OpenGLES program 对象然后链接这个program。在renderer对象的构造器中做这些事情,从而只需做一次即可。

:编译OpenGLES shader们和链接linkingprogram们是很耗CPU的,所以你应该避免多次做这些事。如果在运行时你不知道shader的内容,你应该只创建一次code然后缓存它们以避免多次创建。

public Triangle() {    ...    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);    mProgram = GLES20.glCreateProgram();             // 创建一个空的OpenGL ES Program    GLES20.glAttachShader(mProgram, vertexShader);   // vertex shader添加到program    GLES20.glAttachShader(mProgram, fragmentShader); // fragment shader添加到program    GLES20.glLinkProgram(mProgram);                  // 创建可执行的 OpenGL ES program}

此时,你已经准备好增加真正的绘制调用了。需要为渲染管线指定很多参数来告诉它你想画什么以及如何画。因为绘制操作因形状而异,让你的形状类包含自己的绘制逻辑是个很好主意。

创建一个draw()方法负责绘制形状。下面的代码设置位置和颜色值到形状的vertexshaderfragmentshader,然后执行绘制功能。

public void draw() {    // program加入OpenGL ES环境中    GLES20.glUseProgram(mProgram);    // 获取指向vertex shader的成员vPosition的 handle    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");    // 启用一个指向三角形的顶点数组的handle    GLES20.glEnableVertexAttribArray(mPositionHandle);    // 准备三角形的坐标数据    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,                                 GLES20.GL_FLOAT, false,                                 vertexStride, vertexBuffer);    // 获取指向fragment shader的成员vColorhandle     mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");    // 设置三角形的颜色    GLES20.glUniform4fv(mColorHandle, 1, color, 0);    // 画三角形    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);    // 禁用指向三角形的顶点数组    GLES20.glDisableVertexAttribArray(mPositionHandle);}

一旦你完成这些代码,画这个对象只需要在RendereronDrawFrame()调用draw()方法。当你运行应用时,它应该看起来这样:

1.没有投射和视口时画三角形

此例子中的代码还有很多问题。首先,它不会给人留下印像。其次,三角形会在你从竖屏变为横屏时被压扁。三角形变形的原因是其顶点们没有跟据屏幕的宽高比进行修正。你可以使用投射和视口更正这个问题。那在下一讲了。

[置顶] 一步一步学android OpenGL ES2.0编程(4)

分类: android开发2013-03-01 06:21 10657人阅读 评论(5) 收藏 举报

androidOpenGLES

目录(?)[+]

应用投影和相机视口

OpenGLES环境中,投影和相机视口使你绘制的对象以更接近物理对象的样子显示。这是通过对坐标精确的数学变换实现的。

· 

投影-这种变换跟据所在GLSurfaceView的宽和高调整对象的坐标。如果没有此变换,对象会被不规则的视口扭曲。投射变换一般只需要在OpenGLview创建或发生变化时调用,代码写在rendereronSurfaceChanged()方法中。

· 

· 

相机视口-此变换基于一个虚拟相机的位置调整对象的坐标。注意OpenGLES并没有定义一个真的相机对象,而是提供了一些工具方法变换绘制对象的显示来模拟一个相机。一个相机视口的变换可能只在创建GLSurfaceView时调用,或跟据用户动作动态调用。

· 

本文讲解了如何创建一个投影和一个相机视口然后应用到GLSurfaceView的形状绘制过程。

定义一个投影

 

投影变换的数据是在GLSurfaceView.Renderer 类的 onSurfaceChanged() 方法中计算。下面的例子跟据GLSurfaceView 的宽和高,使用Matrix.frustumM()方法计算出了一个投影变换Matrix

@Overridepublic void onSurfaceChanged(GL10 unused, int width, int height) {    GLES20.glViewport(0, 0, width, height);    float ratio = (float) width / height;    // 此投影矩阵在onDrawFrame()中将应用到对象的坐标    Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);}

以下代码产生了一个投影矩阵mProjMatrix ,你可以把它在 onDrawFrame() 方法中与一个相机视口变换结合。

: 只对你的对象应用一个投影变换一般会导制什么也看不到。通常,你必须也对其应用一个视口变换才能看到东西。

定义一个相机视口

 

再定义一个相机视口变换以使对绘制对象的变换处理变得完整。在下面的例子中,使用方法Matrix.setLookAtM()计算相机视口变换,然后结合前面所计算的投影矩阵。结合后的变换矩阵之后传给要绘制的对象。

@Overridepublic void onDrawFrame(GL10 unused) {    ...    // 设置相机的位置(视口矩阵)    Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);    // 计算投影和视口变换    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);    // 绘制形状    mTriangle.draw(mMVPMatrix);}

应用投影和相机视口变换

 

为了使用前面的合并后的投影和相机视口变换矩阵,修改你的图形对象的方法draw(),接受结果矩阵并应用到你的形状上:

public void draw(float[] mvpMatrix) { // 传递计算出的变换矩阵    ...    // 获得形状的变换矩阵的handle    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");    // 应用投影和视口变换    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);    // 绘制三角形    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);    ...}

一旦你正确的计算和应用了投影和视口变换,你的图像将出现在正确的位置,看起来像下面这样:

1.应用了投影和视口变换后绘制的三角形

现在你拥有了一个正确显示你的形状的应用了,是让你的图形动起来的时候了...嘿嘿...

 

[置顶] 一步一步学android OpenGL ES2.0编程(5)

分类: android开发2013-03-08 14:58 7865人阅读 评论(1) 收藏 举报

OpenGLES

目录(?)[+]

添加运动

在屏幕上绘制是OpenGL的基础能力,但是你也可以用其它的Android图形框架类来做,包括CanvasDrawable。 但是OpenGL ES提供了另外的能力,可以在三维上移动和变换对象。总之它能创造很牛B的用户体验。在本文中,你将学会如何使用OpenGL ES为形状添加旋转功能。

转动一个形状

使用OpenGL ES 2.0旋转一个对象也是十分简单地。你创建另外一个变换矩阵(一个旋转矩阵)然后把它合并到你的投影和相机视口变换矩阵就行了:

[java] view plaincopy

1. private float[] mRotationMatrix = new float[16];  

2. public void onDrawFrame(GL10 gl) {  

3.     ...  

4.     // 为三角形创建一个旋转变换  

5.     long time = SystemClock.uptimeMillis() % 4000L;  

6.     float angle = 0.090f * ((int) time);  

7.     Matrix.setRotateM(mRotationMatrix, 0, mAngle, 00, -1.0f);  

8.   

9.     // 把旋转矩阵合并到投影和相机矩阵  

10.     Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);  

11.   

12.     // 画三角形  

13.     mTriangle.draw(mMVPMatrix);  

14. }  

如果你的三角形在此新后转不起来,则要查看是否把GLSurfaceView.RENDERMODE_WHEN_DIRTY 设置注释了,下面马上就讲到。

启用持续渲染

到现在,你应在代码中注释掉设置只在数据改变时才渲染的代码,否则,OpenGL 只有转一次然后等待直到GLSurfaceView 的包含者调用requestRender()

[java] view plaincopy

1. public MyGLSurfaceView(Context context) {  

2.     ...  

3.     // Render the view only when there is a change in the drawing data  

4.     //setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // 注释掉以自动旋转  

5. }  

除非你不让对象与用户有交互,否则启用这个设置是一个好做法。要准备解除这句的注释了,因为下一讲会用到它。

[置顶] 一步一步学android OpenGL ES2.0编程(6 大结局)

分类: android开发 jni OpenGLES2013-03-14 16:04 10060人阅读 评论(3) 收藏 举报

目录(?)[+]

响应触摸事件

使你的OpenGL ES应用能响应触摸的关键是扩展你实现的GLSurfaceView 代码,覆写onTouchEvent() 方法来监听触摸事件。 

本文向你展示如何监听用户的触摸事件以使用户可以旋转某个OpenGL ES对象。

设置一个触摸监听器

为了使你的OpenGL Es应用响应触摸事件,你必须在你的GLSurfaceView 类中实现onTouchEvent()事件。下面的例子演示了如何监听MotionEvent.ACTION_MOVE 事件然后把它们转换成一个形状的旋转角度。

[java] view plaincopy

1. @Override  

2. public boolean onTouchEvent(MotionEvent e) {  

3.     // MotionEvent带有从触摸屏幕来的输入的详细信息以及其它输入控制  

4.     // 此处,你只需对触摸位置的改变感兴趣即可。  

5.   

6.     float x = e.getX();  

7.     float y = e.getY();  

8.   

9.     switch (e.getAction()) {  

10.         case MotionEvent.ACTION_MOVE:  

11.   

12.             float dx = x - mPreviousX;  

13.             float dy = y - mPreviousY;  

14.   

15.             // reverse direction of rotation above the mid-line  

16.             if (y > getHeight() / 2) {  

17.               dx = dx * -1 ;  

18.             }  

19.   

20.             // reverse direction of rotation to left of the mid-line  

21.             if (x < getWidth() / 2) {  

22.               dy = dy * -1 ;  

23.             }  

24.   

25.             mRenderer.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR;  // = 180.0f / 320  

26.             requestRender();  

27.     }  

28.   

29.     mPreviousX = x;  

30.     mPreviousY = y;  

31.     return true;  

32. }  

注意在计算完旋转角度之后,本方法调用requestRender() 来告诉renderer要渲染帧了。这样做是很高效的,因为在没有发生旋转时不需要重画帧。然而,在你没有要求只在数据发生改变才重画之前,还不能达到最高效,即别忘了解除这一句的注释:

[java] view plaincopy

1. public MyGLSurfaceView(Context context) {  

2.     ...  

3.     // Render the view only when there is a change in the drawing data  

4.     setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);  

5. }  

曝露出旋转角度

上面的例子要求你向其它类曝露出你的旋转角度,所以你要为你的renderer添加一个public成员。既然renderer的代码运行于主界面之外的单独线程中,你必须声明这个公开变量为volatile. 。下面的代码就是这样做的:

[java] view plaincopy

1. public class MyGLRenderer implements GLSurfaceView.Renderer {  

2.     ...  

3.     public volatile float mAngle;  

4. }  

应用旋转

要应用触摸所产生的旋转, 注释掉产生角度的代码并且添加mAngle,它包活了触摸所产生的角度:

[java] view plaincopy

1. public void onDrawFrame(GL10 gl) {  

2.     ...  

3.     // Create a rotation for the triangle  

4.     // long time = SystemClock.uptimeMillis() % 4000L;  

5.     // float angle = 0.090f * ((int) time);  

6.     Matrix.setRotateM(mRotationMatrix, 0, mAngle, 00, -1.0f);  

7.   

8.     // 合并旋转矩阵到投影和相机视口矩阵  

9.     Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);  

10.   

11.     // 画一个角度  

12.     mTriangle.draw(mMVPMatrix);  

13. }  

当你完成了上述的几步,运行程序然后在陪同幕上拖动你的手指头,你会看到下面这样:

 Figure 1. 跟据触摸输入的转动的三角形(圈圈显示了触摸的位置)

 

你可能感兴趣的:(OpenGL,ES)