Android OpenGL学习:最小系统绘制

Posted MichaelX_Blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 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 OpenGL学习:最小系统绘制的主要内容,如果未能解决你的问题,请参考以下文章

我的OpenGL学习进阶之旅介绍一下OpenGL ES的 光栅化 : 剔除多边形偏移

opengl算法学习---消隐

glScalef 用于光栅字体,OpenGL

Android OpenGL ES绘制三角形

光栅化算法-中点画圆算法

OpenGL ES 学习 -- 渲染模式和GLSL