zoukankan      html  css  js  c++  java
  • 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);
        }
    }
  • 相关阅读:
    HAOI2018 奇怪的背包
    HAOI2018 苹果树
    骑士游戏
    飞飞侠
    奶牛排队
    寻找道路
    [USACO08JAN]牛大赛Cow Contest
    灾后重建
    [USACO09DEC]牛收费路径Cow Toll Paths
    萌萌哒
  • 原文地址:https://www.cnblogs.com/hongguang-kim/p/5647010.html
Copyright © 2011-2022 走看看