我是靠谱客的博主 风趣枕头,最近开发中收集的这篇文章主要介绍OpenGL ES 在Android平台的应用实践(2),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

       第一篇文章讲述了OpenGL ES 涉及到的一些原理,相信初学者应该对OpenGL ES有了一定的初步认识与了解。下面我们讲解如何进行OpenGL ES的编程实现。

       先来一个简单的例子,在手机的屏幕上绘制一个三角形,三角形的三个顶点采用不同的颜色。

1.在AndroidManifest.xml中添加对OpenGL ES 2.0的支持。

<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />

  同时在自定义的Activity中判断手机硬件是否支持OpenGL ES 2.0,创建GLSurfaceView实现对OpenGL ES API的绘制。

public class TriangleActivity extends Activity
{
  private GLSurfaceView mGLSurfaceView;
@Override
  public void onCreate(Bundle savedInstanceState)
 {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
if (supportsEs2)
{
mGLSurfaceView.setEGLContextClientVersion(2);
mGLSurfaceView.setRenderer(new MyRenderer());
}
else
{
return;
}
setContentView(mGLSurfaceView);
  }
@Override
  protected void onResume() 
  {
super.onResume();
mGLSurfaceView.onResume();
  }
@Override
  protected void onPause() 
  {
super.onPause();
mGLSurfaceView.onPause();
  }
}

2.实现自定义的渲染器,继承GLSurfaceView.Renderer。

public class MyRenderer implements GLSurfaceView.Renderer
{
  private float[] mModelMatrix = new float[16];//模型矩阵
private float[] mViewMatrix = new float[16];//视图矩阵
private float[] mProjectionMatrix = new float[16];//投影矩阵
private float[] mMVPMatrix = new float[16];//总矩阵
  private final FloatBuffer mVertices;//存储顶点坐标数据
  private int mMVPMatrixHandle;//对应顶点着色器中的总矩阵句柄
  private int mPositionHandle;//坐标句柄
  private int mColorHandle;//颜色句柄
private final int mBytesPerFloat = 4;//每一个float占4个字节
  private final int mStrideBytes = 7 * mBytesPerFloat;//每一个顶点的步长,下文中一个顶点包括X,Y,Z,R,G,B,A所以这里是7
  private final int mPositionOffset = 0;//顶点偏移量
private final int mPositionDataSize = 3;//一个顶点数据大小
private final int mColorOffset = 3;//颜色偏移量,即一个顶点数据,位置为3开始表示的是颜色值
private final int mColorDataSize = 4;//颜色占据的数据大小
public MyRenderer()
{
final float[] verticesData = {
// X, Y, Z,
// R, G, B, A
-0.5f, -0.25f, 0.0f,
        1.0f, 0.0f, 0.0f, 1.0f,//第一个顶点
0.5f, -0.25f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f,//第二个顶点
0.0f, 0.5f, 0.0f,//第三个顶点
        0.0f, 1.0f, 0.0f, 1.0f
    };
    //GPU不能直接访问java虚拟机内存,所以需要将java内存转化为本地系统内存,还要考虑存储时的大小端模式,这里直接指定采用本地存储顺序即可
mVertices = ByteBuffer.allocateDirect(verticesData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
    mVertices.put(verticesData).position(0);//位置置0,从开始的位置取数据
  }
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
{
    //这里可以调用OpenGL ES API进行绘制操作了,下面一行仅为清屏操作,设置屏幕为某一颜色
    GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
    //设置观察点的位置,即摄像机的位置
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = 1.5f;
//设置观察物体的位置
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = -5.0f;
    //设置摄像机的上方向,类比人看某一物体时,头顶方向即为上方向
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.0f;
    //通过下面这个函数设置视图矩阵
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
    //编写顶点着色器,一个顶点执行一次
final String vertexShader =
      "uniform mat4 u_MVPMatrix;      n" //总矩阵,与mMVPMatrixHandle对应
      + "attribute vec4 a_Position;     n" //顶点坐标,与mPositionHandle对应
      + "attribute vec4 a_Color;        n" //颜色值,与mColorHandle对应
      + "varying vec4 v_Color;          n" //由顶点着色器向片元着色器传递颜色值的中间变量
+ "void main()
n"
+ "{
n"
      + "   v_Color = a_Color;          n"
      + "   gl_Position = u_MVPMatrix* a_Position;n" //计算最终的顶点坐标
      + "}                              n";
      //编写片元着色器,一个片元执行一次
final String fragmentShader =
      "precision mediump float;       n" //指定为中等精度
+ "varying vec4 v_Color;
n" //接收来自顶点着色器传递过来的颜色变量
+ "void main()
n"
+ "{
n"
      + "   gl_FragColor = v_Color;     n" //设置片元最后的颜色
+ "}
n";
    //创建顶点着色器
int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
if (vertexShaderHandle != 0)
{
//加载顶点着色器的脚本到创建的顶点着色器中,并进行编译
GLES20.glShaderSource(vertexShaderHandle, vertexShader);
GLES20.glCompileShader(vertexShaderHandle);
final int[] compileStatus = new int[1];
      //检查编译的结果是否正确,因为GPU的执行无法像CPU执行一样通过打印日志来获取执行结果,所以提供了获取执行结果的函数
GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0)
{
GLES20.glDeleteShader(vertexShaderHandle);
vertexShaderHandle = 0;
}
    }
if (vertexShaderHandle == 0)
{
      throw new RuntimeException("创建顶点着色器错误");
}
    //创建片元着色器
int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
if (fragmentShaderHandle != 0)
{
      //加载片元着色器的脚本到创建的片元着色器中,并进行编译
GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);
GLES20.glCompileShader(fragmentShaderHandle);
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0)
{
GLES20.glDeleteShader(fragmentShaderHandle);
fragmentShaderHandle = 0;
}
}
if (fragmentShaderHandle == 0)
{
      throw new RuntimeException("创建片元着色器错误");
    }
    //创建一个着色器程序
int programHandle = GLES20.glCreateProgram();
if (programHandle != 0)
{
      //将顶点着色器和片元着色器链接到执行程序中
GLES20.glAttachShader(programHandle, vertexShaderHandle);
GLES20.glAttachShader(programHandle, fragmentShaderHandle);
GLES20.glLinkProgram(programHandle);
final int[] linkStatus = new int[1];
//检查链接的结果是否出错
GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0)
{
GLES20.glDeleteProgram(programHandle);
programHandle = 0;
}
}
if (programHandle == 0)
{
throw new RuntimeException("创建程序失败");
}
    //获取顶点着色器中相应属性的句柄值
mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color");
    //需要指定使用哪一个着色器程序来执行绘制操作,一个应用程序中可以有多个着色器程序
GLES20.glUseProgram(programHandle);
}
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height)
{
//指定视图窗口的大小,即在手机上需要显示的大小
GLES20.glViewport(0, 0, width, height);
    //计算宽度比例,避免屏幕方向变化,引起比例失调,导致失真
    final float ratio = (float) width / height;
    //指定近平面的值
final float left = -ratio;
final float right =ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 1.0f;
final float far = 10.0f;
//设置透视投影矩阵,设置正交投影矩阵函数为Matrix.orthoM()
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
}
@Override
public void onDrawFrame(GL10 glUnused)
{
//调用OpenGL ES API 清除深度缓存和颜色缓存,避免之前的数据对结果造成影响
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
long time = SystemClock.uptimeMillis() % 10000L;
float angle = (360.0f / 10000.0f) * ((int) time);
Matrix.setIdentityM(mModelMatrix, 0);
        //随着时间的流逝,变换三角形绕着X轴的旋转角度,三角形会绕着X轴不断的旋转
        Matrix.rotateM(mModelMatrix, 0, angle, 1, 0, 0);
drawTriangle(mVertices);
}
private void drawTriangle(final FloatBuffer triangleData)
{
triangleData.position(mPositionOffset);
    //将三角形的顶点数据传给着色器中相应的顶点句柄
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, mStrideBytes, triangleData);
    GLES20.glEnableVertexAttribArray(mPositionHandle);//使能顶点数组
    
    triangleData.position(mColorOffset);
    //将颜色的值传给顶点着色器中颜色对应的句柄
GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false, mStrideBytes, triangleData);
GLES20.glEnableVertexAttribArray(mColorHandle);
    //通过矩阵相乘,计算最终的总矩阵
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
//将计算后的总矩阵传给顶点着色器中总矩阵对应的句柄
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
    //开始绘制三角形,绘制方式为GLES20.GL_TRIANGLES模式,第一篇文章已经指出
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
}

 以上每一行的作用我都在后面做了注释,希望可以说的清楚,不明白的朋友可以留言。

   

 

最后

以上就是风趣枕头为你收集整理的OpenGL ES 在Android平台的应用实践(2)的全部内容,希望文章能够帮你解决OpenGL ES 在Android平台的应用实践(2)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部