AndroidopenGl繪制簡單圖形的實現示例

學習五部曲,弄清楚5個W一個H(when(什么時候使用)、where(在哪個地方使用?)、who(對誰使用)、what(是個什么東西)、why(為什么要這么用?).一個H即:how(到底該怎么用?)),基本的概念篇主要圍繞這幾個方面進行分析

創(chuàng)新互聯公司堅持“要么做到,要么別承諾”的工作理念,服務領域包括:成都網站設計、成都網站制作、企業(yè)官網、英文網站、手機端網站、網站推廣等服務,滿足客戶于互聯網時代的石嘴山網站設計、移動媒體設計的需求,幫助企業(yè)找到有效的互聯網解決方案。努力成為您成熟可靠的網絡建設合作伙伴!

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

相信很多人從事開發(fā)的都或多或少聽到過有關OpenGl這個東西,但是平時用的少,只知道有這么個東西,而且學起來不簡單,所以大多數人都不能講出個個所以然來。

官方對OpenGl的描述為:

OpenGL(Open Graphics Library開發(fā)圖形接口)是一個跨平臺的圖形API,用于指定3D圖形處理硬件中的標準軟件接口。

OpenGl的前身是SGI公司為其圖形工作站開發(fā)的IRIS GL,后來因為IRIS GL的移植性不好,所以在其基礎上,開發(fā)出了OpenGl。OpenGl一般用于在圖形工作站,PC端使用,由于性能各方面原因,在移動端使用OpenGl基本帶不動。為此,Khronos公司就為OpenGl提供了一個子集,OpenGl ES(OpenGl for Embedded System)

什么是OpenGl ES呢?

OpenGl ES是免費的跨平臺的功能完善的2D/3D圖形庫接口的API,是OpenGL的一個子集。

移動端使用到的基本上都是OpenGl ES,當然Android開發(fā)下還專門為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的使用方法:
可以通過創(chuàng)建的實例使用這個類,并增加你的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():系統調用這個方法一次創(chuàng)建時GLSurfaceView。使用此方法來執(zhí)行只需要發(fā)生一次的操作,比如設置OpenGL的環(huán)境參數或初始化的OpenGL圖形對象。
  • onDrawFrame():系統調用上的每個重繪此方法GLSurfaceView。使用此方法作為主要執(zhí)行點用于繪制(和重新繪制)的圖形對象。
  • 系統調用此方法時的GLSurfaceView幾何形狀的變化,包括尺寸變化GLSurfaceView或設備屏幕的取向。例如,當設備從縱向變?yōu)闄M向的系統調用這個方法。使用此方法可以在變化做出反應GLSurfaceView容器。

介紹完了GlSurfaceView和GlSurfaceView.renderer之后,接下來說下如何使用GlSurfaceView;
1. 創(chuàng)建一個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);
  • 創(chuàng)建圖形類,確定好頂點位置和圖形顏色,將頂點和顏色數據轉換為OpenGl使用的數據格式
  • 加載頂點找色器和片段著色器用來修改圖形的顏色,紋理,坐標等屬性
  • 創(chuàng)建投影和相機視圖來顯示視圖的顯示狀態(tài),并將投影和相機視圖的轉換傳遞給著色器。
  • 創(chuàng)建項目(Program),連接頂點著色器片段著色器。
  • 將坐標數據傳入到OpenGl ES程序中:

使用OpenGl修改背景顏色

創(chuàng)建一個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渲染一層黑色。

Android openGl 繪制簡單圖形的實現示例

使用OpenGl繪制幾何圖形

一:圖形創(chuàng)建

創(chuàng)建一個幾何圖形(這里主要列舉三角形和正方形),需要注意一點,我們設置圖形的頂點坐標后,需要將頂點坐標轉為ByteBuffer,這樣OpenGl才能進行圖形處理。

三角形圖形創(chuàng)建:

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個字節(jié)
    ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
    // 數組排列用nativeOrder
    bb.order(ByteOrder.nativeOrder());
    // 從ByteBuffer創(chuàng)建一個浮點緩沖區(qū)
    vertexBuffer = bb.asFloatBuffer();
    // 將坐標添加到FloatBuffer
    vertexBuffer.put(triangleCoords);
    // 設置緩沖區(qū)來讀取第一個坐標
    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個字節(jié)
    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個字節(jié)
    ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
    dlb.order(ByteOrder.nativeOrder());
    drawListBuffer = dlb.asShortBuffer();
    drawListBuffer.put(drawOrder);
    drawListBuffer.position(0);
  }
}

創(chuàng)建圖形基本沒什么技巧可言,按部就班就行了,為什么數據需要轉換格式呢?主要是因為Java的緩沖區(qū)數據存儲結構為大端字節(jié)序(BigEdian),而OpenGl的數據為小端字節(jié)序(LittleEdian),因為數據存儲結構的差異,所以,在Android中使用OpenGl的時候必須要進行下轉換。當然,一般我們在使用的時候都會做個簡單的工具類。這里提供幾個簡單的封裝。(占幾個字節(jié)就初始化ByteBuffer長度的時候*幾)

將int[]轉成IntBuffer

private IntBuffer intBufferUtil(int[] arr)
  {
    IntBuffer mBuffer;
    // 初始化ByteBuffer,長度為arr數組的長度*4,因為一個int占4個字節(jié)
    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個字節(jié)
    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個字節(jié)
    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;
  }

創(chuàng)建完形狀之后,我們就要進行我們的第二步了,將這些形狀渲染到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();
  }

二.:繪制圖形,因為需要提供很多細節(jié)的圖形渲染管線,所以繪制圖形前至少需要一個頂點著色器來繪制形狀和一個片段著色器的顏色,形狀。這些著色器必須被編譯,然后加入到一個OpenGL ES程序,然后將其用于繪制形狀。簡單介紹下這幾個概念:
- 頂點著色器(Vertex Shader)頂點著色器是GPU上運行的小程序,由名字可以知道,通過它來處理頂點,他用于渲染圖形頂點的OpenGL ES圖形代碼。頂點著色器可用來修改圖形的位置,顏色,紋理坐標,不過不能用來創(chuàng)建新的頂點坐標。
- 片段著色器(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;" +
    "}";

  ...
}

當然,上面我們創(chuàng)建了著色器的編譯代碼,代碼編寫完成,需要寫個方法來執(zhí)行這段代碼,這里我們在渲染器中寫一個如下方法來執(zhí)行著色器代碼:

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

    // 創(chuàng)造頂點著色器類型(GLES20.GL_VERTEX_SHADER)
    // 或者是片段著色器類型 (GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);
    // 添加上面編寫的著色器代碼并編譯它
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);
    return shader;
  }

這里有一點需要注意,因為著色器的代碼執(zhí)行是很昂貴滴,所以避免多次執(zhí)行,需要我們一般將執(zhí)行代碼的邏輯寫帶圖形類的構造方法中。比如上面的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);

    // 創(chuàng)建空的OpenGL ES程序
    mProgram = GLES20.glCreateProgram();

    // 添加頂點著色器到程序中
    GLES20.glAttachShader(mProgram, vertexShader);

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

    // 創(chuàng)建OpenGL ES程序可執(zhí)行文件
    GLES20.glLinkProgram(mProgram);
  }

最后,所有繪制的所有基本配置都配置完成之后,我們來寫繪制圖形的方法,我們在圖形類(Triangle)中創(chuàng)建一個繪制的方法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環(huán)境
    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();
  }

最后的呈現效果如下圖所示:

Android openGl 繪制簡單圖形的實現示例

運用投影和相機視圖

通常情況下,OpenGl中展示的視圖和在Android上顯示的圖形會有偏差。借用官方圖片:

Android openGl 繪制簡單圖形的實現示例 

當然我們可以通過矩陣轉換來解決這種問題,讓OpenGl上的視圖在任何android設備上顯示的比例都是一樣的,這里說下什么是投影和相機視圖:

投影的定義

使用OpenGl繪制的3D圖形,需要展示在移動端2D設備上,這就是投影。Android OpenGl ES中有兩種投影方式:一種是正交投影,一種是透視投影:

正交投影投影物體的帶下不會隨觀察點的遠近而發(fā)生變化,我們可以使用下面方法來執(zhí)行正交投影:

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中能處理的小端字節(jié)序(LittleEdian),沒錯,轉換矩陣就是用來將數據轉為OpenGl ES可用的數據字節(jié),我們將相機視圖和投影設置的數據相乘,便得到一個轉換矩陣,然后我們再講此矩陣傳給頂點著色器,具體使用方法及參數說明如下:

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);
}

修改圖形類執(zhí)行代碼

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);
  }

做完這些,我們就能得到如下圖:

Android openGl 繪制簡單圖形的實現示例

沒錯,這才沒有變形的視圖。到這里,基本的通過OpenGl繪制簡單圖形就over了,下面我們講解下如何添加一些交互動作。

添加動作

前面都是簡單的動作介紹,使用OpenGl在屏幕上繪制對象是使用openGl的基本功。下面我來說下如何添加旋轉形狀。使用OpenGl的描繪對象是相對簡單的,首先需要在渲染器中創(chuàng)建一組旋轉矩陣,然后使用之前提到過的投影和相機視圖變換矩陣結合起來使用:

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

  ...

  // 創(chuàng)建一個旋轉矩陣
  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);
}

運行效果圖如下:

Android openGl 繪制簡單圖形的實現示例

修改頂點顏色

一個顏色是不是太單調了?如何讓做成多彩的呢?接下來我們來做一個多彩三角形,如何來做一個多彩三角形?我們通過頂點著色器來做?;谏厦娴拇a,我們只需要做一點點改動,下面是基本步驟:
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)的區(qū)別

關于shader的變量類型(uniform,attribute和varying)的區(qū)別及使用,下面做下說明:
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);
        ... ...
        }

Android openGl 繪制簡單圖形的實現示例

6. 參考鏈接:

opengl官網

opengl的環(huán)境搭建及基本教程

7. 項目地址:

AserbaosAndroid此項目為博主所有的系列學習的代碼匯總項目,該文章的代碼位于:opengl/OneOpenGl/OneOpenGlActivity

到此這篇關于Android openGl 繪制簡單圖形的實現示例的文章就介紹到這了,更多相關Android openGl 繪制簡單圖形內容請搜索創(chuàng)新互聯以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持創(chuàng)新互聯!

當前文章:AndroidopenGl繪制簡單圖形的實現示例
本文地址:http://muchs.cn/article8/ghooop.html

成都網站建設公司_創(chuàng)新互聯,為您提供網站維護、定制開發(fā)網站設計、建站公司軟件開發(fā)、網站策劃

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯

綿陽服務器托管