OpenGL

什么是 OpenGL?

  OpenGL 是个专业的3D程序接口,是一个功能强大,调用方便的底层3D图形库。

  android.opengl包提供了 OpenGL 系统和 Android GUI 系统之间的联系。

最基本的几个类:

  1. android.opengl.GLSurfaceView

    使用Activity来显示GLSurfaceView组件。通过setContentView(GLSurfaceView);

  2. android.opengl.GLSurfaceView.Renderer

    渲染图层的通过GLSurfaceView.setRendere(GLSurfaceView.Renderer);

abstract void onDrawFrame(GL10 gl) //绘制GLSurfaceView的当前帧 
abstract void onSurfaceCreated(GL10 gl, EGLConfig config) //当GLSurfaceView被创建时回调该方法。
abstract void onSurfaceChanged(GL10 gl, int width, int height) //当GLSurfaceView的大小改变的时回调该方法。

  3. javax.microedition.khronos.opengles.GL10

     Graphics Library后面的数字,很简单GLES20是2.0用的,GL10是1.0用的。

  4. java.nio.ByteBuffer

    byte 数据缓冲区,Java 是大端字节序(BigEdian),而 OpenGL 所需要的数据是小端字节序(LittleEdian)。

    所以,我们在将 Java 的缓冲区转化为 OpenGL 可用的缓冲区时需要作一些工作。

    不管我们的数据是整型的还是浮点型的,为了完成 BigEdian 到 LittleEdian 的转换,我们都首先需要一个 ByteBuffer

onSurfaceCreated:

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // TODO Auto-generated method stub
        // 启用阴影平滑
        gl.glShadeModel(GL10.GL_SMOOTH);
        // 黑色背景
        gl.glClearColor(0, 0, 0, 0);
        // 设置深度缓存
        gl.glClearDepthf(122.0f);
        // 启用深度测试
        gl.glEnable(GL10.GL_DEPTH_TEST);
        // 所作深度测试的类型
        gl.glDepthFunc(GL10.GL_LEQUAL);
        // 告诉系统对透视进行修正
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
    }

glHint :用于告诉 OpenGL 我们希望进行最好的透视修正,这会轻微地影响性能,但会使得透视图更好看。

glClearColor:设置清除屏幕时所用的颜色,色彩值的范围从 0.0f~1.0f 大小从暗到这的过程。也可以用一下代码。

  *gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT); // 清楚屏幕和深度缓存

glShadeModel:用于启用阴影平滑度。阴影平滑通过多边形精细地混合色彩,并对外部光进行平滑。

glDepthFunc: 为将深度缓存设想为屏幕后面的层,它不断地对物体进入屏幕内部的深度进行跟踪。

glEnable: 启用深度测试。

onSurfaceChanged:

public void onSurfaceChanged(GL10 gl, int width, int height) {
        // TODO Auto-generated method stub

        float ratio = (float) width / height;
        // 设置OpenGL场景的大小
        gl.glViewport(0, 0, width, height);
        // 设置投影矩阵
        gl.glMatrixMode(GL10.GL_PROJECTION);
        // 重置投影矩阵
        gl.glLoadIdentity();
        // 设置视口的大小
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
        // 选择模型观察矩阵
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        // 重置模型观察矩阵
        gl.glLoadIdentity();

    }

 添加透视图:

我们需要将一个坐标系和我们的GLSurfaceView里的Surface做一个映射关系。

glMatrixMode(GL10.GL_PROJECTION); 是说我们现在改变的是坐标系与Surface的映射关系(投影矩阵)。

gl.glLoadIdentity(); 是将以前的改变都清掉(之前对投影矩阵的任何改变)。

glFrustumf (float left, float right, float bottom, float top, float zNear, float zFar) 它实现了Surface和坐标系之间的映射关系。它是以透视投影的方式来进行映射的。

透视投影的意思见下图:

glMatrixMode函数的选项(参数)有后面三种:

  • GL_PROJECTION,是投影的意思,就是要对投影相关进行操作,也就是把物体投影到一个平面上,就像我们照相一样,把3维物体投到2维的平面上。这样,接下来的语句可以是跟透视相关的函数,比如glFrustum()或gluPerspective();
  • GL_MODELVIEW,是对模型视景的操作,接下来的语句描绘一个以模型为基础的适应,这样来设置参数,接下来用到的就是像gluLookAt()这样的函数;
  • GL_TEXTURE,就是对纹理相关进行操作;

顺便说下,OpenGL里面的操作,很多是基于对矩阵的操作的,比如位移,旋转,缩放,所以,
这里其实说的规范一点就是glMatrixMode是用来指定哪一个矩阵是当前矩阵,而它的参数代表要操作的目标:

  • GL_PROJECTION是对投影矩阵操作;
  • GL_MODELVIEW是对模型视景矩阵操作;
  • GL_TEXTURE是对纹理矩阵进行随后的操作;

onDrawFrame:

public void onDrawFrame(GL10 gl) {
        // TODO Auto-generated method stub

        // 清除屏幕和深度缓存
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        // 重置当前的模型观察矩阵
        gl.glLoadIdentity();

        // 左移 1.5 单位,并移入屏幕 6.0
        gl.glTranslatef(-1.5f, 0.0f, -6.0f);
        // 设置旋转
        gl.glRotatef(rotateTri, 0.0f, 1.0f, 0.0f);
        // 设置定点数组
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        // 设置颜色数组
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        ByteBuffer vbb = ByteBuffer.allocateDirect(colors.length * 4);
        vbb.order(ByteOrder.nativeOrder());
        colorBuffer = vbb.asIntBuffer();
        colorBuffer.put(colors);
        colorBuffer.position(0);
        gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer);
        vbb.clear();
        vbb = ByteBuffer.allocateDirect(trigger.length * 4);
        vbb.order(ByteOrder.nativeOrder());
        triggerBuffer = vbb.asIntBuffer();
        triggerBuffer.put(trigger);
        triggerBuffer.position(0);
        // 设置三角形顶点
        gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer);
        // 绘制三角形
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
     //取消颜色数组 gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
// 绘制三角形结束 gl.glFinish(); // 重置当前的模型观察矩阵 gl.glLoadIdentity(); // 左移 1.5 单位,并移入屏幕 6.0 gl.glTranslatef(1.5f, 0.0f, -6.0f); // 设置当前色为蓝色 gl.glColor4f(0.5f, 0.5f, 1.0f, 1.0f); // 设置旋转 gl.glRotatef(rotateQuad, 1.0f, 0.0f, 0.0f); vbb.clear(); vbb = ByteBuffer.allocateDirect(quates.length * 4); vbb.order(ByteOrder.nativeOrder()); quateBuffer = vbb.asIntBuffer(); quateBuffer.put(quates); quateBuffer.position(0); // 设置和绘制正方形 gl.glVertexPointer(3, GL10.GL_FIXED, 0, quateBuffer); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); // 绘制正方形结束 gl.glFinish(); // 取消顶点数组 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); // 改变旋转的角度 rotateTri += 0.5f; rotateQuad -= 0.5f; }

先清空缓存:

// 清除屏幕和深度缓存
 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置当前的模型观察矩阵
gl.glLoadIdentity();

构图:

有关位置,旋转相关就不说明了重要的是为什么使用ByteBuffer:

不管我们的数据是整型的还是浮点型的,为了完成 BigEdian 到 LittleEdian 的转换,我们都首先需要一个 ByteBuffer。我们通过它来得到一个可以改变字节序的缓冲区。

glVertexPointer 设置顶点位置数据时,需要ByteBuffer等,必须是native Buffer 。

对于FloatBuffer不可以直接用FloatBuffer.wrap将float[]数组转为FloatBuffer,会报如下错误 

  “ Must use a native order direct Buffer” 

可以使用如下函数进行转化:

private FloatBuffer floatBufferUtil(float[] arr)
{
    FloatBuffer mBuffer;
    // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
    ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
    // 数组排列用nativeOrder
    qbb.order(ByteOrder.nativeOrder());
    mBuffer = qbb.asFloatBuffer();
    mBuffer.put(arr);
    mBuffer.position(0);
    return mBuffer;
} 

在下一个图形绘制之前需要把颜色数组清空了。

//取消颜色数组
        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
对应的是
// 设置颜色数组 gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

不取消颜色数组那么下一个图与第一个图的颜色是一样。

但是不要忘了不要把顶点数组给清空了,那么第一个图就不显示了。相关代码如下:

// 取消顶点数组
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

对应的

// 设置定点数组
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

设置顶点坐标与取消顶点数组都仅仅是通知GL,颜色也一样。

加载数据:

那么具体的数据当然是数组的形式

gl.glVertexPointer(3, GL10.GL_FIXED, 0, VertexBuffer);
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer);

这两句话分别绑定了顶点数据数组和颜色数据数组。

其中第一个参数表示的是每个点有几个坐标。例如顶点,有 x、y、z值,所以是 3;

而颜色是 r、g、b、a 值,所以是 4。

绘图:

数据有了那么需要画出来。

// 设置和绘制正方形
        gl.glVertexPointer(3, GL10.GL_FIXED, 0, quateBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);

第一个参数指明了画图的类型——三角形(android 似乎只支持画三角形、点、线,不支持画多边形)。

后面两个参数指明,从哪个顶点开始画,画多少个顶点。

还有第二个图开始画的时候必要的时候重置一下

// 重置当前的模型观察矩阵
        gl.glLoadIdentity();

加载surfaceView:

public class AndroidGLDemo extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GLSurfaceView glView = new GLSurfaceView(this);
        AndroidGLDemoRenderer renderer = new AndroidGLDemoRenderer();
        glView.setRenderer(renderer);
        setContentView(glView);
    }
}
原文地址:https://www.cnblogs.com/hongguang-kim/p/5647010.html