OpenGL ES (15): 重点 -- 绘制正方形-绘制圆形

1.


话不多说,直接看代码!

你可以以顶点1234的顺序画,坐标就是如下:

private final float[] sPos={
            -1.0f,1.0f,    //左上角
            -1.0f,-1.0f,   //左下角
            1.0f,-1.0f ,    //右下角
            1.0f,1.0f     //右上角
    };

你也可以以1243的顺序画,坐标就设置如下:

private final float[] sPos={
            -1.0f,1.0f,    //左上角
            -1.0f,-1.0f,   //左下角
            1.0f,1.0f,     //右上角
            1.0f,-1.0f     //右下角
    };

代码如下:(顶点以1234的顺序)

public class Square implements GLSurfaceView.Renderer {
    String vertexShaderCode = "attribute vec4 vPosition;\n" +
            "uniform mat4 vMatrix;\n" +
            "void main() {\n" +
            "  gl_Position = vMatrix*vPosition;\n" +
            "}" ;

    String fragmentShaderCode = "precision mediump float;\n" +
            " uniform vec4 vColor;\n" +
            " void main() {\n" +
            "     gl_FragColor = vColor;\n" +
            " }";
    int mProgram;

    private FloatBuffer vertexBuffer;
    // number of coordinates per vertex in this array
    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

    float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //顶点统一白色

    public Square() {
        ByteBuffer bb = ByteBuffer.allocateDirect(
                squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);
        //创建一个空的OpenGLES程序
        mProgram = GLES20.glCreateProgram();
        //将顶点着色器加入到程序
        GLES20.glAttachShader(mProgram, vertexShader);
        //将片元着色器加入到程序中
        GLES20.glAttachShader(mProgram, fragmentShader);
        //连接到着色器程序
        GLES20.glLinkProgram(mProgram);
    }

    private final float[] mMVPMatrix = new float[16];
    private final float[] mProjectMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        //计算宽高比
        float ratio=(float)width/height;
        //设置透视投影
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);

    }

    int mMatrixHandler;
    int mPositionHandle;
    int vColorHandle;
    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glUseProgram(mProgram);
        mMatrixHandler= GLES20.glGetUniformLocation(mProgram,"vMatrix");
        GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0);

        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                12, vertexBuffer);

        vColorHandle = GLES20.glGetUniformLocation(mProgram , "vColor");
        GLES20.glUniform4fv(vColorHandle , 1 , color , 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP , 0, 4);
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }

    //编译着色器
    public static int loadShader(int type, String shaderCode){
        int shader = GLES20.glCreateShader(type);
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }

}

上面是以1234的顺序画的,效果如下:

2.思考


很奇怪,我们传入顶点1234的坐标,为什么没有出现正方形的效果?我们来分析其中的一句代码.。

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP , 0, 4);

第二个参数表示从第0个顶点开始绘制,第三个参数表示总共4个顶点需要绘制。4个顶点在之前已经传入。

其中的第一个参数可以设置的值如下:(核心

  • int GL_POINTS       //将传入的顶点坐标作为单独的点绘制
  • int GL_LINES        //将传入的坐标作为单独线条绘制,ABCDEFG六个顶点,绘制AB、CD、EF三条线
  • int GL_LINE_STRIP   //将传入的顶点作为折线绘制,ABCD四个顶点,绘制AB、BC、CD三条线
  • int GL_LINE_LOOP    //将传入的顶点作为闭合折线绘制,ABCD四个顶点,绘制AB、BC、CD、DA四条线。
  • int GL_TRIANGLES    //将传入的顶点作为单独的三角形绘制,ABCDEFG绘制ABC,DEF两个三角形
  • int GL_TRIANGLE_FAN    //将传入的顶点作为扇面绘制,ABCDEF绘制ABC、ACD、ADE、AEF四个三角形
  • int GL_TRIANGLE_STRIP   //将传入的顶点作为三角条带绘制,ABCDEF绘制ABC,BCD,CDE,DEF四个三角形

我们使用的是 GL_TRIANGLE_STRIP ,又传入了4个点按顺序1234绘制。所以会绘制2个三角形是123和234,所以自然没有绘制出正方形。

绘制正方形,只需绘制按顺序1243绘制,就可以绘制出2个三角形是124和243,这两个三角形正好组成了一个正方形。

3.绘制圆形


可以想一想,圆形的绘制上面第一个参数该用什么?

GLES20.GL_TRIANGLE_FAN

假如我们传入坐标012341,0为中心,所以绘制出012 , 023 , 034 , 041 这4个图形,正好环绕一圈得到:

这里我们只绘制了4个,假如我们绘制很多个环绕的小三角形,是不是就可以得到圆呢?

接下来上代码测试一下:

4.绘制圆形-代码:


Activity 和 GLSurfaceView的代码就不给出了,很简单。直接看Render的代码。

public class Circle implements GLSurfaceView.Renderer {
    String vertexShaderCode = "attribute vec4 vPosition;\n" +
            "uniform mat4 vMatrix;\n" +
            "void main() {\n" +
            "  gl_Position = vMatrix*vPosition;\n" +
            "}" ;

    String fragmentShaderCode = "precision mediump float;\n" +
            " uniform vec4 vColor;\n" +
            " void main() {\n" +
            "     gl_FragColor = vColor;\n" +
            " }";
    int mProgram;

    private FloatBuffer vertexBuffer;
    private float[] circlrCoods;
    float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //顶点统一白色

    public Circle() {
        //设置所有坐标
        circlrCoods = createPositions();
        //float[] → FloatBuffer
        ByteBuffer bb = ByteBuffer.allocateDirect(circlrCoods.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(circlrCoods);
        vertexBuffer.position(0);
    }

    private float[]  createPositions(){
        ArrayList<Float> data=new ArrayList<>();
        data.add(0.0f);             //设置圆心坐标
        data.add(0.0f);
        data.add(0.0f);
        //分成100条边,绘制出来应该很像圆了
        float radius = 0.5f; //半径1
        float angDegSpan=360f/100; //依次递加的角度
        for(float i=0;i<360+angDegSpan;i+=angDegSpan){
            data.add((float)(radius*Math.cos(i*Math.PI/180f))); //x
            data.add((float) (radius*Math.sin(i*Math.PI/180f))); //y
            data.add(0.0f); //z
        }
        float[] f=new float[data.size()];
        for (int i=0;i<f.length;i++){
            f[i]=data.get(i);
        }
        return f;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        int vetexShader = loadShader(GLES20.GL_VERTEX_SHADER , vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER , fragmentShaderCode);
        //添加着色器
        mProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(mProgram,vetexShader);
        GLES20.glAttachShader(mProgram,fragmentShader);
        GLES20.glLinkProgram(mProgram);
    }

    private final float[] mMVPMatrix = new float[16];
    private final float[] mProjectMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0,0,width,height);
        float ratio = (float) width/height;
        //设置矩阵
        Matrix.frustumM(mProjectMatrix,0,-ratio,ratio,-1,1,3,20);
        Matrix.setLookAtM(mViewMatrix,0,0f,0f,7.0f,0f,0f,0f,0f,1.0f,0f);
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
    }

    int mMatrixHandler;
    int mPositionHandle;
    int vColorHandle;
    @Override
    public void onDrawFrame(GL10 gl) {
        Log.e("TAG","error");
        GLES20.glUseProgram(mProgram);
        mMatrixHandler = GLES20.glGetUniformLocation(mProgram,"vMatrix");
        GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0);

        mPositionHandle = GLES20.glGetAttribLocation(mProgram,"vPosition");
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        GLES20.glVertexAttribPointer(
                mPositionHandle,
                3,
                GLES20.GL_FLOAT,
                false,
                12,
                vertexBuffer
                );

        vColorHandle = GLES20.glGetUniformLocation(mProgram,"vColor");
        GLES20.glUniform4fv(vColorHandle,1,color,0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN , 0 , circlrCoods.length/3);
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }

    //编译着色器
    public static int loadShader(int type, String shaderCode){
        int shader = GLES20.glCreateShader(type);
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }

}

代码中主要的内容就是计算顶点的坐标,其余的代码跟之前类似。

不过要注意的是:在获得顶点坐标数组 float[] 后转换为 FloatBuffer 时,一定是用下面:

ByteBuffer bb = ByteBuffer.allocateDirect(circlrCoods.length * 4);

而不是

ByteBuffer bb = ByteBuffer.allocate(circlrCoods.length * 4);

之前没发现,一直闪退!!!

猜你喜欢

转载自blog.csdn.net/qq_38261174/article/details/83064949