转载请注明出处
前言OpenGL ES是OpenGL的一个子集,是针对手机、PDA和游戏主机等嵌入式设备而设计的。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
因此OpenGL ES作为第三方库被应用在android中。
到目前为止,OpenGL ES已经发展有了3个版本,OpenGL ES 1.0 , OpenGL ES 2.0 , OpenGL ES 3.0。其中OpenGL ES 1.0 是以OpenGL 1.3规范为基础,OpenGL ES 2.0 是以OpenGL 2.0 为基础,OpenGL ES 3.0是移动设备专用,以OpenGL 4.3为标准。
值得注意的是,在OpenGL2.0以前,OpenGL用的都是固定管道。之后就改为可渲染管道了,
所谓固定管道,就是固定工序,固定流程,以开关的形式开发出来供开发者调用,要实现什么功能,开发者得自己去考虑这些开关之间的组合形式,效率很低。
而渲染管道,就弥补了固定管道的缺点,它自由度高,可通过编程来实现功能,效率在大提高。因此这也是为什么在2.xAPI中删除了以前的一些固定管道的API。
因此,本文将结合一个例子,具体看一下可渲染管道怎么编程。
正文:如何利用OpenGL ES 2.0可渲染管道来创建简单的三角图形第一步:创建GLSurfaceView对象
【说明】:
GLSurfaceView 是为OpenGL专门定义的一个类,它继承自SurfaceView。因此包含SurfaceView的一切特性。同时,它也增加了用于OpenGL特有的一些方法,比如:setRender(Render render)等。
想探究GLSurfaceView渲染过程的同学,可以去官网看看。
获取GLSurfaceView对象的途径有2种,可以直接在xml中写,也可以在代码中new。
本文我们选用后一种,eg:
public class SunnyOpenGLActivity extends FragmentActivity {
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
setContentView(mGLSurfaceView);
}
@Override
protected void onResume() {
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mGLSurfaceView.onPause();
}
private void initView() {
mGLSurfaceView = new GLSurfaceView(this);
}
第二步:自定义Render类实现GLSurfaceView.Render接口
这一步是最重要的一步,因为着色器的生成,矩阵的变换,纹理的加载等都是在这里实现的,GLSurfaceView.Render有三个重要的待实现的接口:
onSurfaceCreated(GL10 glUnused,EGLConfig config);
onSurfaceChanged(GL10 glUnused,int width,int height);
onDrawFrame(GL10 glUnused);
以上三个方法回调的时机分别如下:
方法 | 登时调用 |
---|---|
onSurfaceCreated | 当GLSurfaceView 实例生成时回调; |
onSurfaceChanged | 当手机横/竖屏切换时回调; |
onDrawFrame | 是由系统自动回调的,它会以一定的帧频率来调用重绘View。 |
当设置GLSurfaceView的渲染模式为GLSurfaceView.RENDERMODE_CONTINUOUSLY或不设置时,系统就会主动渲染,就会回调onDrawFrame()方法,
如果设置为 RENDERMODE_WHEN_DIRTY ,则还得手动调用requestRender(),才会渲染。
自定义SunnyGLRender类如下:
public class SunnyGLRender implements GLSurfaceView.Renderer {
private int mProgram;
private int maPostionHandle;
private FloatBuffer mTriangleVB;
//定义顶点(vertex)着色器命令语句
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
" gl_Position = vPosition; \n" +
"} \n";
//片元着色器
private final String fragmentShaderCode =
"precision mediump float;\n" +
"void main(){ \n" +
" gl_FragColor = vec4 (0.63671875,0.76953125,0.22265625,1.0);\n" +//RGBA
"}\n";
//应用投影与相机视图
private int muMVPMatrixHandle;
//用于存储变换矩阵结果的总变换矩阵[4*4]
private float[] mMVPMatrix = new float[16];
//[4*4]的视图变换矩阵
private float[] mVMatrix = new float[16];
//[4*4]的投影变换矩阵
private float[] mProjMatrix = new float[16];
//[4*4]的模型变换矩阵
private float[] mMMatrix = new float[16];
public float mAngle;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//GLES20:为OpenGL ES2.0版本,相应的
//GLES30:OpenGL ES3.0
//黑色背景
GLES20.glClearColor(0.5f,0.5f,0.5f,1.0f);
//glClear:清除缓冲区标志,这里为:清除颜色缓冲及深度缓冲,把整个窗口清除为黑色背景
GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
initShapes();
int vertextShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);
//申请特定的着色器,program != 0 申请成功
mProgram = GLES20.glCreateProgram();
if(mProgram != 0 ){
GLES20.glAttachShader(mProgram,vertextShader);
GLES20.glAttachShader(mProgram,fragmentShader);
//连接着色器
GLES20.glLinkProgram(mProgram);
int[] linkStatus = new int[1];
//查看着色器连接情况
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
//连接失败
Log.e(TAG, "Could not link program: ");
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
//获取着特定着色器vPosition参数。
maPostionHandle = GLES20.glGetAttribLocation(mProgram,"vPosition");
}
}
/**
* 初始化三角形的一些参数
*/
private void initShapes() {
/**
* X,Y,Z三轴顶点坐标
* U,V也即S,T,是图片,视频等以纹理的形式加载到GLSurfaceView中时的坐标,即纹理坐标
* U,V纹理坐标是无方向的
*/
//第一种写法:顶点坐标与纹理坐标写一起
float trianlgeCoords[] = {
//X,Y,Z,U,V
-1.0f, -0.5f, 0, 0.0f, 0.5f,
1.0f, -0.5f, 0, 1.0f, 0.5f,
0.0f, 1.0f, 0, 0.5f, 0.0f
};
//第二种写法:顶点坐标与纹理坐标分开写:(因为Z为0,只考虑二维平面,)
float veticsCoor[] = {
//X,Y
-1.0f,-0.5f,
1.0f,-0.5f,
0.0f,1.0f,
};
float textureCoor[] = {
//U,V
0.0f,0.5f,
1.0f,0.5f,
0.5f,0.0,
};
//把顶点坐标存入Buffer中
ByteBuffer vbb = ByteBuffer.allocateDirect(trianlgeCoords.length * 4);
vbb.order(ByteOrder.nativeOrder());
mTriangleVB = vbb.asFloatBuffer();
mTriangleVB.put(trianlgeCoords);
mTriangleVB.position(0);
}
/**
* 加载指定着色器
* @param type
* @param shaderCode
* @return
*/
private int loadShader(int type,String shaderCode){
int shader = GLES20.glCreateShader(type);
if(shader != 0 ){
//加载着色器脚本程序(即本例的String 变量命令语句)
//我们也可以把顶点着色器与片元着色器以文件的形式加载
GLES20.glShaderSource(shader,shaderCode);
//编译着色器脚本程序
GLES20.glCompileShader(shader);
//因为GLSL不能像我们写Java或C等,可以debug或Log调试,
//这里我们就只能通过其返回的值,来判断是否编译通过,
//如果编译失败,则检查GLSL是否写正确。
//在写GLSL时,一定要检查参数类型,比如:vec2等2个浮点数的类型是否写成vec3或vec4等了,
int[] compiled = new int[1];
//查看编译结果
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
//编译失败,释放申请的着色器
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//给定窗口大小
GLES20.glViewport(0, 0, width, height);
//
float ratio = (float) width/height;
//调用此方法来计算生成透视投影矩阵
Matrix.frustumM(mProjMatrix,0,-ratio,ratio,-1,1,3,7);
//当Sucrface改变时,获取指定着色器的uMVPMatrix参数
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,"uMVPMatrix");
//设定相机的视角
//调用此方法产生摄像机9参数位置矩阵
Matrix.setLookAtM(mVMatrix,0,
0,0,-3, //相机的x,y,z坐标
0,0,0,//目标对应的x,y,z坐标
0,1.0f,1.0f//相机的视觉向量(upx,upy,upz,三个向量最终的合成向量的方向为相机的方向)
);
}
@Override
public void onDrawFrame(GL10 gl) {
// Redraw background color
//把左矩阵投影矩阵与右矩阵视图矩阵变换后的结果存储在总矩阵mMVPMatrix中
Matrix.multiplyMM(mMVPMatrix,0,mProjMatrix,0,mVMatrix,0);
//为三角形创建一个旋转动作
/*long time = SystemClock.uptimeMillis() % 4000L;
mAngle = 0.090f * ((int)time);*/
//创建一个绕x,y,z轴旋转一定角度的矩阵
Matrix.setRotateM(mMMatrix,0,mAngle,0,0,1.0f);
Matrix.multiplyMM(mMVPMatrix,0,mVMatrix,0,mMMatrix,0);
Matrix.multiplyMM(mMVPMatrix,0,mProjMatrix,0,mMVPMatrix,0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle,1,false,mMVPMatrix,0);
//把Program用到OpenGL环境中
GLES20.glUseProgram(mProgram);
//准备画三角形的数据
//为指定着色器取出的参数赋值 GLES20.glVertexAttribPointer(maPostionHandle,3,GLES20.GL_FLOAT,false,12,mTriangleVB);
//使用这个从指定着色器中取出的参数的值
GLES20.glEnableVertexAttribArray(maPostionHandle);
//开始画
GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,3);
}
}
第三步:权限声明
经过以上二步,程序还不能运行,得在AndroidManifest.xml中声明相应权限。
<!-- Tell the system this app requires OpenGL ES 2.0. -->
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
运行程序 就可以看到三角形了。
参考资料Android OpenGL ES2.0 开发文档
Android OpenGL ES 应用(二) 纹理
浅学OpenGLES2.0
google ApiDemo(这个自己看官方Demo就好了,就没有贴出下载地址了)
共同学习,写下你的评论
评论加载中...
作者其他优质文章