初始化GLSurfaceView

接下来我们将尝试使用OpenGLES绘制一个三角形,首先创建一个Android Studio Project,OpenGLESTriangles
接下来我们来学习初始化Open GL ESMainActivity.java内部代码如下:

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
  }

在Android 4.0以前我们进行Open GL渲染主要依赖于GLSurfaceView这个类,GLSurfaceView实际上创建了一个自持的独立窗体,在View层开放一个接口用于展示OpenGL Surface中的内容,因此它不像普通View一样对旋转,动画等支持的那么良好。在Android 4.0后添加了TextureView用于展示OpenGL Surface,这个View并不像GLSurfaceView一样需要创建一个单独的窗体,这里我们先学习使用GLSurfaceView
MainActivity.java中添加如下代码,用于声明GLSurfaceView对象以及记录当前设置是否支持Open GLES2.0:

private GLSurfaceView mGLSurfaceView;
  private boolean mRendererSet = false;

随后我们需要判断当前设置是否支持Open GL ES 2.0,如果支持,就初始化GLSurfaceView,如果不支持,就弹出Toast告知用户,具体代码如下:

/**
   * help to judge whether device support open gl es version 2.0
   * @param context
   * @return true:support 2.0 version,false:do not support 2.0 version
   */
  public static boolean isSupportOpenGLES2(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
    return configurationInfo.reqGlEsVersion >= 0x20000 || (
        VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1 && (
            Build.FINGERPRINT.startsWith(FINGERPRINT_GENERIC) || Build.FINGERPRINT
                .startsWith(FINGERPRINT_UNKNOWN) || Build.MODEL.contains(MODEL_GOOGLE)
                || Build.MODEL.contains(MODEL_EMULATOR) || Build.MODEL.contains(MODEL_SDK)));
  }
@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mGLSurfaceView = new GLSurfaceView(this);
    if (DeviceInfor.isSupportOpenGLES2(this)){
      mGLSurfaceView.setEGLContextClientVersion(2);
      mGLSurfaceView.setRenderer(new TriangleRenderer(this));
      mRendererSet = true;
    }else {
      Toast.makeText(this, "This Device does not support open gl es 2.0 version!", Toast.LENGTH_SHORT).show();
      return;
    }
    setContentView(mGLSurfaceView);
  }

由于GLSurfaceView是连接Open GL ESAndroid View之间的桥梁,所以它和Android组件一样,拥有完整的生命周期函数,在初始化后需要管理它的生命周期,代码如下:

@Override
  protected void onPause() {
    super.onPause();
    if (mRendererSet){
      mGLSurfaceView.onPause();
    }
  }

  @Override
  protected void onResume() {
    super.onResume();
    if (mRendererSet){
      mGLSurfaceView.onResume();
    }
  }

在完成基本的GLSurfaceView创建后,我们来了解一些关于Open Gl ES的基础知识。

Open Gl ES基础

1.在Open Gl ES中只支持三种基本图形,点,线和三角形,所有复杂图形均由这三者绘制而成。
2.在Open Gl ES中绘制图形依赖于渲染器(Renderer),在渲染器中完成图形渲染。在渲染器中使用着色器对图形的每一个位置进行着色,在Open Gl ES中有两类着色器,顶点着色器(VertexShader)和片段着色器(FragmentShader),在渲染器中通过加载,编译着色器,链接程序来完成图形渲染。
按照上文所说我们接下来定义三角形的顶点信息,以及着色器文件。

三角形与着色器(Shader)

接下来为我们的项目添加渲染器(Renderer),代码如下:

public class TriangleRenderer implements Renderer {

  public static final String TAG = "TriangleRenderer";
  private Context mContext;

  public TriangleRenderer(Context context) {
    mContext = context;
  }

  @Override
  public void onSurfaceCreated(GL10 gl, EGLConfig config) {

  }

  @Override
  public void onSurfaceChanged(GL10 gl, int width, int height) {

  }

  @Override
  public void onDrawFrame(GL10 gl) {

  }
}

在Open Gl ES中,其使用的是世界坐标系(即绝对坐标系),附图如下:

android shape 正小三角 android三角形_ide


在该坐标系中,坐标原点位于屏幕中心,XYZ三轴符合右手法则,我们定义三角形的顶点信息如下(PS:不考虑Z轴):

float[] vertexTrangles = {
      -1.0f, -1.0f,
      1.0f, -1.0f,
      0f, 1.0f
  };

定义存储三角形的Buffer信息如下:

private static final int BYTES_PER_FLOAT = 4;//每个Float占四个字节
private FloatBuffer vertexData;//存储顶点数据的Buffer
private static final int POSITION_COMPONENT_COUNT = 2;//代表点坐标的个数

将上述代码写入渲染器中,在渲染器的构造函数中为三角形顶点初始化缓存大小,代码如下:

vertexData = ByteBuffer.allocateDirect(vertexTrangles.length * BYTES_PER_FLOAT)
        .order(ByteOrder.nativeOrder()).asFloatBuffer();
    vertexData.put(vertexTrangles);

随后编写Open Gl ES着色器文件,在res目录下新建raw文件夹,并新建simple_vertex_shader.glsl文件,内容如下:

attribute vec4 a_Position;
#定义一个点的位置信息
void main(){
   gl_Position = a_Position;
}

新建simple_fragement_shader.glsl文件,内容如下:

#数据类型
precision mediump float;
#片段着色器中的色值
uniform vec4 u_Color;

void main(){
   gl_FragColor = u_Color;
}

加载编译Shader,链接程序

从资源文件中读取glsl文件转化成String,工具函数如下:

/**
   * read glsl file from resource
   * @param context
   * @param resourceId example R.raw.vertex_shader
   * @return
   */
  public static String readTextFileFromResource(Context context,int resourceId){
    StringBuilder body = new StringBuilder();
    try {
      InputStream inputStream = context.getResources().openRawResource(resourceId);
      InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
      BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
      String nextLine;
      while ((nextLine = bufferedReader.readLine()) != null){
        body.append(nextLine);
        body.append('\n');
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    return body.toString();
  }

编译着色器文件,代码如下:

public static int compileVertexShader(String shaderCode) {
    return compileShader(GL_VERTEX_SHADER, shaderCode);
  }

  public static int compileFragmentShader(String shaderCode) {
    return compileShader(GL_FRAGMENT_SHADER, shaderCode);
  }

  private static int compileShader(int type, String shaderCode) {
    int shaderObjectId = glCreateShader(type);
    if (shaderObjectId == 0) {
      return 0;
    }
    glShaderSource(shaderObjectId, shaderCode);
    glCompileShader(shaderObjectId);
    final int[] compileStatus = new int[1];
    glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS, compileStatus, 0);
    if (compileStatus[0] == 0) {
      glDeleteShader(shaderObjectId);
      return 0;
    }
    return shaderObjectId;
  }

随后我们在渲染器中完成程序的构建使用,核心代码如下:

@Override
  public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    gl10.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    //加载Shader file
    String vertexShaderSource = TextResourceReader
        .readTextFileFromResource(mContext, R.raw.simple_vertex_shader);
    String fragmentShaderSource = TextResourceReader
        .readTextFileFromResource(mContext, R.raw.simple_fragment_shader);
        //编译Shader
    int vertexShader = ShaderHelper.compileVertexShader(vertexShaderSource);
    int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);
    //链接程序
    mProgramId = ShaderHelper.linkProgram(vertexShader, fragmentShader);
    //验证程序
    ShaderHelper.validateProgram(mProgramId);
    //使用程序
    glUseProgram(mProgramId);
    //查找颜色片元位置
    uColorLocation = glGetUniformLocation(mProgramId, U_COLOR);
    //查找顶点片元位置
    aPositionLocation = glGetAttribLocation(mProgramId, A_POSITION);
    //重置数据集位置
    vertexData.position(0);
    //读取顶点信息并渲染
    glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT, false, 0,
        vertexData);
    glEnableVertexAttribArray(aPositionLocation);
  }

以上代码中的链接程序函数源码如下:

public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
    int programId = glCreateProgram();
    if (programId == 0) {
      return 0;
    }
    //完成着色器和程序之间的绑定
    glAttachShader(programId, vertexShaderId);
    glAttachShader(programId, fragmentShaderId);
    //链接程序
    glLinkProgram(programId);
    final int[] linkStatus = new int[1];
    glGetProgramiv(programId, GL_LINK_STATUS, linkStatus, 0);
    if (linkStatus[0] == 0) {
      glDeleteProgram(programId);
      return 0;
    }
    return programId;
  }

  public static boolean validateProgram(int programId) {
    glValidateProgram(programId);
    final int[] validateStatus = new int[1];
    glGetProgramiv(programId, GL_VALIDATE_STATUS, validateStatus, 0);
    return validateStatus[0] != 0;
  }

绘制三角形

修改渲染器中代码如下:

@Override
  public void onSurfaceChanged(GL10 gl10, int i, int i1) {
    gl10.glViewport(0, 0, i, i1);
  }

  @Override
  public void onDrawFrame(GL10 gl10) {
    gl10.glClear(gl10.GL_COLOR_BUFFER_BIT);
    //传入片元颜色值,可以通过改变最后四个参数值参看片元渲染后的颜色变化
    glUniform4f(uColorLocation, 1.0f, 1.0f, 1.0f, 1.0f);
    //绘制三角形
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
  }

运行截图如下:

android shape 正小三角 android三角形_ide_02