当前位置:网站首页>OpenGL ES 学习初识(1)

OpenGL ES 学习初识(1)

2022-07-06 07:01:00 换元不配限

1.认识OpenGL ES

OpenGL是跨平台2D/3D图形API,而OpenGL ES是在前者基础上扩展的版本,适用于手机等嵌入式设备。具有以下版本:

版本解释
OpenGL ES 1.02003年发布
OpenGL ES 1.1支持了多纹理,顶点缓冲对象等
OpenGL ES 2.02007年发布,基于OpenGL 2.0,删除了1.x中的固定管线(pipeline),提供可编程的管线
OpenGL ES 3.02012年发布,向后兼容2.x
OpenGL ES 3.12014年发布
OpenGL ES 3.22015年发布

OpenGL ES 1.x 和OpenGL ES2.0不兼容的,是完全两种不同的实现。

  • OpenGL EX 1.x仅支持固定管线渲染,可以固件支持或软件模拟,但渲染能力有限。

  • OpenGL EX 2.X采用可编程的渲染管线,提高了渲染能力。但编程难度也提高了,且要求设备中必须有对应GPU硬件支持。
    图源Android3D游戏开发技术宝典图源Android3D游戏开发技术宝典
    上面图3-16和图3-21分别是OpenGL 1.x和OpenGL 2.x渲染管线过程示意图。

  • “变换和光照”---->“顶点着色器”

  • “纹理环境和颜色求和”、“雾”、“Alpha测速”---->“片元着色器”

本系列文章将主要介绍OpenGL ES2.x

2.初识OpenGL ES2.x程序面貌

在认识了OpenGL ES基础后,下面直接带来一个入门级Demo(本程序官网也有类似的可以学习)。代码中添加了注释,可以更好帮助入门学习。

2.1 效果图

在这里插入图片描述

2.2 工具类ShaderUtil.java

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

    /**
     * 加载着色器编码进GPU并编译
     *
     * @param shaderType 着色器类型
     * @param source     着色器脚本字符串
     * @return
     */
    public static int loadShader(int shaderType, String source) {
        int shader = GLES20.glCreateShader(shaderType);
        if (shader != 0) {
            GLES20.glShaderSource(shader, source);
            GLES20.glCompileShader(shader);
            int[] compiled = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0) {
                Log.e(TAG, "loadShader: ES20_ERROR,Could not compile shader " + shaderType + ";");
                Log.e(TAG, "loadShader: ES20_ERROR,shader = " + GLES20.glGetShaderInfoLog(shader));
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

    /**
     * 创建着色器程序
     *
     * @param vertexSource
     * @param fragmentSource
     * @return
     */
    public static int createProgram(String vertexSource, String fragmentSource) {
        //加载顶点着色器
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }

        //加载片元着色器
        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return 0;
        }
        int program = GLES20.glCreateProgram();
        if (program != 0) {
            GLES20.glAttachShader(program, vertexShader);
            checkGLError("glAttachShader");
            GLES20.glAttachShader(program, pixelShader);
            checkGLError("glAttachShader");
            GLES20.glLinkProgram(program);
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES20.GL_TRUE) {
                Log.e(TAG, "createProgram: ES20_ERROR,Could not link program");
                Log.e(TAG, "createProgram: ES20_ERROR,program = " + GLES20.glGetProgramInfoLog(program));
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
    }

    /**
     * 检测每一步操作是否有错误的方法
     *
     * @param op
     */
    public static void checkGLError(String op) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.d(TAG, ">>>>>> " + op + " :glError " + error);
            throw new RuntimeException(op + ":glError " + error);
        }
    }

    /**
     * 从sh脚本中加载着色器内容
     *
     * @param fname
     * @param resources
     * @return
     */
    public static String loadFromAssetsFile(String fname, Resources resources) {
        String result = null;
        try {
            InputStream in = resources.getAssets().open(fname);
            int ch = 0;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((ch = in.read()) != -1) {
                baos.write(ch);//获取的信息写入流
            }
            byte[] buffer = baos.toByteArray();
            baos.close();
            in.close();
            result = new String(buffer, "UTF-8");
            result = result.replaceAll("\\r\\n", "\n");


        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "loadFromAssetsFile: result = " + result);
        return result;
    }


}

2.3 三角形Triangle

public class Triangle {
    public static float[] mProjMatrix = new float[16];//4x4投影矩阵
    public static float[] mVMatrix = new float[16];//摄像机位置朝向的参数矩阵
    public static float[] mMvPMatrix;//总变换矩阵
    private int program;//自定义渲染管线着色器程序id
    private int muMVPMatrixHandle;//总变换矩阵引用
    private int maPositionHandle;//顶点位置属性引用
    private int maColorHandle;//顶点颜色属性引用
    private String mVertexShader;//顶点着色器代码脚本
    private String mFragmentShader;//片元着色器代码脚本
    private static float[] mMMatrix = new float[16];//具体物体的3D变换矩阵,包括旋转、平移、缩放
    private FloatBuffer mVertexBuffer;//顶点坐标数据缓冲
    private FloatBuffer mColorBuffer;//顶点着色数据缓冲
    private int vCount = 0;//顶点数量
    public float xAngle = 0;//绕X轴旋转的角度

    public Triangle(MyTDView myTDView) {
        initVertexData();
        initShader(myTDView);
    }

    /**
     * 初始化着色器
     * @param myTDView
     */
    private void initShader(MyTDView myTDView) {
        mVertexShader = ShaderUtil.loadFromAssetsFile("vertex.sh", myTDView.getResources());
        mFragmentShader = ShaderUtil.loadFromAssetsFile("frag.sh", myTDView.getResources());
        program = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
        maPositionHandle = GLES20.glGetAttribLocation(program, "aPosition");
        maColorHandle = GLES20.glGetAttribLocation(program, "aColor");
        muMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
    }

    /**
     * 绘制三角形
     */
    public void drawSelf() {
        GLES20.glUseProgram(program);
        Matrix.setRotateM(mMMatrix, 0, 0, 0, 1, 0);//初始化变换矩阵
        Matrix.translateM(mMMatrix, 0, 0, 0, 1);//设置沿Z轴正向位移
        Matrix.rotateM(mMMatrix, 0, xAngle, 1, 0, 0);//设置绕X轴旋转
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFinalMatrix(mMMatrix), 0);
        GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer);
        GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false, 4 * 4, mColorBuffer);
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        GLES20.glEnableVertexAttribArray(maColorHandle);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
    }

    /**
     * 初始化顶点数据
     */
    private void initVertexData() {
        vCount = 3;
        final float UNIT_SIZE = 0.2f;//设置单位长度
        //顶点坐标数组
        float vertices[] = new float[]{
                -4 * UNIT_SIZE, 0, 0,
                0, -4 * UNIT_SIZE, 0,
                4 * UNIT_SIZE, 0, 0
        };
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
        vbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
        mVertexBuffer = vbb.asFloatBuffer();//转换为浮点型缓冲
        mVertexBuffer.put(vertices);//在缓冲区内写入数据
        mVertexBuffer.position(0);//设置缓冲区起始位置

        //顶点颜色数组
        float colors[] = new float[]{
                1, 1, 1, 0,
                0, 0, 1, 0,
                0, 1, 0, 0
        };
        ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
        cbb.order(ByteOrder.nativeOrder());
        mColorBuffer = cbb.asFloatBuffer();
        mColorBuffer.put(colors);
        mColorBuffer.position(0);
    }

    /**
     * 最终变换矩阵
     *
     * @param spec
     * @return
     */
    public static float[] getFinalMatrix(float[] spec) {
        //初始化总变换矩阵
        mMvPMatrix = new float[16];
        Matrix.multiplyMM(mMvPMatrix, 0, mVMatrix, 0, spec, 0);
        Matrix.multiplyMM(mMvPMatrix, 0, mProjMatrix, 0, mMvPMatrix, 0);
        return mMvPMatrix;
    }
}

2.4 渲染类MyTDView

GLSurfaceView和GLSurfaceView.Render的使用

public class MyTDView extends GLSurfaceView {
    private static final String TAG = "MyTDView";
    //每次三角形旋转的角度
    private final float ANGLE_SPAN = 0.375f;
    //线程
    private RotateThread mRotateThread;
    //自定义渲染器
    private SceneRenderer mSceneRenderer;


    public MyTDView(Context context) {
        super(context);
        //使用OpenGLES2.0
        this.setEGLContextClientVersion(2);
        mSceneRenderer = new SceneRenderer();
        this.setRenderer(mSceneRenderer);
        this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }


    private class SceneRenderer implements GLSurfaceView.Renderer {
        Triangle mTriangle;

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            GLES20.glClearColor(0, 0, 0, 1.0f);//设置屏幕背景颜色
            mTriangle = new Triangle(MyTDView.this);
            GLES20.glEnable(GLES20.GL_DEPTH_TEST);
            mRotateThread = new RotateThread();
            mRotateThread.start();

        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);//设置视口
            float ratio = width / height;
            //设置透视投影
            Matrix.frustumM(Triangle.mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
            //设置摄像机
            Matrix.setLookAtM(Triangle.mVMatrix, 0, 0, 0, 3,
                    0f, 0f, 0f, 0f, 1.0f, 0.0f);


        }

        @Override
        public void onDrawFrame(GL10 gl) {
            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
            mTriangle.drawSelf();
        }
    }

    private class RotateThread extends Thread {
        //设置循环标识位
        public boolean flag = true;

        @Override
        public void run() {
            super.run();
            while (flag) {
                mSceneRenderer.mTriangle.xAngle = mSceneRenderer.mTriangle.xAngle + ANGLE_SPAN;
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }
}

2.5 顶点着色器和片元着色器代码

顶点着色器和片元着色器资源放置在assets目录下。本案例使用的如下所示:

2.5.1 frag.sh

precision mediump float;
varying vec4 vColor;
void main(){
  gl_FragColor = vColor;
}

2.5.2 vertex.sh顶点着色器

uniform mat4 uMVPMatrix;//总变换矩阵
attribute vec3 aPosition;//顶点位置
attribute vec4 aColor;//顶点颜色
varying vec4 vColor;//用于传递给片元着色器的易变变量
void main(){
    gl_Position = uMVPMatrix * vec4(aPosition,1);//根据总变换矩阵计算此次绘制顶点的位置
    vColor = aColor;
}

2.6 其他

public class MainActivity extends AppCompatActivity {
    private MyTDView mTDView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        mTDView = new MyTDView(this);
        mTDView.requestFocus();
        mTDView.setFocusableInTouchMode(true);
        setContentView(mTDView);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mTDView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mTDView.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

    }
}

本示例程序可以直接运行,查看效果。

【参考】

  1. https://www.khronos.org/opengles/
  2. 《OpenGL ES应用开发实践指南 Android卷 Kevin Brothaler》
  3. https://en.wikipedia.org/wiki/OpenGL_ES
  4. https://developer.android.google.cn/guide/topics/graphics/opengl
  5. https://developer.android.google.cn/training/graphics/opengl
  6. Android3D游戏开发技术宝典OpenGL ES 2.0
原网站

版权声明
本文为[换元不配限]所创,转载请带上原文链接,感谢
https://blog.csdn.net/ITMonkeyKing/article/details/125618082