Android openGl 绘制简单图形的实现示例

学习五部曲,弄清楚5个W一个H(when(什么时候使用)、where(在哪个地方使用?)、who(对谁使用)、what(是个什么东西)、why(为什么要这么用?).一个H即:how(到底该怎么用?)),基本的概念篇主要围绕这几个方面进行分析

1. What? openGl是什么?openGl ES又是什么?

相信很多人从事开发的都或多或少听到过有关OpenGl这个东西,但是平时用的少,只知道有这么个东西,而且学起来不简单,所以大多数人都不能讲出个个所以然来。

官方对OpenGl的描述为:

OpenGL(Open Graphics Library开发图形接口)是一个跨平台的图形API,用于指定3D图形处理硬件中的标准软件接口。

OpenGl的前身是SGI公司为其图形工作站开发的IRIS GL,后来因为IRIS GL的移植性不好,所以在其基础上,开发出了OpenGl。OpenGl一般用于在图形工作站,PC端使用,由于性能各方面原因,在移动端使用OpenGl基本带不动。为此,Khronos公司就为OpenGl提供了一个子集,OpenGl ES(OpenGl for Embedded System)

什么是OpenGl ES呢?

OpenGl ES是免费的跨平台的功能完善的2D/3D图形库接口的API,是OpenGL的一个子集。

移动端使用到的基本上都是OpenGl ES,当然Android开发下还专门为OpenGl提供了android.opengl包,并且提供了GlSurfaceView,GLU,GlUtils等工具类。

2. How? Android中的openGL 如何使用?

在了解OpenGl的使用之前,我们需要了解两个基本类别的Android框架:GlSurfaceView和GlSurfaceView.Renderer

3. GlSurfaceView是什么? GLSurfaceView的作用是什么? GLSurfaceView如何使用?

GlSurfaceView从名字就可以看出,它是一个SurfaceView,看源码可知,GlSurfaceView继承自SurfaceView。并增加了Renderer.它的作用就是专门为OpenGl显示渲染使用的。

GLSurfaceView的使用方法:
可以通过创建的实例使用这个类,并增加你的Renderer.

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GLSurfaceView glSurfaceView = new GLSurfaceView(this);
    glSurfaceView.setRenderer(new GLSurfaceView.Renderer() {
      @Override
      public void onSurfaceCreated(GL10 gl, EGLConfig config) {

      }

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

      }

      @Override
      public void onDrawFrame(GL10 gl) {

      }
    });
    setContentView(glSurfaceView);
  }

4. GlSurfaceView.Renderer是什么?GLSurfaceView.Renderer的作用?GLSurfaceView.Renderer的用法?

该接口定义了用于绘制在图形所需的方法GLSurfaceView。你必须提供这个接口作为一个单独的类的实现,并将其连接到您的GLSurfaceView使用实例 GLSurfaceView.setRenderer()。如上面的代码所示。作用就是提供各种渲染方法,OpenGl的渲染操作均在此接口中实习。下面说下实现该接口的方法含义:

  • onSurfaceCreated():系统调用这个方法一次创建时GLSurfaceView。使用此方法来执行只需要发生一次的操作,比如设置OpenGL的环境参数或初始化的OpenGL图形对象。
  • onDrawFrame():系统调用上的每个重绘此方法GLSurfaceView。使用此方法作为主要执行点用于绘制(和重新绘制)的图形对象。
  • 系统调用此方法时的GLSurfaceView几何形状的变化,包括尺寸变化GLSurfaceView或设备屏幕的取向。例如,当设备从纵向变为横向的系统调用这个方法。使用此方法可以在变化做出反应GLSurfaceView容器。

介绍完了GlSurfaceView和GlSurfaceView.renderer之后,接下来说下如何使用GlSurfaceView;
1. 创建一个GlSurfaceView
2. 为这个GlSurfaceView设置渲染
3. 在GlSurfaceView.renderer中绘制处理显示数据

5. OpenGl的简单使用实例(绘制一个三角形)

在使用OpenGl之前,需要在AndroidManifest.xml中设置OpenGl的版本:这里我们使用的是OpenGl ES 2.0,所以需要添加如下说明:

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

使用GLSufaceView(上面有介绍)

  • 具体在GlSurfaceView.Renderer中的绘制步骤:
  • 设置视图展示窗口(viewport) :在onSurfaceChanged中调用GLES20.glViewport(0, 0, width, height);
  • 创建图形类,确定好顶点位置和图形颜色,将顶点和颜色数据转换为OpenGl使用的数据格式
  • 加载顶点找色器和片段着色器用来修改图形的颜色,纹理,坐标等属性
  • 创建投影和相机视图来显示视图的显示状态,并将投影和相机视图的转换传递给着色器。
  • 创建项目(Program),连接顶点着色器片段着色器。
  • 将坐标数据传入到OpenGl ES程序中:

使用OpenGl修改背景颜色

创建一个GlSurfaceView,并为其设置渲染OneGlRenderer;

public class OneGlSurfaceView extends GLSurfaceView {
  private final OneGlRenderer mRenderer;
  public OneGlSurfaceView(Context context) {
    super(context);
    // Create an OpenGL ES 2.0 context
    setEGLContextClientVersion(2);

    mRenderer = new OneGlRenderer();

    // Set the Renderer for drawing on the GLSurfaceView
    setRenderer(mRenderer);
  }
}

实现渲染接口

public class OneGlRenderer implements GLSurfaceView.Renderer {
  public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    // Set the background frame color
    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  }

  public void onDrawFrame(GL10 unused) {
    // Redraw background color
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
  }

  public void onSurfaceChanged(GL10 unused, int width, int height) {
    GLES20.glViewport(0, 0, width, height);
  }
}

展示渲染后的GlSurfaceView

public class OneOpenGlActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    OneGlSurfaceView glSurfaceView = new OneGlSurfaceView(this);
    setContentView(glSurfaceView);
  }
}

效果如下:就是简单给GlSurfaceView渲染一层黑色。

使用OpenGl绘制几何图形

一:图形创建

创建一个几何图形(这里主要列举三角形和正方形),需要注意一点,我们设置图形的顶点坐标后,需要将顶点坐标转为ByteBuffer,这样OpenGl才能进行图形处理。

三角形图形创建:

public class Triangle {

  private FloatBuffer vertexBuffer;

  // number of coordinates per vertex in this array
  static final int COORDS_PER_VERTEX = 3;
  static float triangleCoords[] = {  // in counterclockwise order:
       0.0f, 0.5f, 0.0f, // top
      -0.5f, -0.5f, 0.0f, // bottom left
       0.5f, -0.5f, 0.0f // bottom right
  };

  // Set color with red, green, blue and alpha (opacity) values
  float color[] = { 255, 0, 0, 1.0f };

  public Triangle() {
    // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个float占4个字节
    ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
    // 数组排列用nativeOrder
    bb.order(ByteOrder.nativeOrder());
    // 从ByteBuffer创建一个浮点缓冲区
    vertexBuffer = bb.asFloatBuffer();
    // 将坐标添加到FloatBuffer
    vertexBuffer.put(triangleCoords);
    // 设置缓冲区来读取第一个坐标
    vertexBuffer.position(0);
  }
}

正方型图:

public class Square {

  private FloatBuffer vertexBuffer;
  private ShortBuffer drawListBuffer;

  // number of coordinates per vertex in this array
  static final int COORDS_PER_VERTEX = 3;
  static float squareCoords[] = {
      -0.5f, 0.5f, 0.0f,  // top left
      -0.5f, -0.5f, 0.0f,  // bottom left
       0.5f, -0.5f, 0.0f,  // bottom right
       0.5f, 0.5f, 0.0f }; // top right

  private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

  public Square() {
    // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个float占4个字节
    ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
    bb.order(ByteOrder.nativeOrder());
    vertexBuffer = bb.asFloatBuffer();
    vertexBuffer.put(squareCoords);
    vertexBuffer.position(0);

    // 初始化ByteBuffer,长度为arr数组的长度*2,因为一个short占2个字节
    ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
    dlb.order(ByteOrder.nativeOrder());
    drawListBuffer = dlb.asShortBuffer();
    drawListBuffer.put(drawOrder);
    drawListBuffer.position(0);
  }
}

创建图形基本没什么技巧可言,按部就班就行了,为什么数据需要转换格式呢?主要是因为Java的缓冲区数据存储结构为大端字节序(BigEdian),而OpenGl的数据为小端字节序(LittleEdian),因为数据存储结构的差异,所以,在Android中使用OpenGl的时候必须要进行下转换。当然,一般我们在使用的时候都会做个简单的工具类。这里提供几个简单的封装。(占几个字节就初始化ByteBuffer长度的时候*几)

将int[]转成IntBuffer

private IntBuffer intBufferUtil(int[] arr)
  {
    IntBuffer mBuffer;
    // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
    ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
    // 数组排列用nativeOrder
    qbb.order(ByteOrder.nativeOrder());
    mBuffer = qbb.asIntBuffer();
    mBuffer.put(arr);
    mBuffer.position(0);
    return mBuffer;
  }

将float[]数组转为OpenGl 所需要的FloatBuffer

private FloatBuffer floatBufferUtil(float[] arr)
  {
    FloatBuffer mBuffer;
    // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
    ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
    // 数组排列用nativeOrder
    qbb.order(ByteOrder.nativeOrder());
    mBuffer = qbb.asFloatBuffer();
    mBuffer.put(arr);
    mBuffer.position(0);
    return mBuffer;
  }

当然,依葫芦画瓢,如何将short[]转ShortBuffer这个就照着写就ok了

private ShortBuffer shortBufferUtil(short[] arr){
    ShortBuffer mBuffer;
    // 初始化ByteBuffer,长度为arr数组的长度*2,因为一个short占2个字节
    ByteBuffer dlb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 2 bytes per short)
        arr.length * 2);
    dlb.order(ByteOrder.nativeOrder());
    mBuffer = dlb.asShortBuffer();
    mBuffer.put(arr);
    mBuffer.position(0);
    return mBuffer;
  }

创建完形状之后,我们就要进行我们的第二步了,将这些形状渲染到GlSurfaceView中去。主要可分为下面几步:
1. 首先我们需要在GlSurfaceView.Renderer中初始化需要渲染的几何图形

private Triangle mTriangle;
  private Square  mSquare;
  public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    // 设置背景颜色
    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    // 初始化triangle
    mTriangle = new Triangle();
    // 初始化 square
    mSquare = new Square();
  }

二.:绘制图形,因为需要提供很多细节的图形渲染管线,所以绘制图形前至少需要一个顶点着色器来绘制形状和一个片段着色器的颜色,形状。这些着色器必须被编译,然后加入到一个OpenGL ES程序,然后将其用于绘制形状。简单介绍下这几个概念:
- 顶点着色器(Vertex Shader)顶点着色器是GPU上运行的小程序,由名字可以知道,通过它来处理顶点,他用于渲染图形顶点的OpenGL ES图形代码。顶点着色器可用来修改图形的位置,颜色,纹理坐标,不过不能用来创建新的顶点坐标。
- 片段着色器(Fragment Shader ) 用于呈现与颜色或纹理的形状的面的OpenGL ES代码。
- 项目(Program) -包含要用于绘制一个或多个形状着色器的OpenGL ES的对象。

下面给Triangle类定义一个基本的着色器代码:

public class Triangle {

  private final String vertexShaderCode =
    "attribute vec4 vPosition;" +
    "void main() {" +
    " gl_Position = vPosition;" +
    "}";

  private final String fragmentShaderCode =
    "precision mediump float;" +
    "uniform vec4 vColor;" +
    "void main() {" +
    " gl_FragColor = vColor;" +
    "}";

  ...
}

当然,上面我们创建了着色器的编译代码,代码编写完成,需要写个方法来执行这段代码,这里我们在渲染器中写一个如下方法来执行着色器代码:

public static int loadShader(int type, String shaderCode){

    // 创造顶点着色器类型(GLES20.GL_VERTEX_SHADER)
    // 或者是片段着色器类型 (GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);
    // 添加上面编写的着色器代码并编译它
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);
    return shader;
  }

这里有一点需要注意,因为着色器的代码执行是很昂贵滴,所以避免多次执行,需要我们一般将执行代码的逻辑写带图形类的构造方法中。比如上面的Triangle,我们就这么写:

private final int mProgram;
public Triangle() {
    ... ...//数据转换
    int vertexShader = OneGlRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
        vertexShaderCode);
    int fragmentShader = OneGlRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
        fragmentShaderCode);

    // 创建空的OpenGL ES程序
    mProgram = GLES20.glCreateProgram();

    // 添加顶点着色器到程序中
    GLES20.glAttachShader(mProgram, vertexShader);

    // 添加片段着色器到程序中
    GLES20.glAttachShader(mProgram, fragmentShader);

    // 创建OpenGL ES程序可执行文件
    GLES20.glLinkProgram(mProgram);
  }

最后,所有绘制的所有基本配置都配置完成之后,我们来写绘制图形的方法,我们在图形类(Triangle)中创建一个绘制的方法onDraw(),可以在onDraw()方法中设置绘制逻辑。

private int mPositionHandle;
  private int mColorHandle;

  private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
  private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

  public void draw() {
    // 将程序添加到OpenGL ES环境
    GLES20.glUseProgram(mProgram);

    // 获取顶点着色器的位置的句柄
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // 启用三角形顶点位置的句柄
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    //准备三角形坐标数据
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
        GLES20.GL_FLOAT, false,
        vertexStride, vertexBuffer);

    // 获取片段着色器的颜色的句柄
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    // 设置绘制三角形的颜色
    GLES20.glUniform4fv(mColorHandle, 1, color, 0);

    // 绘制三角形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // 禁用顶点数组
    GLES20.glDisableVertexAttribArray(mPositionHandle);
  }

完成上面所有步骤,只需要在GlSurfaceView.Renderer的onDrawFrame()方法中调用图形类的绘制方法即可(上面的onDraw()):

public void onDrawFrame(GL10 unused) {
    mTriangle.draw();
  }

最后的呈现效果如下图所示:

运用投影和相机视图

通常情况下,OpenGl中展示的视图和在Android上显示的图形会有偏差。借用官方图片:

当然我们可以通过矩阵转换来解决这种问题,让OpenGl上的视图在任何android设备上显示的比例都是一样的,这里说下什么是投影和相机视图:

投影的定义

使用OpenGl绘制的3D图形,需要展示在移动端2D设备上,这就是投影。Android OpenGl ES中有两种投影方式:一种是正交投影,一种是透视投影:

正交投影投影物体的带下不会随观察点的远近而发生变化,我们可以使用下面方法来执行正交投影:

Matrix.orthoM (float[] m,      //接收正交投影的变换矩阵
        int mOffset,    //变换矩阵的起始位置(偏移量)
        float left,     //相对观察点近面的左边距
        float right,    //相对观察点近面的右边距
        float bottom,    //相对观察点近面的下边距
        float top,     //相对观察点近面的上边距
        float near,     //相对观察点近面距离
        float far)     //相对观察点远面距离

透视投影:随观察点的距离变化而变化,观察点越远,视图越小,反之越大,我们可以通过如下方法来设置透视投影:

Matrix.frustumM (float[] m,     //接收透视投影的变换矩阵
        int mOffset,    //变换矩阵的起始位置(偏移量)
        float left,     //相对观察点近面的左边距
        float right,    //相对观察点近面的右边距
        float bottom,    //相对观察点近面的下边距
        float top,     //相对观察点近面的上边距
        float near,     //相对观察点近面距离
        float far)     //相对观察点远面距离

相机视图

什么是相机视图?简单来说生活中我们拍照,你站的高度,拿相机的位置,姿势不同,拍出来的照片也就不一样,相机视图就是来修改相机位置,观察方式以及相机的倾斜角度等属性。我们可以通过下面方法来修改相机视图属性:

Matrix.setLookAtM (float[] rm,   //接收相机变换矩阵
        int rmOffset,    //变换矩阵的起始位置(偏移量)
        float eyeX,float eyeY, float eyeZ,  //相机位置
        float centerX,float centerY,float centerZ, //观察点位置
        float upX,float upY,float upZ) //up向量在xyz上的分量

转换矩阵(变换矩阵)

转换矩阵用来做什么的呢?是否记得上面我们绘制的图形坐标需要转换为OpenGl中能处理的小端字节序(LittleEdian),没错,转换矩阵就是用来将数据转为OpenGl ES可用的数据字节,我们将相机视图和投影设置的数据相乘,便得到一个转换矩阵,然后我们再讲此矩阵传给顶点着色器,具体使用方法及参数说明如下:

Matrix.multiplyMM (float[] result, //接收相乘结果
        int resultOffset, //接收矩阵的起始位置(偏移量)
        float[] lhs,    //左矩阵
        int lhsOffset,   //左矩阵的起始位置(偏移量)
        float[] rhs,    //右矩阵
        int rhsOffset)   //右矩阵的起始位置(偏移量)

下面简单讲解下如何使用投影和相机视图来实现矩阵变换并传递给顶点着色器;

定义一个投影:

// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
  private final float[] mMVPMatrix = new float[16];
  private final float[] mProjectionMatrix = new float[16];
  private final float[] mViewMatrix = new float[16];
  public void onSurfaceChanged(GL10 unused, int width, int height) {
    GLES20.glViewport(0, 0, width, height);

    float ratio = (float) width / height;

    // 这个投影矩阵被应用于对象坐标在onDrawFrame()方法中
    Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
  }

定义一个相机视图

@Override
public void onDrawFrame(GL10 unused) {
  ...
  // Set the camera position (View matrix)
  Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

  // Calculate the projection and view transformation
  Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

  // Draw shape
  mTriangle.draw(mMVPMatrix);
}

修改图形类执行代码

public class Triangle {

  private final String vertexShaderCode =
    // This matrix member variable provides a hook to manipulate
    // the coordinates of the objects that use this vertex shader
    "uniform mat4 uMVPMatrix;" +
    "attribute vec4 vPosition;" +
    "void main() {" +
    // the matrix must be included as a modifier of gl_Position
    // Note that the uMVPMatrix factor *must be first* in order
    // for the matrix multiplication product to be correct.
    " gl_Position = uMVPMatrix * vPosition;" +
    "}";

  // Use to access and set the view transformation
  private int mMVPMatrixHandle;

  ...
}

投影和相机视图代码到图形类的绘制方法中去onDraw()

public void draw(float[] mvpMatrix){
    ... ...
    // 得到形状的变换矩阵的句柄
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

    // 将投影和视图转换传递给着色器
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

    // 画三角形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // 禁用顶点数组
    GLES20.glDisableVertexAttribArray(mPositionHandle);
  }

做完这些,我们就能得到如下图:

没错,这才没有变形的视图。到这里,基本的通过OpenGl绘制简单图形就over了,下面我们讲解下如何添加一些交互动作。

添加动作

前面都是简单的动作介绍,使用OpenGl在屏幕上绘制对象是使用openGl的基本功。下面我来说下如何添加旋转形状。使用OpenGl的描绘对象是相对简单的,首先需要在渲染器中创建一组旋转矩阵,然后使用之前提到过的投影和相机视图变换矩阵结合起来使用:

private float[] mRotationMatrix = new float[16];
public void onDrawFrame(GL10 gl) {
  float[] scratch = new float[16];

  ...

  // 创建一个旋转矩阵
  long time = SystemClock.uptimeMillis() % 4000L;
  float angle = 0.090f * ((int) time);
  Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f);

  // 将旋转矩阵与投影和相机视图组合在一起
  // Note that the mMVPMatrix factor *must be first* in order
  // for the matrix multiplication product to be correct.
  Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

  // Draw triangle
  mTriangle.draw(scratch);
}

运行效果图如下:

修改顶点颜色

一个颜色是不是太单调了?如何让做成多彩的呢?接下来我们来做一个多彩三角形,如何来做一个多彩三角形?我们通过顶点着色器来做。基于上面的代码,我们只需要做一点点改动,下面是基本步骤:
1. 修改着色器代码
2. 将颜色值修改为float数组并转为floatBuffer
3. 将获取的floatBuffer传递给顶点着色器。

修改着色器代码:

private final String vertexShaderCode =
      "attribute vec4 vPosition;" +
          "uniform mat4 uMVPMatrix;"+
          "varying vec4 vColor;"+
          "attribute vec4 aColor;"+
          "void main() {" +
          " gl_Position = uMVPMatrix*vPosition;" +
          " vColor=aColor;"+
          "}";

  private final String fragmentShaderCode =
      "precision mediump float;" +
          "varying vec4 vColor;" +
          "void main() {" +
          " gl_FragColor = vColor;" +
          "}";

shader的变量类型(uniform,attribute和varying)的区别

关于shader的变量类型(uniform,attribute和varying)的区别及使用,下面做下说明:
1. uniform:uniform变量在vertex和fragment两者之间声明方式完全一样,则它可以在vertex和fragment共享使用。(相当于一个被vertex和fragment shader共享的全局变量)uniform变量一般用来表示:变换矩阵,材质,光照参数和颜色等信息。在代码中通过GLES20.glGetUniformLocation(int program, String name)来获取属性值。并通过 GLES20.glUniformMatrix4fv(int location, int count, boolean transpose, float[] value, int offset);方法将数据传递给着色器。
2. attribute:这个变量只能在顶点着色器中使用(vertex Shader),用来表示顶点的数据,比如顶点坐标,顶点颜色,法线,纹理坐标等。在绘制的时候通过GLES20.glGetAttribLocation(int program, String name)来获取变量值,通过 GLES20.glEnableVertexAttribArray(int index)来启动句柄,最后通过 GLES20.glVertexAttribPointer(int indx,int size,int type,boolean normalized,int stride,java.nio.Buffer ptr)来设置图形数据。
3. varying变量:这个变量只能用来在vertex和fragment shader之间传递数据时使用,不可以通过代码获取其变量值。

接来下我们进行数据转换:

float color[] = {
      1.0f, 0f, 0f, 1.0f ,
      0f, 1.0f, 0f, 1.0f ,
      0f, 0f, 1.0f, 1.0f
  };
    public Triangle() {
      ... ...
     ByteBuffer dd = ByteBuffer.allocateDirect(
          color.length * 4);
      dd.order(ByteOrder.nativeOrder());
      colorBuffer = dd.asFloatBuffer();
      colorBuffer.put(color);
      colorBuffer.position(0);
    }

最后我们需要获取着色器的句柄并设置着色器的颜色:

public void draw(float[] mvpMatrix){
    ... ...
     /* // 获取片段着色器的vColor成员的句柄
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    // 设置绘制三角形的颜色
    GLES20.glUniform4fv(mColorHandle, 1, colorBuffer, 0);*/

    //获取片元着色器的vColor成员的句柄
    mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
    //设置绘制三角形的颜色
    GLES20.glEnableVertexAttribArray(mColorHandle);
    GLES20.glVertexAttribPointer(mColorHandle,4,
        GLES20.GL_FLOAT,false,
        0,colorBuffer);
        ... ...
        }

6. 参考链接:

opengl官网

opengl的环境搭建及基本教程

7. 项目地址:

AserbaosAndroid此项目为博主所有的系列学习的代码汇总项目,该文章的代码位于:opengl/OneOpenGl/OneOpenGlActivity

到此这篇关于Android openGl 绘制简单图形的实现示例的文章就介绍到这了,更多相关Android openGl 绘制简单图形内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android开发 OpenGL ES绘制3D 图形实例详解

    OpenGL ES是 OpenGL三维图形API 的子集,针对手机.PDA和游戏主机等嵌入式设备而设计. Ophone目前支持OpenGL ES 1.0 ,OpenGL ES 1.0 是以 OpenGL 1.3 规范为基础的,OpenGL ES 1.1 是以 OpenGL 1.5 规范为基础的.本文主要介绍利用OpenGL ES绘制图形方面的基本步骤. 本文内容由三部分构成.首先通过EGL获得OpenGL ES的编程接口;其次介绍构建3D程序的基本概念;最后是一个应用程序示例. OpenGL E

  • android使用OPENGL ES绘制圆柱体

    本文实例为大家分享了android使用OPENGL ES绘制圆柱体的具体代码,供大家参考,具体内容如下 效果图: 编写jiem.java *指定屏幕所要显示的假面,并对见.界面进行相关设置     *为Activity设置恢复处理,当Acitvity恢复设置时显示界面同样应该恢复     *当Activity暂停设置时,显示界面同样应该暂停 package com.scout.eeeeeee; import android.app.Activity; import android.os.Bund

  • Android基于OpenGL在GLSurfaceView上绘制三角形及使用投影和相机视图方法示例

    本文实例讲述了Android基于OpenGL在GLSurfaceView上绘制三角形及使用投影和相机视图方法.分享给大家供大家参考,具体如下: 定义三角形 OpenGL 允许我们使用三维坐标来定义物体.在绘制三角形前,我们需要定义它各个点的坐标.我们一般使用数组来存储各个顶点的坐标. OpenGL ES 默认 [0,0,0] (X,Y,Z) 在GLSurfaceView的中心,[1,1,0]在右上角,[-1,-1,0]在左下角. 绘制三角形 在绘制三角形之前,我们必须告诉OpenGL我们正在使用

  • Android开发之OpenGL绘制2D图形的方法分析

    本文实例讲述了Android开发之OpenGL绘制2D图形的方法.分享给大家供大家参考,具体如下: Android为OpenGL ES支持提供了GLSurviceView组建,这个组建用于显示3D图形.GLSurviceView本身并不提供绘制3的图形的功能,而是由GLSurfaceView.Renderer来完成了SurviceView中3D图形的绘制. 归纳起来,在android中使用OpenGL ES需要3个步骤. 1. 创建GLSurviceView组件,使用Activity来显示GLS

  • Android OpenGLES2.0绘制三角形(二)

    选择绘制三角形作为OpenGL ES 2.0的第一个实例,是因为前文中提到的,点.线.三角形是OpenGL ES世界的图形基础.无论多么复杂的几何物体,在OpenGL ES的世界里都可以用三角形拼成.关于Android OpenGL ES 三角形的绘制,在Android官方文档中有详细的说明和步骤,本文实例也是依照官方文档步骤绘制的三角形. 步骤 依照官方文档中的说明,Android中利用OpenGL ES 2.0绘制三角形的步骤为: 1. 在AndroidManifest.xml文件中设置使用

  • Android利用OpenGLES绘制天空盒实例教程

    前言 天空盒这个效果最早是在腾讯的实景地图里看到的,当时觉得很牛逼,但是没有想过自己去实现以下.最近这段时间对opengl很有兴趣,顺便就搞了这个天空盒,话不多说,先上效果. 天空盒的原理就是在三维空间中放置一个正方体,然后将我们的相机放置在正方体内,当我们的视点转动,相机跟着转动.我们就可以看到相应的景色的变换了,天空盒本质上是一个立方体. OpenGL 关于什么是OpenGL,什么是OpenGLES就不细说了,不了解的就自行百度吧,我们主要是关注代码.整个项目采用了Kotlin + Ndk的

  • Android openGl 绘制简单图形的实现示例

    学习五部曲,弄清楚5个W一个H(when(什么时候使用).where(在哪个地方使用?).who(对谁使用).what(是个什么东西).why(为什么要这么用?).一个H即:how(到底该怎么用?)),基本的概念篇主要围绕这几个方面进行分析 1. What? openGl是什么?openGl ES又是什么? 相信很多人从事开发的都或多或少听到过有关OpenGl这个东西,但是平时用的少,只知道有这么个东西,而且学起来不简单,所以大多数人都不能讲出个个所以然来. 官方对OpenGl的描述为: Ope

  • python绘制简单折线图代码示例

    1.画最简单的直线图 代码如下: import numpy as np import matplotlib.pyplot as plt x=[0,1] y=[0,1] plt.figure() plt.plot(x,y) plt.savefig("easyplot.jpg") 结果如下: 代码解释: #x轴,y轴 x=[0,1] y=[0,1] #创建绘图对象 plt.figure() #在当前绘图对象进行绘图(两个参数是x,y轴的数据) plt.plot(x,y) #保存图象 plt

  • Android Studio实现简单音乐播放功能的示例代码

    项目要求 基于Broadcast,BroadcastReceiver等与广播相关的知识实现简单的音乐播放功能,包括音乐的播放.暂停.切换.进度选择.音量调整. 设计效果 (进度条时间刷新功能还没有实现) 实现思路 音乐服务端负责播放音乐和收发广播的功能.当音乐服务端作为接收器时,只能接收到主页面广播的控制消息:作为发送器时,向主页面发送歌曲信息更新的消息 主页面负责进度条以及音量按钮的监听,同时也有收发广播的功能.当主页面作为接收器时,只能接收到音乐服务端广播的歌曲信息更新的消息:作为发送器时,

  • C# Chart绘制简单图形波形

    本文实例为大家分享了C# Chart绘制简单图形波形的具体代码,供大家参考,具体内容如下 此次用C#绘制波形使用的是Chart控件 1.将Chart控件拖进主界面,然后设置属性. //  // chart1 //  chartArea2.Name = "ChartArea1"; this.chart1.ChartAreas.Add(chartArea2); legend2.Name = "Legend1"; this.chart1.Legends.Add(legen

  • jQuery插件HighCharts绘制简单2D柱状图效果示例【附demo源码】

    本文实例讲述了jQuery插件HighCharts绘制简单2D柱状图效果.分享给大家供大家参考,具体如下: 1.实例代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>HighCharts 2D柱状图</title> <script type="text/javascript" src="js/jquer

  • Android自定义控件绘制基本图形基础入门

    本文讲述绘制Android自定义各种图形效果,为自定义控件的入门篇 相关视频链接: Android自定义控件系列 http://edu.csdn.net/course/detail/3719/65396 Android视频全系列 http://edu.csdn.net/course/detail/2741/43163 绘制点–这个控件只需要在布局中引用或者代码中new 即可,下面几个绘制只展示onDraw方法 package com.example.viewdemo1.view; import

  • iOS App开发中用CGContextRef绘制基本图形的基本示例

    Graphics Context是图形上下文,也可以理解为一块画布,我们可以在上面进行绘画操作,绘制完成后,将画布放到我们的view中显示即可,view看作是一个画框. CGContextRef功能强大,我们借助它可以画各种图形.开发过程中灵活运用这些技巧,可以帮助我们提供代码水平. 首先创建一个集成自UIView的,自定义CustomView类. 在CustomView.m中实现代码. 复制代码 代码如下: #import <QuartzCore/QuartzCore.h> 覆盖DranRe

  • Python使用统计函数绘制简单图形实例代码

    前言 Matplotlib 是 Python 的绘图库. 它可与 NumPy 一起使用,提供了一种有效的 MatLab 开源替代方案. 它也可以和图形工具包一起使用,如 PyQt 和 wxPython. 用matplotlib绘制一些大家比较熟悉又经常混淆的统计图形,掌握这些统计图形可以对数据可视化有一个深入理解. Windows 系统安装 Matplotlib 进入到 cmd 窗口下,执行以下命令: python -m pip install -U pip setuptools python

  • Android开发实现各种图形绘制功能示例

    本文实例讲述了Android开发实现各种图形绘制功能.分享给大家供大家参考,具体如下: 这里结合本人的开发事例,简单介绍一下如何在Android平台下实现各种图形的绘制. 首先自定义一个View类,这个view类里面需要一个Paint对象来控制图形的属性,需要一个Path对象来记录图形绘制的路径,需要一个Canvas类来执行绘图操作,还需要一个Bitmap类来盛放绘画的结果. Paint mPaint = new Paint(); mPaint.setAntiAlias(true); mPain

随机推荐