Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> OpenglES2.0 for Android:來畫個矩形吧

OpenglES2.0 for Android:來畫個矩形吧

編輯:關於Android編程

上一節中我們繪制了一個三角形,我們在上一節的基礎上來完成矩形的繪制 。 OK,開始動手做吧,首先在上一節的項目中的shape目錄下新建一個類——Square (Square.java),然後定義矩形的四個頂點的坐標,此時代碼如下(Square.java):  
package com.cumt.shape;

public class Square {

	//float類型的字節數
	private static final int BYTES_PER_FLOAT = 4;
	// 數組中每個頂點的坐標數
    static final int COORDS_PER_VERTEX = 2;
	//矩形頂點坐標
	static float squareCoords[] = { -0.5f,  0.5f ,   // top left
        -0.5f, -0.5f ,   // bottom left
         0.5f, -0.5f ,   // bottom right
         0.5f,  0.5f }; // top right
}

和上一節一樣,我們需要將float[ ]的數據轉換為 floatbuffer ,此時代碼如下 (Square.java):  
package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.content.Context;

public class Square {
	
	private Context context;

	//float類型的字節數
	private static final int BYTES_PER_FLOAT = 4;
	// 數組中每個頂點的坐標數
    static final int COORDS_PER_VERTEX = 2;
	//矩形頂點坐標
	static float squareCoords[] = { -0.5f,  0.5f ,   // top left
        -0.5f, -0.5f ,   // bottom left
         0.5f, -0.5f ,   // bottom right
         0.5f,  0.5f }; // top right
	
	private FloatBuffer vertexBuffer;
	
	public Square(Context context) {
		this.context = context;
		vertexBuffer = ByteBuffer
    			.allocateDirect(squareCoords.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐標們加入FloatBuffer中
        vertexBuffer.put(squareCoords);
        // 設置buffer,從第一個坐標開始讀
        vertexBuffer.position(0);
	}
}

接下來就是著色器的編寫,讀取與編譯,鏈接,獲取program ID的操作,我們仍然使用上一節的著色器就好,其他的過程也與上一節相同,這裡就不再說明了, copy一下拿來用吧~~,當然你想進一步封裝一下著色器的操作更好。現在的代碼(Square.java):  
package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;

public class Square {
	
	private Context context;

	//float類型的字節數
	private static final int BYTES_PER_FLOAT = 4;
	// 數組中每個頂點的坐標數
    static final int COORDS_PER_VERTEX = 2;
	//矩形頂點坐標
	static float squareCoords[] = { -0.5f,  0.5f ,   // top left
        -0.5f, -0.5f ,   // bottom left
         0.5f, -0.5f ,   // bottom right
         0.5f,  0.5f }; // top right
	
	private FloatBuffer vertexBuffer;
	
	//------------第一步 : 定義兩個標簽,分別於著色器代碼中的變量名相同, 
    //------------第一個是頂點著色器的變量名,第二個是片段著色器的變量名
	private static final String A_POSITION = "a_Position";
	private static final String U_COLOR = "u_Color";
	
	//------------第二步: 定義兩個ID,我們就是通ID來實現數據的傳遞的,這個與前面
	//------------獲得program的ID的含義類似的
	private int uColorLocation;
	private int aPositionLocation;
	
	private int program;//保存program的id
	
	public Square(Context context) {
		this.context = context;
		vertexBuffer = ByteBuffer
    			.allocateDirect(squareCoords.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐標們加入FloatBuffer中
        vertexBuffer.put(squareCoords);
        // 設置buffer,從第一個坐標開始讀
        vertexBuffer.position(0);
        
        getProgram();
        
      //----------第三步: 獲取這兩個ID ,是通過前面定義的標簽獲得的
        uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		
		//---------第五步: 傳入數據
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
	}
	
	//獲取program
    private void getProgram(){
    	//獲取頂點著色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_vertex_shader);
    	//獲取片段著色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_fragment_shader);
		//獲取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }
}
  接下來就是繪制了,這是我們這一節的重點,我們應該如何繪制一個矩形呢? 我們知道在OpenglES中支持的繪制方式有3 類 :點 ,線 , 三角形 ,每一類又有一種或多種繪制方式。 那我們來繪制一個矩形 ,其繪制方式是不是也有多種呢 ?答案是肯定的 。我們先用線段的方式來繪制這個矩形 。   線段的幾種繪制方式:   GL_LINES : 將傳入的頂點按照順序,兩兩組織成線段進行繪制,若頂點個數為奇數,則會自動忽略掉最後一個頂點。   GL_LINE_STRIP:將傳入的頂點按照順序依次連接進行繪制   GL_LINE_LOOP:將傳入的頂點按照順序依次連接進行繪制,但是最後一個頂點會與第一個連接,形成線段環   \   我們就按照GL_LINE_LOOP 的方式來繪制這個矩形,添加draw方式,此時代碼如下 (Square.java):  
package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;

public class Square {
	
	private Context context;

	//float類型的字節數
	private static final int BYTES_PER_FLOAT = 4;
	// 數組中每個頂點的坐標數
    static final int COORDS_PER_VERTEX = 2;
	//矩形頂點坐標
	static float squareCoords[] = { -0.5f,  0.5f ,   // top left
        -0.5f, -0.5f ,   // bottom left
         0.5f, -0.5f ,   // bottom right
         0.5f,  0.5f }; // top right
	
	private FloatBuffer vertexBuffer;
	
	//------------第一步 : 定義兩個標簽,分別於著色器代碼中的變量名相同, 
    //------------第一個是頂點著色器的變量名,第二個是片段著色器的變量名
	private static final String A_POSITION = "a_Position";
	private static final String U_COLOR = "u_Color";
	
	//------------第二步: 定義兩個ID,我們就是通ID來實現數據的傳遞的,這個與前面
	//------------獲得program的ID的含義類似的
	private int uColorLocation;
	private int aPositionLocation;
	
	private int program;//保存program的id
	
	//---------第四步:定義坐標元素的個數,這裡有三個頂點
	private static final int POSITION_COMPONENT_COUNT = 4;
		
	
	public Square(Context context) {
		this.context = context;
		vertexBuffer = ByteBuffer
    			.allocateDirect(squareCoords.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐標們加入FloatBuffer中
        vertexBuffer.put(squareCoords);
        // 設置buffer,從第一個坐標開始讀
        vertexBuffer.position(0);
        
        getProgram();
        
      //----------第三步: 獲取這兩個ID ,是通過前面定義的標簽獲得的
        uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		
		//---------第五步: 傳入數據
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
	}
	
	//獲取program
    private void getProgram(){
    	//獲取頂點著色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_vertex_shader);
    	//獲取片段著色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_fragment_shader);
		//獲取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }
    
    //以GL_LINE_LOOP方式繪制
    public void draw(){
		GLES20.glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);		
		GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, POSITION_COMPONENT_COUNT);
    }
}

最後在MyRender中創建對象,調用draw來繪制 ,代碼如下 (MyRender.java):  
package com.cumt.render;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.cumt.shape.Square;
import com.cumt.shape.Triangle;

import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glViewport;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;

public class MyRender implements Renderer {
	
	private Context context;
	
	public MyRender(Context context){
		this.context = context;
	}
	
	//定義三角形對象
	Triangle triangle;
	Square square;
	
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		Log.w("MyRender","onSurfaceCreated");
		// TODO Auto-generated method stub
		//First:設置清空屏幕用的顏色,前三個參數對應紅綠藍,最後一個對應alpha
		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//		triangle = new Triangle(context);
		square = new Square(context);
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		Log.w("MyRender","onSurfaceChanged");
		// TODO Auto-generated method stub
		//Second:設置視口尺寸,即告訴opengl可以用來渲染的surface大小
		glViewport(0,0,width,height);
	}

	public void onDrawFrame(GL10 gl) {
		Log.w("MyRender","onDrawFrame");
		// TODO Auto-generated method stub
		//Third:清空屏幕,擦除屏幕上所有的顏色,並用之前glClearColor定義的顏色填充整個屏幕
		glClear(GL_COLOR_BUFFER_BIT);
		//繪制三角形
//		triangle.draw();
		square.draw();
	}
}

運行結果:   \   好吧,貌似看不太清楚,我們把背景顏色改為白色,把 MyRender.java 中onSurfaceCreated 方法的glClearColor(0.0f, 0.0f, 0.0f, 0.0f) 改為 glClearColor(1.0f, 1.0f, 1.0f, 0.0f);   再運行一下:   \ 看到我們實際上畫出了一個矩形框,中間是沒有著色的。下面我們來使用三角形的方式來繪制。   三角形的幾種繪制方式:   GL_TRIANGGLES :將傳入的頂點按照沒3個一組組成一個三角形進行繪制   GL_TRIANGLE_TRIP:將傳入的頂點按照順序三個一組組成三角形進行,前面三個頂點的後兩個頂點做為下一個三角形的前兩個頂點, 比如 有v0 v1 v2 v3 四個頂點順序排列,則v0 v1 v2組成一個三角形,v1,v2,v3組成一個三角形。   GL_TRIANGLE_FAN:三角形扇的形式,將傳入的頂點數據的第一個頂點做為中心點,其他點做為邊緣點繪制一系列組成扇形的相鄰三角形。     \       我們先使用第一種方式來畫這個矩形,此時我們需要畫兩個三角形,所以需要6個頂點數據,我們copy一下Square.java,重命名為Square2.java, 首先 修改頂點數據 ,然後修改繪制方式 ,代碼及步驟(見代碼中 /* */注釋的內容 ) 如下 (Square2.java):  
package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;

import android.content.Context;
import android.opengl.GLES20;

public class Square2 {
	
	private Context context;

	//float類型的字節數
	private static final int BYTES_PER_FLOAT = 4;
	// 數組中每個頂點的坐標數
    static final int COORDS_PER_VERTEX = 2;
    
    /*------------------第一步: 修改頂點數據-------------------------*/
	//矩形頂點坐標
	static float squareCoords[] = { -0.5f,  0.5f ,   // top left
		 0.5f,  0.5f ,   // top right
		-0.5f, -0.5f ,   // bottom left
		-0.5f, -0.5f ,   // bottom left
         0.5f, -0.5f ,   // bottom right
         0.5f,  0.5f }; // top right
	
	private FloatBuffer vertexBuffer;
	
    //------------第一個是頂點著色器的變量名,第二個是片段著色器的變量名
	private static final String A_POSITION = "a_Position";
	private static final String U_COLOR = "u_Color";
	
	//------------獲得program的ID的含義類似的
	private int uColorLocation;
	private int aPositionLocation;
	
	private int program;//保存program的id
	
	/*------------------第二步: 修改頂點個數-------------------------*/
	private static final int POSITION_COMPONENT_COUNT = 6;
		
	
	public Square2(Context context) {
		this.context = context;
		vertexBuffer = ByteBuffer
    			.allocateDirect(squareCoords.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐標們加入FloatBuffer中
        vertexBuffer.put(squareCoords);
        // 設置buffer,從第一個坐標開始讀
        vertexBuffer.position(0);
        
        getProgram();
        
        uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
	}
	
	//獲取program
    private void getProgram(){
    	//獲取頂點著色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_vertex_shader);
    	//獲取片段著色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_fragment_shader);
		//獲取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }
    
    //以GL_LINE_LOOP方式繪制
    public void draw(){
		GLES20.glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);		
		
		/*------------------第三步: 修改繪制方式-------------------------*/
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
    }
}

然後在MyRender類中就可以new 該對象並調用其draw方法繪制了 ,運行效果:   \   雖然這種方式滿足了我們的需求,但是造成了數據的冗余,我們可以使用後兩種繪制方式來解決這個問題。這裡給出後兩者方式的數據和draw方法:     GL_TRIANGLE_STRIP方式 :
//GL_TRIANGLE_STRIP
    static float squareCoords[] = { -0.5f,  0.5f ,   // top left
    	 0.5f,  0.5f  , // top right
        -0.5f, -0.5f  ,   // bottom left
         0.5f, -0.5f  }; // bottom right
    private static final int POSITION_COMPONENT_COUNT = 4;
    
    public void draw(){
		GLES20.glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);		
		
		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, POSITION_COMPONENT_COUNT);
    }

GL_TRIANGLE_FAN方式:  
 //GL_TRIANGLE_FAN 要注意點的順序問題 (試試將 bottom right和bottom left交換位置看看繪制的是否還是矩形)
    static float squareCoords[] = { -0.5f,  0.5f ,   // top left
    	 0.5f,  0.5f  , // top right
    	 0.5f, -0.5f  , // bottom right
        -0.5f, -0.5f  };  // bottom left
    private static final int POSITION_COMPONENT_COUNT = 4;
    
    public void draw(){
		GLES20.glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);		
		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0 , POSITION_COMPONENT_COUNT);
    }

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved