image.png
关于OpenGL ES Android的介绍这里略过
本文主要涉及的部分是着色器的使用。
今天的目标是做一个OpenGL ES学习的开端。就是画一个简单的三角形。暂时不考虑坐标系的矩阵变换和纹理等。只需要用顶点着色器简单的来进行描述。
这一节需要使用和认识的关键类是
GLSurfaceView
和GLSurfaceView.Render
一句话来描述就是,我们会在GLSurfaceView.Render
上进行描绘,在GLSurfaceView
中显示出来。
/**
* 判断是否支持es2.0
*
* @param context
* @return
*/
public static boolean isSupportEs2(Context context) {
//检查是否支持2.0
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (activityManager != null) {
ConfigurationInfo deviceConfigurationInfo = activityManager.getDeviceConfigurationInfo();
int reqGlEsVersion = deviceConfigurationInfo.reqGlEsVersion;
return reqGlEsVersion >= GLES_VERSION_2 || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
&& (Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK build for x86")));
} else {
return false;
}
}
接着创建一个GLSurfaceView
。并且设置其版本为2。同时设置我们自己的Render
//创建一个GLSurfaceView
glSurfaceView = new GLSurfaceView(this);
glSurfaceView.setEGLContextClientVersion(2);
//设置自己的Render.Render 内进行图形的绘制
glSurfaceView.setRenderer(new TriangleShapeRender(this));
isRenderSet = true;
setContentView(glSurfaceView);
还需要在Activity对应的生命周期内,来调用我们的GLSurfaceView
的方法
@Override
protected void onPause() {
super.onPause();
if (isRenderSet) {
glSurfaceView.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (isRenderSet) {
glSurfaceView.onResume();
}
}
因为Android中的GLSurfaceView
的操作,其实都是在GLThread
中进行。所以生命周期方法的回调也都在GLThread
线程中。所有OpenGL的操作也都需要在该线程中。
接下来转到Render
的实现类里面来。先关注需要实现的生命周期方法。
/**
* 注意:
* 因为我们是使用g20来进行编程,需要要注意导包
*
* 主要在Render类内,完成对应的绘制.
* 对应的生命周期的回调
* -> onSurfaceCreated
* -> onSurfaceChanged
* -> onDrawFrame
*
* Created by a2957 on 2018/5/3.
*/
public class ViewGLRender implements GLSurfaceView.Renderer {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//0.简单的给窗口填充一种颜色
GLES20.glClearColor(0.0f,0.0f,0.0f,0.0f);
//在创建的时候,去创建这些着色器
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//在窗口改变的时候调用
GLES20.glViewport(0,0,width,height);
}
@Override
public void onDrawFrame(GL10 gl) {
//0.glClear()的唯一参数表示需要被清除的缓冲区。当前可写的颜色缓冲
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
}
如上面代码所示,对应的生命周期方法,对应了各个状态
GLSurfaceView
创建的时机。按照惯用思维,在这里进行一些初始化的操作。GLSurfaceView
改变的时机。如代码所示,初始化GL的ViewPort
我们需要熟悉编写着色器代码的套路。 之所以说是套路,因为这些步骤都是类似的。
1. 编写着色器的glsl
我们先简单的写一下顶点着色器和片元着色器的代码。这些代码具体是为什么这样写。我们在这里先不关注。我们先熟悉一下流程。
在assets
中创建对应的文件。代码内容如下
aPosition
来描述位置。(ps:像是废话)
2. 在onSurfaceCreated
方法内初始化
注意OpenGL的操作,都必须在
GLThread
中进行。生命周期方法的回调也都在这个线程中
program
,将得到的id绑定上,并链接program
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { super.onSurfaceCreated(gl, config); //0.先从Asset中得到着色器的代码 String vertexShaderCode = GLESUtils.readAssetShaderCode(context, VERTEX_SHADER_FILE); String fragmentShaderCode = GLESUtils.readAssetShaderCode(context, FRAGMENT_SHADER_FILE); //1.得到之后,进行编译。得到id int vertexShaderObjectId = GLESUtils.compileShaderCode(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShaderObjectId = GLESUtils.compileShaderCode(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); //3.继续套路。取得到program mProgramObjectId = GLES20.glCreateProgram(); //将shaderId绑定到program当中 GLES20.glAttachShader(mProgramObjectId, vertexShaderObjectId); GLES20.glAttachShader(mProgramObjectId, fragmentShaderObjectId); //4.最后,启动GL link program GLES20.glLinkProgram(mProgramObjectId); }
整体的流程就如上面代码注释的一样。
创建program=> 将shaderId绑定到program当中=> 最后,启动GL link program
这样着色器的套路就基本确定下来了。
上面编写的顶点着色器中,我们定义了aPosition
的属性。就相当于我们将在OpenGL中定义了一个存储的点。接下来,我们就会将这个点来存储我们定义的形状信息。来显示出形状。
OpenGL中的坐标系是从[-1,1]。 我们先用一组数组的坐标系,来描述我们的三角形
//顶点的坐标系
private static float TRIANGLE_COORDS[] = {
//Order of coordinates: X, Y, Z
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
三角形的归一化坐标.png
我们还会定义一些常量,来帮助我们操作。常量就如注释说明
//在数组中,一个顶点需要3个来描述其位置,需要3个偏移量
private static final int COORDS_PER_VERTEX = 3;
private static final int COORDS_PER_COLOR = 0;
//在数组中,描述一个顶点,总共的顶点需要的偏移量。这里因为只有位置顶点,所以和上面的值一样
private static final int TOTAL_COMPONENT_COUNT = COORDS_PER_VERTEX+COORDS_PER_COLOR;
//一个点需要的byte偏移量。
private static final int STRIDE = TOTAL_COMPONENT_COUNT * Constant.BYTES_PER_FLOAT;
ByteBuffer .allocateDirect
方法onDrawFrame
中调用绘制@Override
public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
//0.先使用这个program?这一步应该可以放到onCreate中进行
GLES20.glUseProgram(mProgramObjectId);
//1.根据我们定义的取出定义的位置
int vPosition = GLES20.glGetAttribLocation(mProgramObjectId, A_POSITION);
//2.开始启用我们的position
GLES20.glEnableVertexAttribArray(vPosition);
//3.将坐标数据放入
GLES20.glVertexAttribPointer(
vPosition, //上面得到的id
COORDS_PER_VERTEX, //告诉他用几个偏移量来描述一个顶点
GLES20.GL_FLOAT, false,
STRIDE, //一个顶点需要多少个字节的偏移量
mVertexFloatBuffer);
//取出颜色
int uColor = GLES20.glGetUniformLocation(mProgramObjectId, U_COLOR);
//开始绘制
//设置绘制三角形的颜色
GLES20.glUniform4fv(
uColor,
1,
TRIANGLE_COLOR,
0
);
//绘制三角形.
//draw arrays的几种方式 GL_TRIANGLES三角形
//GL_TRIANGLE_STRIP三角形带的方式(开始的3个点描述一个三角形,后面每多一个点,多一个三角形)
//GL_TRIANGLE_FAN扇形(可以描述圆形)
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, VERTEX_COUNT);
//禁止顶点数组的句柄
GLES20.glDisableVertexAttribArray(vPosition);
}
use
我们之前得到的programObjectId
image.png
总结一下,我们从这第一章节的内容了解到了下面这些使用的知识点:
未知道的: 坐标矩阵的变化。和纹理等。