Android OpenGL学习:最小系统绘制

文章目录

  • 最小系统目标
  • 整体流程
    • 编写着色器脚本
    • 定义三角形
    • 定义渲染器
    • 应用渲染器
  • OpenGL管道概述(题外)

最小系统目标

利用OpenGL对应api在屏幕上绘制一个平面三角形。

整体流程

  1. 编写着色器脚本
  2. 定义三角形
  3. 定义渲染器
  4. 应用渲染器

编写着色器脚本

vertex_shader.glsl 顶点着色器

attribute vec4 vPosition;
void main() {
	# gl_Position是固定表达
    gl_Position = vPosition;
}

fragment_shader.glsl

precision mediump float;
uniform vec4 vColor;
void main() {
	# gl_FragColor是固定表达
    gl_FragColor = vColor;
}

将脚本放入res/raw/ 文件夹下。

可参考链接:
《OpenGL shader GLSL 语法和函数详解》

定义三角形

Triangle.java

/**
 * 三角形
 * 
 * @version 1.0
 * @since 2019/3/8
 */
public class Triangle implements Shape {
    // 坐标本地内存地址
    private FloatBuffer vertexBuffer;

    // 取几个点
    private static final int COORDS_PER_VERTEX = 3;

    // 三角形坐标
    private static final float triangleCoords[] = {
          -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
            0.0f, 0.3f, 0.0f
    };

    // 设置颜色 red, green, blue 和 alpha (可选) values
    private static final float color[] = {0.0f, 1.0f, 0f, 1.0f};

    private final int mProgram;
    private int mPositionHandle;
    private int mColorHandle;

    // 顶点数量
    private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;

    // 每个顶点4byte
    private final int vertexStride = COORDS_PER_VERTEX * 4;

    public Triangle(Context context) {
        vertexBuffer = ByteBuffer.allocateDirect(triangleCoords.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(triangleCoords);
        vertexBuffer.put(0);

        //根据shader代码和fragment代码 获取到一个渲染程序
        mProgram = ShaderUtil.createProgram(ShaderUtil.readRawTxt(context, R.raw.vertex_shader),
                ShaderUtil.readRawTxt(context, R.raw.fragment_shader));
        if (mProgram > 0) {
            //获取vertex shader的属性vPosition 的地址
            mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
            //获取fragment shader的属性vColor 的地址
            mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        }
    }

    @Override
    public void draw() {
        // 使用渲染程序
        GLES20.glUseProgram(mProgram);
        // 使顶点属性数组有效
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        // 为顶点属性赋值
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
        // 设置颜色
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
        // 绘制图形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
        // 禁用顶点数组
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}

工具类ShaderUtil.java代码

public class ShaderUtil {
    private static final String TAG = "ShaderUtil";

    public static String readRawTxt(Context context, int rawId) {
        InputStream inputStream = context.getResources().openRawResource(rawId);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuffer sb = new StringBuffer();
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    public static int loadShader(int shaderType, String source) {
        // 创建一个顶点或者片段shader
        int shader = GLES20.glCreateShader(shaderType);
        if (shader != 0) {
            // 添加代码到shader
            GLES20.glShaderSource(shader, source);
            // 编译shader
            GLES20.glCompileShader(shader);
            int[] compile = new int[1];
            // 检查编译结果
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0);
            int err = GLES20.glGetError();
            if (compile[0] != GLES20.GL_TRUE) {
                Log.e(TAG, "shader compile error:" + err);
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

    public static int createProgram(String vertexSource, String fragmentSource) {
        // 获取片段shader
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (fragmentShader == 0) {
            return 0;
        }
        // 获取顶点shader
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }
        // 创建一个空的渲染程序
        int program = GLES20.glCreateProgram();
        if (program != 0) {
            //添加vertexShader到渲染程序
            GLES20.glAttachShader(program, vertexShader);
            //添加fragmentShader到渲染程序
            GLES20.glAttachShader(program, fragmentShader);
            // 关联可执行渲染程序
            GLES20.glLinkProgram(program);
            // 检查是否关联成功
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            int err = GLES20.glGetError();
            if (linkStatus[0] != GLES20.GL_TRUE) {
                Log.e(TAG, "link program error:" + err);
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
    }
}

插播一条Shader的创建流程图

定义渲染器

MyRender.java

public class MyRender implements GLSurfaceView.Renderer {
    private Context context;

    private Shape shape;

    public MyRender(Context context) {
        this.context = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        shape = new Triangle(context);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // 清空颜色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        // 绘制三角形
        shape.draw();
    }
}

应用渲染器

MyGLSurfaceView.java

public class MyGLSView extends GLSurfaceView {
    public MyGLSView(Context context) {
        this(context, null);
    }

    public MyGLSView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setEGLContextClientVersion(2);
        // 应用渲染器
        setRenderer(new MyRender(context));
    }
}

只要将MyGLSurfaceView应用到Activity就好了。。。

OpenGL管道概述(题外)

OpenGL是一套流程,而非特定API或者类。如下所示:应用层主要业务是:①,②,⑤。
①读取顶点数据->②执行顶点着色器->③组装图元->④光栅化图元->⑤执行片段着色器->⑥写入帧缓冲区->⑦显示在屏幕上

你可能感兴趣的:(Android)