创建一个简单的OpenGl 启动程序
1 . 创建一个Activiy叫做 AirHockey,并且使用GLSurfaceView作为布局。GLSurfaceView 是一个特殊的类,可以用来初始化话OpenGL,使是初始化过程变得简单一些。代码如下:
private boolean renderSet =false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glSurfaceView =new GLSurfaceView(this);
//setContentView(R.layout.activity_first_open_glproject);
final ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
assert activityManager !=null;
final ConfigurationInfo configuration = activityManager.getDeviceConfigurationInfo();
final boolean supportsEs2 = configuration.reqGlEsVersion >=0x20000;
Log.e(TAG,supportsEs2+"");
if(supportsEs2){
glSurfaceView.setEGLContextClientVersion(2);
glSurfaceView.setRenderer(new AirHockeyRenderer(this));
renderSet =true;
}else{
Toast.makeText(this,"not support OpenGL Es 2.0",Toast.LENGTH_LONG).show();
return ;
}
setContentView(glSurfaceView);
}
@Override
protected void onResume() {
super.onResume();
if(renderSet){
glSurfaceView.onResume();
}
}
@Override
protected void onPause() {
super.onPause();
if(renderSet){
glSurfaceView.onPause();
}
}
AirHockeyRenderer 类是后面要说到的渲染类。
glSurfaceView.setEGLContextClientVersion(2); 配置了OpenGL 的版本为2
在onPause 和 onResume里面要处理oengl ,否则会导致页面切换的时候的崩溃。
OpenGL 会在单独的线程里面调用渲染器的方法(就是Render里面的方法)。如果主线程调用GLSurfaceView 实例可以调用queueEvent()方法传入一个Runnable给后台渲染线程,渲染线程可以调用Activity的runOnUIThread()来传递事件(event)给主线程。
2.创建Render类AirHockeyRenderer
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES20.GL_LINES;
import static android.opengl.GLES20.GL_TRIANGLES;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glDrawArrays;
import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glUniform4f;
import static android.opengl.GLES20.glUseProgram;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.glVertexAttribPointer;
import static javax.microedition.khronos.opengles.GL10.GL_POINTS;
public class AirHockeyRendererimplements GLSurfaceView.Renderer {
private static final int POSITION_COMPONENT_COUNT =2;
private static final int BYTES_PER_FLOAT =4;
private final FloatBuffervertexData;
private int program;
private static final StringU_COLOR ="u_Color";
private int uColorLocation;
private static final StringA_POSIONT="a_Position";
private int aPositionLocation;
private ContextmContext;
public AirHockeyRenderer(Context context) {
mContext = context;
float[] tableVerticesWithTriangles = {
//Triagle1
-0.5f,-0.5f,
0.5f,0.5f,
-0.5f,0.5f,
//Triagle2
-0.5f,-0.5f,
0.5f,-0.5f,
0.5f,0.5f,
// Line 1
-0.5f,0f,
0.5f,0f,
// Mallets
0f,-0.25f,
0f,0.25f
};
vertexData = ByteBuffer.allocateDirect(tableVerticesWithTriangles.length *BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexData.put(tableVerticesWithTriangles);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
glClearColor(0.0f,0.0f,0.0f,0.0f);
//加载着色器代码
String vertexShaderSource = TextResourceReader.readTextFileFromResource(mContext,R.raw.simple_vertex_shader);
String fragmentShaderSource = TextResourceReader.readTextFileFromResource(mContext,R.raw.simple_fragment_shader);
//编译着色器代码
int vertexShader = ShaderHelper.compileVertextShader(vertexShaderSource);
int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);
//链接着色器程序
program = ShaderHelper.linkProgram(vertexShader,fragmentShader);
ShaderHelper.validateProgram(program);
//使用程序
glUseProgram(program);
uColorLocation =glGetUniformLocation(program,U_COLOR);
aPositionLocation =glGetAttribLocation(program,A_POSIONT);
//程序和具体的数据的指定
vertexData.position(0);
glVertexAttribPointer(aPositionLocation,POSITION_COMPONENT_COUNT,GL_FLOAT,false,0,vertexData);
glEnableVertexAttribArray(aPositionLocation);
}
@Override
public void onSurfaceChanged(GL10 gl,int width,int height) {
}
@Override
public void onDrawFrame(GL10 gl) {
glClear(GL_COLOR_BUFFER_BIT);
//绘制三角形
glUniform4f(uColorLocation,1.0f,1.0f,1.0f,1.0f);
glDrawArrays(GL_TRIANGLES,0,6);
//绘制分割线
glUniform4f(uColorLocation,1.0f,0.0f,0.0f,1.0f);
glDrawArrays(GL_LINES,6,2);
//绘制点
glUniform4f(uColorLocation,0.0f,0.0f,1.0f,1.0f);
glDrawArrays(GL_POINTS,8,1);
glUniform4f(uColorLocation,1.0f,0.0f,1.0f,1.0f);
glDrawArrays(GL_POINTS,9,1);
}
}
Renderer 是渲染器类,主要与三个方法:
onSurfaceCreated,当surface被创建的时候调用,当程序被唤醒,或者从其他activity切换回来的时候也有可能被调用,所以说这个方法可能会被调用多次。
onSurfaceChanged,当surface被创建以后,surface每次尺寸变化的时候,都会被调用。比如横竖屏切换的时候,surface尺寸就会发生变化。
onDrawFrame,每绘制一帧的时候,就会调用这个方法,这个方法一定要绘制一点东西,就是是清屏幕。
下面讲一下渲染类里面的具体操作。
AirHockeyRenderer 的构造函数里面,我们要讲我们自己定义的二维定点数据传输到native层。然后后面可以使用FloatBuffer来操作native层里面的顶点数据。
onSurfaceCreated,里面增加glClearColor(0.0f,0.0f,0.0f,0.0f);用于设置清空屏幕的颜色。然后我们要在这方法里面编写着色器程序。为什么还有着色器其程序,请参考文档的参考书籍。
TextResourceReader 为着色器加载程序。使用java 的io操作。
ShaderHelper 为着色器编译工具类。包括编译和链接能力。
compileShader 编译着色器程序主要是三步。
final int shaderObjectId =glCreateShader(type);//创建着色器对象
glShaderSource(shaderObjectId,shaderCode);//上传做着色器代码
glCompileShader(shaderObjectId);//编译
linkProgram 连接着色器程序也是分为三步:
int programObjectId =glCreateProgram();//新建着色器程序
glAttachShader(programObjectId,vertexShaderId);//附上顶点着色器
glAttachShader(programObjectId,fragmentShaderId);//附上片段着色器
glLinkProgram(programObjectId);//链接程序,把这些着色器联合起来
validateProgram 是一个程序检测方法,可以自行参考书籍理解。
最后,这些着色器程序准备好了,我们要调用
glUseProgram(program); 告诉OpenGL 在屏幕上绘制东西的时候要使用这里定义的程序。
接下来将怎么在onDrawFrame绘画方法里面使用程序绘画曲棍球桌面。
在绘画之前,首先要提前获得uniform的位置。当OpenGL 把着色器链接成一个程序的时候。它实际是用一个位置变化把片段着色器里面定义的每个uniform都关联起来了。这些位置编号可以用来给着色器发送数据。
uColorLocation =glGetUniformLocation(program,U_COLOR);
和uniform一样,在使用定点数据之前,我们首先要获得其位置。我们可以通过glBindAttribLocation由我们自己给他分配位置编号。也可以让OpenGL自动给这些属性分配位置编号。在我们程序里面,使用自动分配的方式。这样使代码更加容易管理。
aPositionLocation =glGetAttribLocation(program,A_POSIONT);
接着我们就要使用这些属性。然后关联器着色器和我们定义的定点数据。
vertexData.position(0);
glVertexAttribPointer(aPositionLocation,POSITION_COMPONENT_COUNT,GL_FLOAT,false,0,vertexData);
关联后,调用glEnableVertexAttribArray(aPositionLocation); 来告诉OpengGL 从哪里取需要的数据。
最后一步,就是绘画了。
glUniform4f(uColorLocation,1.0f,1.0f,1.0f,1.0f);//指定了使用的片段着色器,和颜色
glDrawArrays(GL_TRIANGLES,0,6);//指定了形状,和顶点列表的开始位置和个数。
这是绘制一个三角形,
glUniform4f(uColorLocation,1.0f,0.0f,0.0f,1.0f);
glDrawArrays(GL_LINES,6,2);
这是一个分割线
glUniform4f(uColorLocation,0.0f,0.0f,1.0f,1.0f);
glDrawArrays(GL_POINTS,8,1);
需要注意的是,为了让点能够看得见,我们在定点着色器程序里面gl_PointSize =10.0;这样让指定了点的大小。
参考书籍:OpenGL ES 应用开发实践指南(Kevin Brothaler)
github源码: https://github.com/SnailCoderGu/OpenGL