OpenGL ES-GLSurfaceView绘制三角形、正方形

发布于:2023-02-15 ⋅ 阅读:(520) ⋅ 点赞:(0)

一、Activity中初始化GLSurfaceView,setMyRenderer

setEGLContextClientVersion():设置需要使用的OpenGL ES的版本;
setRenderer:声明一个Render,对渲染的操作都在Render里面
setRenderMode:
设置渲染模式,默认是不断地渲染,即 RENDERMODE_CONTINUOUSLYRENDERMODE_WHEN_DIRTY只在onSurfaceCreated时会渲染一次,之后只有调用 GLSurfaceView.requestRender() 方法时才会进行渲染。

    @Override
    protected void onStart() {
        super.onStart();
        mGLSurfaceView = findViewById(R.id.gl_surface_view);
        //设置OpenGL ES的版本2.0
        mGLSurfaceView.setEGLContextClientVersion(2);
        mGLSurfaceView.setRenderer(new MyRenderer(getApplicationContext()));
        // 设置渲染的模式
        mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }

二、MyRenderer类实现 GLSurfaceView.Renderer

实现GLSurfaceView.Renderer接口的三个方法:
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig);
public void onSurfaceChanged(GL10 gl10, int i, int i1);
public void onDrawFrame(GL10 gl10);

(1)创建着色器程序

着色器程序需要在surface创建完成后才能进行创建。
surface创建完成后会回调进onSurfaceCreated方法,可以在onSurfaceCreated方法中对Renderer进行初始化。

  1. 通过*GLES20.glCreateProgram()*获取着色器程序;
  2. glAttachShader( int program, int shader ) 编译顶点着色器代码于片元着色器代码,并与着色器程序建立链接;
  3. *GLES20.glGetAttribLocation(mProgramId, A_POSITION)*获取顶点着色器中的参数“a_Position”与“a_Color”的地址;
  4. *GLES20.glVertexAttribPointer( int indx, int size, int type, boolean normalized, int stride, java.nio.Buffer ptr );可以为顶点着色器中的参数“a_Position”进行赋值声明,
    indx代表上一步获取的地址,
    size表示一个顶点在数组中占几位,代码中只是用了xy坐标,所以长度为2,
    type表示顶点所使用的数据类型,代码中使用的是浮点型对应GLES20.GL_FLOAT,
    normalized指定在访问定点数据值时是应将其标准化(GL_TRUE)还是直接转换为定点值(GL_FALSE)
    stride表示当前顶点到下一个顶点需要偏移的字节位数,代码中 = [(坐标位数 + 颜色位数)
    每一位的字节长度 ]
    ptr 表示传入的顶点数组转换的float类型的byteBuffer

(2)设置显示窗口

onSurfaceChanged回调方法中会返回当前SurfaceView的宽高;
通过 glViewport( int x, int y, int width, int height ) 设置显示窗口;x,y分别代表窗口向X和Y方向的偏移量,X值增长则图像向右偏移,反之向左,y变量控制上下偏移,width和height 则控制窗口的宽高。
glViewport( int x, int y, int width, int height ) XJ_

(3)绘制三角形、正方形

在*onDrawFrame(GL10 gl10)*中开始进行图形绘制:
顶点数组中共有7个顶点,绘制三角形从索引0开始取三个,绘制四边形从3开始取4个,OpenGL如何知道每个顶点要取多少位数据,是在前面GLES20.glVertexAttribPointer是声明了顶点数据的步长信息。
//绘制三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN,0,3);
//绘制四边形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN,3,4);

glDrawArrays( int mode, int first, int count );

  1. mode:指定要渲染的图元类型。 (GL_POINTS,GL_LINE_STRIP,GL_LINE_LOOP,GL_LINES,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN和GL_TRIANGLES)
    GL_POINTS:GL_POINTS绘制的点比较小不宜看清,可以再顶点着色器代码中设置OpenGL的内置变量gl_PointSize加粗点的显示效果,也可以通过获取参数“a_Position”与“a_Color”类似步骤传入变量赋值给gl_PointSize实现动态调整点的大小。
    private static final String VERTEX_SHADER =
    “attribute vec4 a_Position;\n” +
    “attribute vec4 a_Color;\n” +
    “varying vec4 v_Color;\n” +
    “void main() {\n” +
    " v_Color = a_Color;\n" +
    " gl_Position = a_Position;\n" +
    " gl_PointSize = 30.0;\n" +
    “}”;
    在这里插入图片描述
    GL_LINE_STRIP
    在这里插入图片描述
    GL_LINE_LOOP
    在这里插入图片描述
    GL_LINES
    在这里插入图片描述
    GL_TRIANGLE_STRIP
    在这里插入图片描述
    GL_TRIANGLE_FAN
    在这里插入图片描述
    GL_TRIANGLES
    在这里插入图片描述
  2. first:指定已启用阵列中的起始索引。
  3. count:指定要渲染的索引数。
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.Log;

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

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

public class MyRenderer implements GLSurfaceView.Renderer {

    private static final String TAG = "XJ_MyRenderer";
    private Context mContext;
    private int mProgramId;

    private static final String VERTEX_SHADER =
            "attribute vec4 a_Position;\n" +
            "attribute vec4 a_Color;\n" +
            "varying vec4 v_Color;\n" +
            "void main() {\n" +
            "    v_Color = a_Color;\n" +
            "    gl_Position = a_Position;\n" +
            "}";
    private static final String FRAGMENT_SHADER =
            "precision mediump float;\n" +
            "varying vec4 v_Color;\n" +
            "void main() {\n" +
            "    gl_FragColor = v_Color;\n" +
            "}";

    //单个顶点坐标的长度(<=着色器中定义的向量维度)
    private final static int COORDINATE_LENGTH = 2;
    //单个顶点颜色的长度
    private final static int COLOR_LENGTH = 3;
    //浮点类型占用的字节数
    private final static int BYTES_FOR_FLOAT = 4;
    //需要向GLSL顶点着色器输入的变量名
    private static final String A_POSITION = "a_Position";
    private static final String A_COLOR = "a_Color";

    //STRIDE是一个顶点的字节偏移(顶点坐标xy+颜色rgb)
    private final int STRIDE = (COORDINATE_LENGTH+ COLOR_LENGTH )* BYTES_FOR_FLOAT;

    private FloatBuffer mVertexData;

    /**
     *              ^
     *              |1
     *              |
     *              |
     *  -1          |0            1
     * ------------------------------>
     *              |
     *              |
     *              |
     *              |-1
     */
    public MyRenderer(Context context) {

        mContext = context;

        //顶点数组
        float[] TRIANGLE_COORDS = {
                //坐标            //颜色
                0.25f, 0.25f,     1f,0.5f,0.5f,
                0.25f, 0.75f,   0.5f, 1f,0.5f,
                0.75f, 0.75f,    0.5f, 0.5f,1f,

                -0.25f, -0.25f,     1f,0.5f,0.5f,
                -0.75f, -0.25f,   0.5f, 1f,0.5f,
                -0.75f, -0.75f,    0.5f, 0.5f,1f,
                -0.25f, -0.75f,    1f, 0.5f,1f
        };

        //通过nio ByteBuffer把设置的顶点数据加载到内存
        mVertexData = ByteBuffer
                .allocateDirect(TRIANGLE_COORDS.length * BYTES_FOR_FLOAT) //需要多少字节内存
                .order(ByteOrder.nativeOrder())//大小端排序
                .asFloatBuffer()
                .put(TRIANGLE_COORDS);//设置数据
    }
    
    private void initRenderer() {
        //创建着色器程序
        mProgramId = MyShaderLoader.loadProgram(VERTEX_SHADER, FRAGMENT_SHADER);
        Log.d(TAG, "initRenderer: mProgramId = " + mProgramId );
        int aPosition = GLES20.glGetAttribLocation(mProgramId, A_POSITION);
        Log.i(TAG, "initRenderer: aPosition = "+aPosition);
        mVertexData.position(0);
        GLES20.glVertexAttribPointer(aPosition,
                COORDINATE_LENGTH,//用几个偏移描述一个顶点
                GLES20.GL_FLOAT,//顶点数据类型
                false,
                STRIDE,//一个顶点需要多少个字节偏移
                mVertexData//分配的buffer
        );

        //开启顶点着色器的attribute
        GLES20.glEnableVertexAttribArray(aPosition);

        int aColor = GLES20.glGetAttribLocation(mProgramId, A_COLOR);
        mVertexData.position(COORDINATE_LENGTH);
        GLES20.glVertexAttribPointer(aColor,COLOR_LENGTH,GLES20.GL_FLOAT,false,STRIDE,mVertexData);
        GLES20.glEnableVertexAttribArray(aColor);
    }


    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        Log.i(TAG, "onSurfaceCreated!");
        initRenderer();
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int i, int i1) {
        Log.i(TAG, "onSurfaceChanged!");
        GLES20.glViewport(0, 0, i, i1);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        //清屏
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        //绘制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN,0,3);
        //绘制四边形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN,3,4);
    }
}

三、加载着色器以及程序的工具类

着色器句柄、程序句柄、变量句柄类似于在GPU中对应对象的地址。

import android.opengl.GLES20;
import android.util.Log;

public class MyShaderLoader {

    private static final String TAG = "XJ_MyShaderLoader";

    private static int loadShader(int type, String codeStr) {
        //1. 根据类型(顶点着色器、片元着色器)创建着色器,拿到着色器句柄
        int shader = GLES20.glCreateShader(type);
        Log.i(TAG, "compileShaderCode: type=" + type + " shaderId=" + shader);

        if (shader > 0) {
            //2. 设置着色器代码 ,shader句柄和code进行绑定
            GLES20.glShaderSource(shader, codeStr);
            //3. 编译着色器,
            GLES20.glCompileShader(shader);

            //4. 查询编译状态
            int[] status = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0);
            Log.i(TAG, "loadShader: status[0]=" + status[0]);
            //如果失败,释放资源
            if (status[0] == 0) {
                GLES20.glDeleteShader(shader);
                return 0;
            }
        }
        return shader;
    }

    public static int loadProgram(String verCode, String fragmentCode) {
        //1. 创建Shader程序,获取到program句柄
        int programId = GLES20.glCreateProgram();
        if(programId == 0){
            Log.e(TAG, "loadProgram: glCreateProgram error!" );
            return 0;
        }
        Log.d(TAG, "loadProgram: programId = " + programId );
        //2. 根据着色器语言类型和代码,attach着色器
        GLES20.glAttachShader(programId, loadShader(GLES20.GL_VERTEX_SHADER, verCode));
        GLES20.glAttachShader(programId, loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentCode));
        //3. 链接
        GLES20.glLinkProgram(programId);
        //4. 使用
        GLES20.glUseProgram(programId);
        return programId;
    }

}


网站公告

今日签到

点亮在社区的每一天
去签到