3.着色程序相关的API
创建着色程序流程 : 加载顶点着色器 --> 加载片元着色器 --> 创建着色程序 --> 将顶点着色器加入着色程序 --> 将片元着色器加入着色程序 --> 链接着色程序 --> 获取链接着色程序结果
(1)创建OpenGL程序
int program = GLES20.glCreateProgram();
调用GLES20.glCreateProgram()方法 , 可以创建一个3D程序 , 返回程序的引用 , 如果不返回0 , 说明没有创建成功.
(2)获取OpenGL中的错误信息
GLES20.glGetError();
返回一个int类型的错误码 , 如果没有错误 , 就会返回 GLES20.GL_NO_ERROR 常量.
(3)向程序中加入着色器
GLES20.glAttachShader(program, vertextShader);
参数 : program 是调用GLES20.glCreateProgram()方法创建程序的返回值 , 这是程序的引用 . vertextShader是着色器的引用 , 注意 这个着色器是加载了着色脚本并且成功编译的着色器引用 .
返回值 : 该方法没有返回值;
(4)连接程序
GLES20.glLinkProgram(program);
参数 : 需要链接的程序的引用, 即着色程序容器的句柄;
作用 : 着色程序中存放定点着色器与片元着色器;
(5)获取链接程序结果
int[] linkStatus = new int[1]; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
参数 : program , 程序的引用 ;
GLES20.GL_LINK_STATUS , 想要获取的信息的类别;
linkStatus , 存放结果的数组;
index , 将结果存放的数组的下标;
作用 : 这个方法可以获取到链接程序操作是否成功, 如果结果不为1, 说明链接程序失败;
(6)删除着色程序
GLES20.glDeleteProgram(program);
参数 : 着色程序的引用;
作用 : 删除链接失败的着色程序;
4. 源码
package shuliang.han.rotatetriangle; import java.io.ByteArrayOutputStream; import java.io.InputStream; import android.content.res.Resources; import android.opengl.GLES20; import android.util.Log; /* * 这个工具类用来加载定点着色器与片元着色器 */ public class ShaderUtil { /** * 加载着色器方法 * * 流程 : * * ① 创建着色器 * ② 加载着色器脚本 * ③ 编译着色器 * ④ 获取着色器编译结果 * * @param shaderType 着色器类型,顶点着色器(GLES20.GL_FRAGMENT_SHADER), 片元着色器(GLES20.GL_FRAGMENT_SHADER) * @param source 着色脚本字符串 * @return 返回的是着色器的引用, 返回值可以代表加载的着色器 */ public static int loadShader(int shaderType , String source){ //1.创建一个着色器, 并记录所创建的着色器的id, 如果id==0, 那么创建失败 int shader = GLES20.glCreateShader(shaderType); if(shader != 0){ //2.如果着色器创建成功, 为创建的着色器加载脚本代码 GLES20.glShaderSource(shader, source); //3.编译已经加载脚本代码的着色器 GLES20.glCompileShader(shader); int[] compiled = new int[1]; //4.获取着色器的编译情况, 如果结果为0, 说明编译失败 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); if(compiled[0] == 0){ Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":"); Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader)); //编译失败的话, 删除着色器, 并显示log GLES20.glDeleteShader(shader); shader = 0; } } return shader; } /** * 检查每一步的操作是否正确 * * 使用GLES20.glGetError()方法可以获取错误代码, 如果错误代码为0, 那么就没有错误 * * @param op 具体执行的方法名, 比如执行向着色程序中加入着色器, * 使glAttachShader()方法, 那么这个参数就是"glAttachShader" */ public static void checkGLError(String op){ int error; //错误代码不为0, 就打印错误日志, 并抛出异常 while( (error = GLES20.glGetError()) != GLES20.GL_NO_ERROR ){ Log.e("ES20_ERROR", op + ": glError " + error); throw new RuntimeException(op + ": glError " + error); } } /** * 创建着色程序 * * ① 加载顶点着色器 * ② 加载片元着色器 * ③ 创建着色程序 * ④ 向着色程序中加入顶点着色器 * ⑤ 向着色程序中加入片元着色器 * ⑥ 链接程序 * ⑦ 获取链接程序结果 * * @param vertexSource 定点着色器脚本字符串 * @param fragmentSource 片元着色器脚本字符串 * @return */ public static int createProgram(String vertexSource , String fragmentSource){ //1. 加载顶点着色器, 返回0说明加载失败 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); if(vertexShader == 0) return 0; //2. 加载片元着色器, 返回0说明加载失败 int fragShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); if(fragShader == 0) return 0; //3. 创建着色程序, 返回0说明创建失败 int program = GLES20.glCreateProgram(); if(program != 0){ //4. 向着色程序中加入顶点着色器 GLES20.glAttachShader(program, vertexShader); checkGLError("glAttachShader"); //5. 向着色程序中加入片元着色器 GLES20.glAttachShader(program, fragShader); checkGLError("glAttachShader"); //6. 链接程序 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("ES20.ERROR", "链接程序失败 : "); Log.e("ES20.ERROR", GLES20.glGetProgramInfoLog(program)); //如果链接程序失败删除程序 GLES20.glDeleteProgram(program); program = 0; } } return program; } /** * 从assets中加载着色脚本 * * ① 打开assets目录中的文件输入流 * ② 创建带缓冲区的输出流 * ③ 逐个字节读取文件数据, 放入缓冲区 * ④ 将缓冲区中的数据转为字符串 * * @param fileName assets目录中的着色脚本文件名 * @param resources 应用的资源 * @return */ public static String loadFromAssetsFile(String fileName, Resources resources){ String result = null; try { //1. 打开assets目录中读取文件的输入流, 相当于创建了一个文件的字节输入流 InputStream is = resources.getAssets().open(fileName); int ch = 0; //2. 创建一个带缓冲区的输出流, 每次读取一个字节, 注意这里字节读取用的是int类型 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //3. 逐个字节读取数据, 并将读取的数据放入缓冲器中 while((ch = is.read()) != -1){ baos.write(ch); } //4. 将缓冲区中的数据转为字节数组, 并将字节数组转换为字符串 byte[] buffer = baos.toByteArray(); baos.close(); is.close(); result = new String(buffer, "UTF-8"); result = result.replaceAll("\\r\\n", "\n"); } catch (Exception e) { e.printStackTrace(); } return result; } }
三. Triangle 3D三角形数据
1. 顶点数据容器相关api
初始化顶点数据流程 : 创建ByteBuffer对象 -> 设置ByteBuffer对象顺序 -> 将ByteBuffer对象转为FloatBuffer对象 -> 设置FloatBuffer对象值 -> 设置FloatBuffer对象起始位置
(1) 创建ByteBuffer对象
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4)
allocateDirect()方法创建ByteBuffer对象, 同时分配该字节缓冲去的大小, 注意这个对象最终要转为FloatBuffer对象, 每个float占4个字节, 一共有vertices.length个浮点数, 因此要分配vertices.length * 4 个字节大小.
(2) 设置字节缓冲区顺序
vbb.order(ByteOrder.nativeOrder());
设置字节缓冲区的顺序为本地顺序.
(3) 将字节缓冲区转为浮点缓冲区
mVertexBuffer = vbb.asFloatBuffer();
(4) 向字节缓冲区中存入数据
mColorBuffer.put(colors);
直接调用put方法, 将浮点型数组放入缓冲区.
(5)指定浮点型缓冲区起始位置
mColorBuffer.position(0);
2. 初始化着色器相关api
初始化着色器流程 : 获取顶点,片元着色器 -> 创建着色程序 -> 从着色程序中的顶点着色器获取顶点位置,颜色,投影矩阵引用
(1) 获取着色器属性变量引用
float[] muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
该方法从着色程序中的顶点着色器中获取属性变量(Attribute) aPosition.
(2) 获取着色器一直变量引用
float[] muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
该方方法从着色程序中的顶点着色器获取一致变量
3. 绘制3D图形相关api
绘制三角形流程 :
(1) 指定着色器程序
GLES20.glUseProgram(mProgram);
参数 : 着色程序的引用id
作用 : 该方法的作用是指定程序中要使用的着色器
(2) 设置旋转初始情况
Matrix.setRotateM(float[] rm, int rmOffset, float a, float x, float y, float z)
参数 : rm 变换矩阵; rmOffset 变换矩阵的索引; a 旋转角度; 剩下的三个是旋转的轴
这个方法的作用是设置旋转变化矩阵
(3) 设置位移
Matrix.translateM(float[] m, int mOffset, float x, float y, float z)
参数 : m 变换矩阵; mOffset 变换矩阵的起始位置; 剩下的三个是位移向量.
(4) 设置旋转矩阵
Matrix.rotateM(float[] m, int mOffset, float a, float x, float y, float z)
参数 : m 变换矩阵; mOffset 变换矩阵起始位置; a 旋转的角度; 剩下的三个参数是旋转的轴;
(5) 应用投影和视口变换
GLES20.glUniformMatrix4fv(int location, int count, boolean transpose, float[] value, int offset)
参数 :
(6) 将顶点数据传进渲染管线
GLES20.glVertexAttribPointer( maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer );
参数 : 顶点位置数据引用 几个一组 单位 false 个数 数据缓冲区.
(7) 启用传入的数据
GLES20.glEnableVertexAttribArray(maPositionHandle);
参数 : 从着色程序中获取的数据引用
作用 : 将刚才传入渲染管线的数据启用;
(8) 执行绘制方法
GLES20.glEnableVertexAttribArray(maPositionHandle);
该方法绘制三角形