Android OpenGL 开始篇

我常给一些人的建议:如果条件不错,就不要来做程序员了,因为这不是人干的事!程序员睡觉的时候也是清洁工人开始扫马路的时候!

废话不多说,自己也是作为学习笔记而已,也是督促自己,因为如果仅仅运行一个例子很简单.自己研究这个当然也是需要应用到一定背景下的.

android APP如果需要使用opengl制图,如果在java层实现,一般是GLSurfaceView来显示出opengl制图,GLSurfaceView的特点:

1.管理一个平面,这个平面是一个特殊的内存块,它可以和android视图系统混合.
2.管理一个EGL显示,它能够让OpenGL渲染到一个平面.
3.接受一个用户提供的实际显示的Renderer对象.
4.使用一个专用线程去渲染从而和UI线程解耦.
5.支持on-demand  和连续的渲染.
6.可选的包,追踪 和/或者错误检查这个渲染器的OpenGL调用.

一个GLSurfaceView  一定要注意,这个activity的paused  和resumed 两种状态.当这个activity处于pauses  状态时,GLSurfaceView  客户端需要去调用 onPause() 方法,当activity处于resumed 状态时,GLSurfaceView  客户端也需要去调用 onResume() 方法.

下面新建一个使用opengl的工程,在android studio中.

<1> : 新建一个DurianOpenGL1工程,下面直接贴代码:

package org.durian.durianopengl1;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

import org.durian.durianopengl1.gl.Durian3DRender;
import org.durian.durianopengl1.gl.DurianGLRender;
import org.durian.durianopengl1.gl.DurianPhotoRender;
import org.durian.durianopengl1.gl.DurianRotateRender;
import org.durian.durianopengl1.gl.DurianTextureRender;
import org.durian.durianopengl1.input.DurianInputSurfaceView;
import org.durian.durianopengl1.texturefilter.DurianTextureFilterSurfaceView;


public class DurianMainActivity extends Activity {

    private GLSurfaceView glview;
    private DurianInputSurfaceView glinputview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        glview=new GLSurfaceView(this);
        //glinputview=new DurianInputSurfaceView(this);
        //glview.setRenderer(new DurianPhotoRender(this)/*new DurianTextureRender(this)*//*new Durian3DRender(this)*//*new DurianRotateRender(this)*//*new DurianGLRender(this)*/);
        glview.setRenderer(new DurianGLRender(this));
        setContentView(/*new DurianTextureFilterSurfaceView(this)*//*glinputview*/glview/*R.layout.activity_durian_main*/);
    }

    @Override
    protected void onResume() {
        super.onResume();
        glview.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        glview.onPause();
    }
}

注意上面Activity的onResume和onPause两个周期,GLSurfaceView也需要根据Activity做相应的调整.

下面是渲染器Renderer:

package org.durian.durianopengl1.gl;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;

import org.durian.durianopengl1.draw2d.Square;
import org.durian.durianopengl1.draw2d.Triangle;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by Administrator on 2016/4/11.
 */
public class DurianGLRender implements GLSurfaceView.Renderer {

    private Context mContext;

    private Triangle triangle;
    private Square square;

    private org.durian.durianopengl1.draw2d.color.Triangle ctriangle;
    private org.durian.durianopengl1.draw2d.color.Square csquare;

    public DurianGLRender(Context context){
        mContext=context;

        triangle=new Triangle();
        square=new Square();

        //ctriangle=new org.durian.durianopengl1.draw2d.color.Triangle();
        //csquare=new org.durian.durianopengl1.draw2d.color.Square();

    }
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        gl.glClearColor(0.0f,0.0f,0.0f,1.0f);
        gl.glClearDepthf(1.0f);
        gl.glEnable(GL10.GL_DEPTH_TEST);
        gl.glDepthFunc(GL10.GL_LEQUAL);
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_NICEST);
        gl.glShadeModel(GL10.GL_SMOOTH);
        gl.glDisable(GL10.GL_DITHER);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        if(height==0){
            height=1;
        }
        float aspect=(float)width/height;

        gl.glViewport(0,0,width,height);

        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        GLU.gluPerspective(gl,45,aspect,0.1f,100.0f);

        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();

    }

    @Override
    public void onDrawFrame(GL10 gl) {

        gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

        gl.glLoadIdentity();
        gl.glTranslatef(-3.0f,0.0f,-6.0f);
        triangle.draw(gl);

        gl.glTranslatef(3.0f,0.0f,1.0f);
        square.draw(gl);

        gl.glTranslatef(-5.0f,0.0f,-6.0f);
        //ctriangle.draw(gl);

        gl.glTranslatef(5.0f,0.0f,1.0f);
        //csquare.draw(gl);

    }

}


这个类中其中有一个Triangle和Square的类,一个是绘制边型,一个是方形的.

实现Renderer类就要实现它的三个基本方法.

@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)

这个方法在Renderer创建的时候就会运行,一般在生命周期中只会运行一次.

这个方法一般是做一些初始化的工作.

gl.glClearColor(0f, 0f, 0f, 0f);

设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。当它用来清除屏幕的时候,我们不用关心第四个数字。现在让它为0.0f。我会用另一个教程来解释这个参数。
通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。希望您在学校里学过这些。因此,当您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您将用亮蓝色来清除屏幕。如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,您将使用中红色来清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。要得到白色背景,您应该将所有的颜色设成最亮(1.0f)。要黑色背景的话,您该将所有的颜色设为最暗(0.0f)。我们在这里设置屏幕为黑色.

gl.glClearDepthf(1.0f);
        gl.glEnable(GL10.GL_DEPTH_TEST);
        gl.glDepthFunc(GL10.GL_LEQUAL);
这三行是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分.

gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_NICEST);
这里告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点.
gl.glShadeModel(GL10.GL_SMOOTH);

启用smooth shading(阴影平滑).阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑,在以后的课程中会看到他的效果.

gl.glDisable(GL10.GL_DITHER);

关闭服务器端GL功能,在GL中很多都是一对一对的,比如这个的另一个gl.glEnable(...).


@Override
    public void onSurfaceChanged(GL10 gl, int width, int height) 

当发生绘制变化的时候运行.比如横屏切换到竖屏.

gl.glViewport(0,0,width,height);
此方法用来设定可见区域,即OpenGL应把渲染之后的图形绘制在窗体的哪个部位,当视见区域是整个窗体时,OpenGL将把渲染结果绘制到整个窗口.

gl.glMatrixMode(GL10.GL_PROJECTION);
这个函数其实就是对接下来要做什么进行一下声明,也就是在要做下一步之前告诉计算机我要对“什么”进行操作了,这个“什么”在glMatrixMode的“()”里的选项(参数)有3种模式: GL_PROJECTION 投影, GL_MODELVIEW 模型视图, GL_TEXTURE 纹理.
gl.glLoadIdentity();

重设视图模型变换 , 用于观测创建的物体.

GLU.gluPerspective(gl,45,aspect,0.1f,100.0f);
参考:http://blog.chinaunix.net/uid-24448954-id-3059473.html


@Override
    public void onDrawFrame(GL10 gl)
这个才是真正绘制图形的地方,类似于自定义的View的onDraw方法.
gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

将缓存清除为预先的设置值.

gl.glTranslatef(-3.0f,0.0f,-6.0f);

这个方法是位移操作,三个参数分别是x,y,z左边,(0,0,0)通过前面的glLoadIdentity将被设置在平面中间位置,z的正方向是从屏幕指向上(或者屏幕外).


下面的实现边形的类:

package org.durian.durianopengl1.draw2d;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

/**
 * Created by Administrator on 2016/4/11.
 */
public class Triangle {

    private FloatBuffer vertexBuffer;
    private ByteBuffer indexBuffer;

    private float[] vertices={
        0.0f,1.0f,0.0f,
            -1.0f,-1.0f,0.0f,
            1.0f,-1.0f,0.0f
    };
    private byte[] indices={0,1,2};

    public Triangle(){

        ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        vertexBuffer=vbb.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);

        indexBuffer=ByteBuffer.allocateDirect(indices.length);
        indexBuffer.put(indices);
        indexBuffer.position(0);

    }

    public void draw(GL10 gl){

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);

        gl.glDrawElements(GL10.GL_TRIANGLES,indices.length,GL10.GL_UNSIGNED_BYTE,indexBuffer);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

    }

}

边是由两点一线组成,上面是三边形,那么就有三个坐标,由于opengl是空间坐标,所以坐标是由x,y,z三项组成:

private float[] vertices={
        0.0f,1.0f,0.0f,
            -1.0f,-1.0f,0.0f,
            1.0f,-1.0f,0.0f
    };

上面每一行分别对应是x,y,z

构造方法体中:

ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        vertexBuffer=vbb.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);
opengl对于顶点操作实际是在native层完成的,内存分配也是在native完成的,所以上面是完成顶点的数据的内存分配
gl.glEnableClientState
gl.glDisableClientState

这两个一般是成对出现,可以控制管道(pipeline)开关
下图显示了OpenGL ES 1.x 固定管道的结构图:

Android OpenGL 开始篇_第1张图片

管道“工序”大致可以分为 Transformation Stage 和 Rasterization Stage两大步。

  OpenGL ES 支持的基本图形为 点Point, 线Line, 和三角形Triangle ,其它所有复制图形都是通过这几种基本几何图形组合而成。

  在发出绘图指令后,会对顶点(Vertices)数组进行指定的坐标变换或光照处理。

  顶点处理完成后,通过Rasterizer 来生成像素信息,称为”Fragments“ 。

  对于Fragment 在经过Texture Processing, Color Sum ,Fog 等处理并将最终处理结果存放在内存中(称为FrameBuffer)。

  OpenGL 2.0可以通过编程来修改蓝色的步骤,称为Programmable Shader.

以上管道中工序可以通过设置来打开或关闭某些功能(比如无需雾化Fog处理),并可以为某个工序设置参数,比如设置Vertext Array.

gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);

第一个参数代表每个点或者顶点用几个坐标表示,本例中使用三个xyz。你也可以指定两个只用xy,这样话,z就为0.需要注意的是,第一个参数并不代表有几个点,如果你想要使用20个点来代表一个三角形,这里你需要传入的不是20而是2或3.
第二个参数表示坐标值应当被解析为float类型(这样OpenGL就可以知道每个值占用几位).

第三个参数可以称为“步长”,代表每个点之间有几位分割。本例中,0代表一个点挨着一个点,有时候你可能会在点的后面定义颜色,这时,你应该指出每个颜色占用的位长,以便OpenGL在解析时跳过这段长度.

gl.glDrawElements(GL10.GL_TRIANGLES,indices.length,GL10.GL_UNSIGNED_BYTE,indexBuffer);


第一个参数代表你想要画的几何图形(GL_TRIANGLE_STRIP代表一个三角strip)。其他可能的选项包括GL_POINTS,GL_LINE_STRIP,GL_LINES,GL_LINE_LOOP,GL_TRIANGLES和G:_TRIANGLE_FAN.

第二个参数:glDrawElements其它的参数可以让你重用预定义的带你。比如说,一个四边形包括四个点,每一个四边形可以用两个三角形组合而来。如果你想使用两个三角形组合成一个四边形,你有必要去定义6个点吗?当然不是,你只需要定义四个点,然后引用6次画出两个三角形就可以了,这种处理被称为“indexing into the point buffer”如下所示:
Points:(p1,p2,p3,p4)
Draw indices(p1,p2,p3,   p2,p3,p4)
知道了这些,glDrawElements的第二个参数就好理解了,它代表了index buffer中有几个坐标标示(可以理解为点).

第三个参数代表index 数组中值的类型,是GL_UNSIGNED_SHORT还是GL_UNSIGNED_BYTE。
最后一个参数引用index buffer.

OK,基本操作和解释已经相当清楚了.

另外一个方形类就不过多解释了;

package org.durian.durianopengl1.draw2d;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

/**
 * Created by Administrator on 2016/4/11.
 */
public class Square {

    private FloatBuffer vertexBuffer;

    private float[] vertices={
            -1.0f,-1.0f,0.0f,
            1.0f,-1.0f,0.0f,
            -1.0f,1.0f,0.0f,
            1.0f,1.0f,0.0f
    };

    public Square(){

        ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        vertexBuffer=vbb.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);

    }

    public void draw(GL10 gl){

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);

        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,vertices.length/3);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

    }

}




















你可能感兴趣的:(Android,OPenGL)