Android OpenGL ES3.0 (1)

本文详细解析了OpenGLES3.0的基本原理和绘图流程,包括3D模型构建、顶点和片元着色器的编写、数据加载及图形变换等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

\最近研究了一下OpenGL ES3.0,参照了网上很多的示例,在这里总结一部分经验

1)数据

OpenGL ES3.0 中的3D数据模型是由无数个三角形组成的。例如下面这段数据,就是一个绘制立方体的模型。float数组中每个三个元素代表一个顶点(x,y, z),没三个顶点(九个元素)代表一个三角形。opengles 的数据特点是在-1到1的范围表示整个空间。下面的数据就是用来绘制一个长宽高都占半个空间的立方体。

 

  float vertexes[] = {
                -0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, -0.5f,
                0.5f, 0.5f, -0.5f,
                0.5f, 0.5f, -0.5f,
                -0.5f, 0.5f, -0.5f,
                -0.5f, -0.5f, -0.5f,

                -0.5f, -0.5f, 0.5f,
                0.5f, -0.5f, 0.5f,
                0.5f, 0.5f, 0.5f,
                0.5f, 0.5f, 0.5f,
                -0.5f, 0.5f, 0.5f,
                -0.5f, -0.5f, 0.5f,

                -0.5f, 0.5f, 0.5f,
                -0.5f, 0.5f, -0.5f,
                -0.5f, -0.5f, -0.5f,
                -0.5f, -0.5f, -0.5f,
                -0.5f, -0.5f, 0.5f,
                -0.5f, 0.5f, 0.5f,

                0.5f, 0.5f, 0.5f,
                0.5f, 0.5f, -0.5f,
                0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, 0.5f,
                0.5f, 0.5f, 0.5f,

                -0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, 0.5f,
                0.5f, -0.5f, 0.5f,
                -0.5f, -0.5f, 0.5f,
                -0.5f, -0.5f, -0.5f,

                -0.5f, 0.5f, -0.5f,
                0.5f, 0.5f, -0.5f,
                0.5f, 0.5f, 0.5f,
                0.5f, 0.5f, 0.5f,
                -0.5f, 0.5f, 0.5f,
                -0.5f, 0.5f, -0.5f,
        };
       float[] colors = {
            
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,

        
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,

           
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,

          
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,

           
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,

          
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f
        };

这里先把数据加载到 FloatBuffer 中

 vertexBuffer=loadData(vertexes);
        colorBuffer=loadData(colors);
   private FloatBuffer loadData(float [] arr){
        ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
        bb.order(ByteOrder.nativeOrder());
        FloatBuffer  floatBuffer = bb.asFloatBuffer();
        floatBuffer.put(arr);
        floatBuffer.position(0);
        return floatBuffer;
    }

那么opengl es 是怎么获取这些数据的呢,就是用顶点着色器,下面是一段顶点着色器的源码,3.0必须指定版本好,在Android api里面是直接把顶点着色器的源码以String的类型读取,可以放在文件中,也可以放在String对象里。下面的这段源码 layout 关键字后面的括号中的location 是在载入数据的时候指定的 ,in 就是指数据是输入数据。 vec3 是一种类型,可以理解为矢量数据,用来存放顶点数据。out 是指输出,将顶点着色器的颜色输出给片源着色器。片源着色器把顶点着色器中传递进来的颜色输出。

    private final String vertexShaderCode ="//指定版本号\n" +
            "#version 300 es\n" +
            "uniform mat4 transform;" +
            "layout (location = 0) in vec3 aPos;\n" +
            "layout (location = 1) in vec4 aColor;\n" +
            "out vec4 color;\n" +
            "void main()\n" +
            "{\n" +
            "    // gl_Position (内置函数) 赋值位置\n" +
            "    gl_Position = transform*vec4(aPos, 1.0);\n" +
            "    color = aColor;\n" +
            "}";

 

 private final String fragmentShaderCode =
            "#version 300 es\n" +
                    "precision mediump float;\n" +
                    "\n" +
                    "out vec4 fragColor;\n" +
                    "in vec4 color;\n" +
                    "void main()\n" +
                    "{\n" +
                    "    fragColor = color;\n" +
                    "}" ;

那么这些数据如何读取,就使用下面的代码,mProgram是一个int类型,相当于顶点着色器的位置指示。显示载入着色器的源码,这里面GL_VERTEX_SHADER 是顶点着色器用于绘制顶点, GL_FRAGMENT_SHADER是 片源着色器用于绘制颜色。

然后是一个着色器的程序,如果不为0级说明创建成功了,之后的代码是连接着色器。连接后会有一个状态数组返回,第一个用元素就是状态。载入着色器的程序也类似,先调用载入源码,之后编译源码,最后看返回的数据,如果编译成功就不为0.这里的返回数据就用来传递给着色器程序。(vertexShader 和framentShader中存的int值) 。最后要配置使程序相应指定的数据,第一个参数就是之前在着色器中指定的location (GLES30.glVertexAttribPointer(0, VERTEX_POSITION_SIZE, GLES30.GL_FLOAT, false, 0, vertexBuffer); GLES30.glEnableVertexAttribArray(0);) 代码里从程序中读取了两套,一个是用于绘制顶点,一个是用于绘制颜色, 就是对应顶点着色器中 in  关键字修饰的 vec 。  这里准备工作就做好了。下面就是绘制。

 

    private void initProgram(){
        int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER,vertexShaderCode);
        int fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER,fragmentShaderCode);
        mProgram = GLES30.glCreateProgram();
        if(mProgram != 0 ){
            GLES30.glAttachShader(mProgram,vertexShader);
            GLES30.glAttachShader(mProgram,fragmentShader);
            GLES30.glLinkProgram(mProgram);
            int[] linkStatus = new int[1];
            GLES30.glGetProgramiv(mProgram, GLES30.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES30.GL_TRUE) {
                Log.e(TAG, "连接着色器失败 ");
                Log.e(TAG, GLES30.glGetProgramInfoLog(mProgram));
                GLES30.glDeleteProgram(mProgram);
                mProgram = 0;
            }
        }
        GLES30.glVertexAttribPointer(0, VERTEX_POSITION_SIZE, GLES30.GL_FLOAT, false, 0, vertexBuffer);
        GLES30.glEnableVertexAttribArray(0);

        GLES30.glVertexAttribPointer(1, VERTEX_COLOR_SIZE, GLES30.GL_FLOAT, false, 0, colorBuffer);
        GLES30.glEnableVertexAttribArray(1);
    }
  private int loadShader(int type,String shaderCode){

        int shader = GLES30.glCreateShader(type);
        if(shader != 0 ){
            GLES30.glShaderSource(shader,shaderCode);
            GLES30.glCompileShader(shader);
            int[] compiled = new int[1];
            GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0) {
                GLES30.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

绘制的代码,这里的transform  是用于图形变换的,在顶点着色器的源码中可以看到他是一个mat4类型,他是一个矩阵,网上有大量的文章说明了opengl es中利用矩阵运算,进行图形转换的示例,在顶点着色器源码中也可以看到,是做了矩阵的相乘。这里opengl es的api中也提供了相应生成变换矩阵的方法。 setIdentityM  就相当于声明了一个矩阵,这个矩阵的需要我们提前在内存中创建,它的长度是固定的。然后 调用 gluniformmatrix4fv就相当于把 modelMatrix的数据传递给 顶点着色器中与顶点数据相乘的transform 。

 

    int transform = GLES30.glGetUniformLocation(mProgram, "transform");
        GLES30.glUniformMatrix4fv(transform, 1, false, modelMatrix, 0);

        GLES30.glClearColor(0.2f, 0.1f, 0.3f, 1.0f);
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);


        GLES30.glUseProgram(mProgram);
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 36);
        modelMatrix =new float[16];

        Matrix.setIdentityM(modelMatrix, 0);

        Matrix.rotateM(modelMatrix, 0, 30, 0f, 1f, 0f);
//
        Matrix.rotateM(modelMatrix, 0, 30, 0f, 0f, 1f);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值