Android Opengles开篇
前言:opengles感觉算是一个很"古老"的技术了,从1.0-2.0-3.0经历 了三个版本了
本篇及以后讲述的都是建立在opengles2.0基础上进行demo演示的。可能有些人
对opengl比较陌生,但在Android手机当中和图像相关的东西,都会涉及到Opengl。
一.什么是Opengles?
Opengl这东西是有的,为什么要在后面加上一个ES呢?它主要是针对于嵌入式设备,对原本Opengl
进行了裁剪的缩减库.Opengles的功能比Opengl要少一些。说白了就是Opengles中你要写的代码就更多了,
因为在Opengl中有的一些基本图形被缩减到只有点,线,三角形了。
二.废话就不. 多说了,开篇就用opengles画个三角形,对其中的基本概念进行叙述。
开发工具就用是Android studio2.1
在AndroidStudio中有个GLSL的插件,可以识别着色器语言的.
GLSL是opengles中一个关键的文件,它主要是分为Vertex(顶点)Shader的glsl以及Fragment(片段)Shader的glsl.对它们的编写就决定最终物体相关属性是怎么设置的了。
对于GLES的说明会放到后面进行。这里画三角形,对GLSL编写也是很简单的,就直接在代码里加注释说明。
以建造房子为例子来阐述一个三角形的绘制
三.首先是GLES的简单编写
1.画笔准备:
Vertex和Frament在opengles中是以字符串的形式被载入,所以在assets里面创建两个资源文件
vertex.glsl和frag.glsl
vertex.glsl
uniform mat4 uMVPMatrix; //顶点最终变换矩阵
attribute vec3 aPostion; //顶点坐标值(x,y,z)
attribute vec4 aColor; //顶点的颜色(R,G,B,A)
varying vec4 vColor; //传递给片段着色器的颜色值,varying声明的变量都是要传递给fragment的
void main(){
//gl_Position是glsl的内置变量,记录顶点最终的位置 。
//vec4(aPostion,1)为是矩阵想成匹配
gl_Position = uMVPMatrix * vec4(aPostion,1);
vColor = aColor;//将顶点颜色值传递给fragment
}
frag.glsl
precision mediump float; //声明float的精度,一般情况下都是用mediump的
varying vec4 vColor; //接收从顶点glsl传过来的颜色参数
//对片段颜色的具体处理
void main(){
//直接将顶点传过来的颜色参数赋值给了内置变量gl_fragColor,也就给fragment上色了
gl_FragColor = vColor;
}
2.将shader(glsl)载入到系统中做画画准备:
帮助类的编写,这个是固定的模板,以后都可以直接进行复用的.
这个帮主类的简单流程如下:
- 将asset中的vertext.glsl和frag.glsl
- 方法名为:public static String loadFromAssetsFile(String fname, Resources r);
- 获取vertex的ID和fragment的ID
- 方法名为:public static int loadShader(int shaderType, String source )
- 通过上面获得的ID,将vertext和fragment组合成programe
- public static int createProgram(String vertexSource, String fragmentSource)
帮助类ShaderUtil的具体代码
package com.bn.Sample3_1;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log;
//加载顶点Shader与片元Shader的工具类
public class ShaderUtil {
// 加载制定shader的方法
public static int loadShader(int shaderType, // shader的类型
// GLES20.GL_VERTEX_SHADER(顶点)
// GLES20.GL_FRAGMENT_SHADER(片元)
String source // shader的脚本字符串
) {
// 创建一个新shader
int shader = GLES20.glCreateShader(shaderType);
// 若创建成功则加载shader
if (shader != 0) {
// 加载shader的源代码
GLES20.glShaderSource(shader, source);
// 编译shader
GLES20.glCompileShader(shader);
// 存放编译成功shader数量的数组
int[] compiled = new int[1];
// 获取Shader的编译情况
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {// 若编译失败则显示错误日志并删除此shader
Log.e("ES20_ERROR", "Could not compile shader " + shaderType
+ ":");
Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
// 创建shader程序的方法
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);
// 存放链接成功program数量的数组
int[] linkStatus = new int[1];
// 获取program的链接情况
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
// 若链接失败则报错并删除程序
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e("ES20_ERROR", "Could not link program: ");
Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
// 检查每一步操作是否有错误的方法
public static void checkGlError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e("ES20_ERROR", op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
// 从sh脚本中加载shader内容的方法
public static String loadFromAssetsFile(String fname, Resources r) {
String result = null;
try {
InputStream in = r.getAssets().open(fname);
int ch = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((ch = in.read()) != -1) {
baos.write(ch);
}
byte[] buff = baos.toByteArray();
baos.close();
in.close();
result = new String(buff, "UTF-8");
result = result.replaceAll("\\r\\n", "\n");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
准备工作做完,就需要开始绘制三角形了。
3.Trangle 三角形类编写
注意事项:1.opengles采用的是左手坐标系x->大拇指,y->中指,z->食指
2.其中涉及到的一些矩阵,后续会讲到。
//三角形
public class Triangle
{
public static float[] mProjMatrix = new float[16];//4x4矩阵 投影用
public static float[] mVMatrix = new float[16];//摄像机位置朝向9参数矩阵
public static float[] mMVPMatrix;//最后起作用的总变换矩阵
int mProgram;//自定义渲染管线程序id
int muMVPMatrixHandle;//总变换矩阵引用id
int maPositionHandle; //顶点位置属性引用id
int maColorHandle; //顶点颜色属性引用id
String mVertexShader;//顶点着色器
String mFragmentShader;//片元着色器
static float[] mMMatrix = new float[16];//具体物体的移动旋转矩阵,旋转、平移
FloatBuffer mVertexBuffer;//顶点坐标数据缓冲
FloatBuffer mColorBuffer;//顶点着色数据缓冲
int vCount=0;
float xAngle=0;//绕x轴旋转的角度
public Triangle(MyTDView mv)
{
//初始化顶点坐标与着色数据
initVertexData();
//初始化shader
initShader(mv);
}
public 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,
-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,0,0,1,
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);
}
//初始化shader
public void initShader(MyTDView mv)
{
//加载顶点着色器的脚本内容
mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
//加载片元着色器的脚本内容
mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
//基于顶点着色器与片元着色器创建程序
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
//获取程序中顶点位置属性引用id
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
//获取程序中顶点颜色属性引用id
maColorHandle= GLES20.glGetAttribLocation(mProgram, "aColor");
//获取程序中总变换矩阵引用id
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
}
int i=0;
public void drawSelf()
{
//制定使用某套shader程序
GLES20.glUseProgram(mProgram);
//初始化变换矩阵
Matrix.setRotateM(mMMatrix,0,0,0,1,0);
i=1;
if(i == 1){
for(int j=0;j<mMMatrix.length;j++){
Log.i("liang.chen","item"+j+" is:"+mMMatrix[j]);
}
}
i++;
//设置沿Z轴正向位移1
Matrix.translateM(mMMatrix,0,0,0,-1);
//设置绕x轴旋转
Matrix.rotateM(mMMatrix,0,xAngle,0,0,1);
//
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(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);
}
public static float[] getFianlMatrix(float[] spec)
{
mMVPMatrix=new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}
}
现在对代码的==关键点==进行说明:
-
四个重要的矩阵mMMatrix,mVMatrix,mProjMatrix,mMVPMatrix
- mMMatrix指的是物体本身位置,角度的状态矩阵
- mVMatrix指的是人眼看物体时候的位置,角度的状态矩阵
- mProjMatrix指的是物体成像所处的透视环境状态
- mMVPMatrix指的是mMMatrix,mVMatrix,mProjMatrix三者结合的矩阵
-
其它参数说明:
- mVertexBuffer指的是vertex buffer ,将顶点数据灿在vertex buffer里面后后续的管线操作就可以直接从buffer取的数据,这样加快了数据的读取
- mColorBuffer 类似
-
局部代码说明:
float vertices[]=new float[] { -4*UNIT_SIZE,0, 0,0,-4*UNIT_SIZE, 0,4*UNIT_SIZE,0,0, };
顶点的坐标的,前面说过了opengl采用的是左手坐标系
float colors[]=new float[] { 1,0,0,1, 0,0,1,0, 0,1,0,0 };
三个顶点的颜色值采用的是RGBA
Matrix.setRotateM(mMMatrix,0,0,1,0,0),是将mMMatrix初始化为单位矩阵
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0); //为画笔指定顶点位置数据 GLES20.glVertexAttribPointer( maPositionHandle, 3, GLES20.GL_FLOAT, false, 3*4, mVertexBuffer ); GLES20.glVertexAttribPointer ( maColorHandle, 4, GLES20.GL_FLOAT, false, 4*4, mColorBuffer );
这两个的作用是对顶点属性值进行赋值的,以第一个glVertexAttribPointer为例
==maPositionHandle ==是vertex shader中对应的位置属性值,==3==指的是坐标点是以顶点数组中三个值为一个顶点,==GLES20.GL_FLOAT==是顶点数组中数据类型,==34==值的是Buffer中一个顶点的大小因为三个float组成一个顶点所以是34,mVertexBuffer就是顶点坐标数组转成的buffer了5.GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
Android的openlgles的图元有点,线,三角形,所有的图形就靠它们来组成,因为这里
是直接画三角形,所以就用GLES20.GL_TRAINGLES这篇就直观上讲解了opengles绘制一个简单图形是怎么绘制的。下篇开始讲知识点。
代码地址:http://pan.baidu.com/s/1dFcjdZj