Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> OpenglES2.0 for Android:再談紋理映射

OpenglES2.0 for Android:再談紋理映射

編輯:關於Android編程

前言

上一節我們實現了一個簡單的紋理映射的例子——一個簡單的貼圖,這節我們來做一些稍微復雜一點的例子,最後再給我們前面的立方體做一個紋理。  

紋理拉伸

重復拉伸方式

  這種是經常使用的一張紋理拉伸方式,常用於繪制一些重復的元素,比如我們在游戲繪制一幅方格式的地圖時。使用重復拉伸方式使得紋理能夠根據目標平
面的大小自動重復,這樣既不會失去紋理圖的效果,也可以節省內存。如下圖所示:   \   實現起來很簡單,我們回到上節的項目,找到我們紋理的工具類TextureHelper.java ,修改如下 (TextureHelper.java):  
package com.cumt.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;

public class TextureHelper {
	
	public static final String TAG = "TextureHelper";
	
	public static int loadTexture(Context context,int resourceId,boolean isRepeat){
		/*
		 * 第一步 : 創建紋理對象
		 */
		final int[] textureObjectId = new int[1];//用於存儲返回的紋理對象ID
		GLES20.glGenTextures(1,textureObjectId, 0);
		if(textureObjectId[0] == 0){//若返回為0,,則創建失敗
			if(LoggerConfig.ON){
				Log.w(TAG,"Could not generate a new Opengl texture object");
			}
			return 0;
		}
		/*
		 * 第二步: 加載位圖數據並與紋理綁定
		 */
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inScaled = false;//Opengl需要非壓縮形式的原始數據
		final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),resourceId, options);
		if(bitmap == null){
			if(LoggerConfig.ON){
				Log.w(TAG,"ResourceId:"+resourceId+"could not be decoded");
			}
			GLES20.glDeleteTextures(1, textureObjectId, 0);
			return 0;
		}
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureObjectId[0]);//通過紋理ID進行綁定
		
		if(isRepeat){
			GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
			GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
		}
		
		/*
		 * 第三步: 設置紋理過濾
		 */
		//設置縮小時為三線性過濾
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR_MIPMAP_LINEAR);
		//設置放大時為雙線性過濾
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
		/*
		 * 第四步: 加載紋理到Opengl並返回ID
		 */
		GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
		bitmap.recycle();
		GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
		return textureObjectId[0];
	}
}
  我們只做了兩處修改,首先loadTexture新加一個布爾型參數isRepeat ,下面更加isRepeat的值來判斷是否設置重復拉伸 。如果isRepeat為true則調用 :   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);   設置紋理在S與T緯度都設置為重復拉伸 。   下面我們來設置繪制4*4個,我們只需將Square.java中的頂點坐標與紋理坐標的對應改為下面 所示 :  
 //矩形頂點坐標  與 紋理坐標
    static float squareCoords[] = { //以三角形扇的形式繪制
    	//x     y       s   t
    	-0.5f,  0.5f ,  0 , 0 , // top left  
        0.5f,  0.5f  ,  4 , 0 ,// top right  
        0.5f, -0.5f  ,  4 , 4 ,// bottom right  
       -0.5f, -0.5f  ,  0 , 4};  // bottom left  

此時我們的紋理在S軸范圍為0 到 4 ,在T軸也是0到4 ,於是紋理在S軸方向重復四次,在T軸方向重復四次。下面使用loadTexture方法時傳入true, 運行一下即可看到上面的效果。  

截取拉伸方式

  將上面的紋理工具類中的 if (isRepeat )條件下的代碼改為 :  
if(isRepeat){
			GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
			GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
		}
其它代碼不變 。我們將S軸方向與T軸方向都設置為截取拉伸 ,運行效果如下 :   \   能夠看出在S軸方向與T軸方向紋理的邊緣都被拉伸。當然也可以設置在S軸方向為重復拉伸,在T軸方向為截取拉伸。  
if(isRepeat){
			GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
			GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
		}
  如圖所示 ; \  

立方體添加紋理

前面一直在說二維圖形的貼圖,其實對於三維物體也是一樣。關鍵在於找到頂點坐標與紋理坐標的關系。 接下來我們來給我們上次繪制的立方體添加紋理。   首先,將我們的上節的紋理工具類copy到立方體的工具類下 ,修改頂點著色器和片段著色器如下 :  
//vertex_shader_cube.glsl
uniform mat4 u_Matrix;
attribute vec4 a_Position;  
attribute vec2 a_TextureCoordinates;
varying vec2 v_TextureCoordinates;
void main()                    
{                              
    gl_Position = u_Matrix * a_Position;
    v_TextureCoordinates = a_TextureCoordinates;	
}  

//fragment_shader_cube.glsl
precision mediump float; 				
uniform sampler2D u_TextureUnit;      	 								
varying vec2 v_TextureCoordinates;
void main()                    		
{                              	
    gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);                                       		
}

最後修改我們的Cube類 ,添加紋理坐標,傳入紋理坐標數據等 ,過程和上一節一樣 ,我們直接看代碼 :  
package com.cumt.shape;

import static android.opengl.GLES20.GL_TEXTURE0;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.glActiveTexture;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glUniform1i;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglescubewen.R;
import com.cumt.utils.MatrixState;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import com.cumt.utils.TextureHelper;

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

public class Cube {

	//頂點坐標
	private FloatBuffer vertexBuffer;
	private Context context;
	//float類型的字節數
	private static final int BYTES_PER_FLOAT = 4;
	//共有72個頂點坐標,每個面包含12個頂點坐標
	private static final int POSITION_COMPONENT_COUNT = 6*12;
	// 數組中每個頂點的坐標數
    private static final int COORDS_PER_VERTEX = 3;
    // 顏色數組中每個顏色的值數
	
	private static final String A_POSITION = "a_Position";
	private static final String U_MATRIX = "u_Matrix";
	private int uMatrixLocation;
	private int aPositionLocation;
	private int program;
	private static final int TEXTURE_COORDIANTES_COMPONENT_COUNT = 2; //一個紋理坐標含有的元素個數
	private static final int STRIDE = (COORDS_PER_VERTEX + TEXTURE_COORDIANTES_COMPONENT_COUNT)
			* BYTES_PER_FLOAT;
	
	private static final String A_TEXTURE_COORDINATES = "a_TextureCoordinates";//紋理
	private static final String U_TEXTURE_UNIT = "u_TextureUnit";//紋理
    private int uTextureUnitLocation;
    private int aTextureCoordinates;
    private int texture;
    
    static float vertices[] = {
    	//X   Y   Z        S    T
		//前面
    	0,0,1.0f,  			0.5f,0.5f,
    	1.0f,1.0f,1.0f, 	1.0f,0,
    	-1.0f,1.0f,1.0f, 	0,0,
    	0,0,1.0f,           0.5f,0.5f,
    	-1.0f,1.0f,1.0f,    0,0,
    	-1.0f,-1.0f,1.0f,   0,1.0f,
    	0,0,1.0f,           0.5f,0.5f,
    	-1.0f,-1.0f,1.0f,   0,1.0f,
    	1.0f,-1.0f,1.0f,    1.0f,1.0f,
    	0,0,1.0f,           0.5f,0.5f,
    	1.0f,-1.0f,1.0f,    1.0f,1.0f,
    	1.0f,1.0f,1.0f,     1.0f,0,
    	//後面
    	0,0,-1.0f,        	0.5f,0.5f,
    	1.0f,1.0f,-1.0f,    1.0f,0,
    	1.0f,-1.0f,-1.0f,   1.0f,1.0f,
    	0,0,-1.0f,          0.5f,0.5f,
    	1.0f,-1.0f,-1.0f,   1.0f,1.0f,
    	-1.0f,-1.0f,-1.0f,  0,1.0f,
    	0,0,-1.0f,          0.5f,0.5f,
    	-1.0f,-1.0f,-1.0f,  0,1.0f,
    	-1.0f,1.0f,-1.0f,   0,0,
    	0,0,-1.0f,          0.5f,0.5f,
    	-1.0f,1.0f,-1.0f,   0,0,
    	1.0f,1.0f,-1.0f,    1.0f,0,
    	//左面
    	-1.0f,0,0,      	0.5f,0.5f, 
    	-1.0f,1.0f,1.0f,    1.0f,0,
    	-1.0f,1.0f,-1.0f,   0,0,
    	-1.0f,0,0,          0.5f,0.5f,
    	-1.0f,1.0f,-1.0f,   0,0,
    	-1.0f,-1.0f,-1.0f,  0,1.0f,
    	-1.0f,0,0,          0.5f,0.5f,
    	-1.0f,-1.0f,-1.0f,  0,1.0f,
    	-1.0f,-1.0f,1.0f,   1.0f,1.0f,
    	-1.0f,0,0,          0.5f,0.5f,
    	-1.0f,-1.0f,1.0f,   1.0f,1.0f,
    	-1.0f,1.0f,1.0f,    1.0f,0,
    	//右面  
    	1.0f,0,0,           0.5f,0.5f,
    	1.0f,1.0f,1.0f,     0,0,
    	1.0f,-1.0f,1.0f,    0,1.0f,
    	1.0f,0,0,           0.5f,0.5f,
    	1.0f,-1.0f,1.0f,    0,1.0f,
    	1.0f,-1.0f,-1.0f,   1.0f,1.0f,
    	1.0f,0,0,           0.5f,0.5f,
    	1.0f,-1.0f,-1.0f,   1.0f,1.0f,
    	1.0f,1.0f,-1.0f,    1.0f,0,
    	1.0f,0,0,           0.5f,0.5f,
    	1.0f,1.0f,-1.0f,    1.0f,0,
    	1.0f,1.0f,1.0f,     0,0,
    	//上面
    	0,1.0f,0,           0.5f,0.5f,
    	1.0f,1.0f,1.0f,     1.0f,0,
    	1.0f,1.0f,-1.0f,    1.0f,1.0f,
    	0,1.0f,0,        	0.5f,0.5f,
    	1.0f,1.0f,-1.0f,    1.0f,1.0f,
    	-1.0f,1.0f,-1.0f,   0,1.0f,
    	0,1.0f,0,           0.5f,0.5f,
    	-1.0f,1.0f,-1.0f,   0,1.0f,
    	-1.0f,1.0f,1.0f, 	0,0,
    	0,1.0f,0,           0.5f,0.5f,
    	-1.0f,1.0f,1.0f,    0,0,
    	1.0f,1.0f,1.0f,  	1.0f,0,
    	//下面
    	0,-1.0f,0,        	0.5f,0.5f,
    	1.0f,-1.0f,1.0f,    1.0f,0,
    	-1.0f,-1.0f,1.0f,   0,0,
    	0,-1.0f,0,          0.5f,0.5f,
    	-1.0f,-1.0f,1.0f,   0,0,
    	-1.0f,-1.0f,-1.0f,  0,1.0f,
    	0,-1.0f,0,          0.5f,0.5f,
    	-1.0f,-1.0f,-1.0f,  0,1.0f,
    	1.0f,-1.0f,-1.0f,   1.0f,1.0f,
    	0,-1.0f,0,          0.5f,0.5f,
    	1.0f,-1.0f,-1.0f,   1.0f,1.0f,
    	1.0f,-1.0f,1.0f ,   1.0f,0,
	};
	
	
	public Cube(Context context){
		this.context = context;
		
		vertexBuffer = ByteBuffer
    			.allocateDirect(vertices.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐標們加入FloatBuffer中
        vertexBuffer.put(vertices);
        // 設置buffer,從第一個坐標開始讀
        vertexBuffer.position(0);
        
        getProgram();
        
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		uMatrixLocation = GLES20.glGetUniformLocation(program, U_MATRIX);
		
		aTextureCoordinates = GLES20.glGetAttribLocation(program, A_TEXTURE_COORDINATES);
		uTextureUnitLocation = GLES20.glGetAttribLocation(program, U_TEXTURE_UNIT);
		texture = TextureHelper.loadTexture(context, R.drawable.umei,false);
		// Set the active texture unit to texture unit 0.
        glActiveTexture(GL_TEXTURE0);
        // Bind the texture to this unit.
        glBindTexture(GL_TEXTURE_2D, texture);
        // Tell the texture uniform sampler to use this texture in the shader by
        // telling it to read from texture unit 0.
        glUniform1i(uTextureUnitLocation, 0);
        
		//---------傳入頂點數據數據
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, STRIDE, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
		//設置從第二個元素開始讀取,因為從第二個元素開始才是紋理坐標
		vertexBuffer.position(COORDS_PER_VERTEX);
		GLES20.glVertexAttribPointer(aTextureCoordinates, TEXTURE_COORDIANTES_COMPONENT_COUNT,
						GLES20.GL_FLOAT, false, STRIDE, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aTextureCoordinates);
	}
	
	//獲取program
    private void getProgram(){
    	//獲取頂點著色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.vertex_shader_cube);
    	//獲取片段著色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.fragment_shader_cube);
		//獲取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }
    
    public void draw(){
		GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, MatrixState.getFinalMatrix(),0);
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
    }
}

運行結果如下 (屏幕方向設置為橫向):   \   這裡如上面所說,關鍵是找到頂點坐標與紋理坐標的對應。 我們以正面為例,按照我們的繪制方式正面我們實際上需要確定五個頂點對應的紋理坐標,這五個頂點分別是 : ----------------------------------------------------------------------------------------------------------------------------------------------------- 左上角 (-1 ,-1,1 ) 左下角 (-1,1,1 ) 右上角 (1 ,-1 , 1) 右下角 (1, 1 ,1 ) 中心點 (0 ,0 ,1) ------------------------------------------------------------------------------------------------------------------------------------------------------ 對應的紋理的坐標依次為 : (0 , 1 ) (0 ,0 ) (1 ,1 ) (1, 0) (0.5 , 0.5 )要注意紋理的左上角的ST坐標並不是(0,0)而是(0,1) 否則我們繪制的紋理繪制上下顛倒的。  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved