1. 程式人生 > >初學Android,OpenGL ES之使用紋理(八十三)

初學Android,OpenGL ES之使用紋理(八十三)

在網上發現這些講紋理的文章,非常不錯

android 遊戲導引(4. 簡單紋理貼圖)

Android OpenGL es 紋理座標設定與貼圖規則

Android OpenGL | ES給立方體進行紋理對映

這篇的例子也是立方體的紋理,不過是用手指劃過讓立方體旋轉


使用紋理的重要程式碼如下

		private void loadTexture(GL10 gl)
		{
			Bitmap bitmap = null;
			try
			{
				// 載入點陣圖
				bitmap = BitmapFactory.decodeResource(context.getResources(),
					R.drawable.sand);
				int[] textures = new int[1];
				// 指定生成N個紋理(第一個引數指定生成1個紋理),
				// textures陣列將負責儲存所有紋理的代號。
				gl.glGenTextures(1, textures, 0);
				// 獲取textures紋理陣列中的第一個紋理
				texture = textures[0];
				// 通知OpenGL將texture紋理繫結到GL10.GL_TEXTURE_2D目標中
				gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
				// 設定紋理被縮小(距離視點很遠時被縮小)時候的濾波方式
				gl.glTexParameterf(GL10.GL_TEXTURE_2D,
					GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
				// 設定紋理被放大(距離視點很近時被方法)時候的濾波方式
				gl.glTexParameterf(GL10.GL_TEXTURE_2D,
					GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
				// 設定在橫向、縱向上都是平鋪紋理
				gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
					GL10.GL_REPEAT);
				gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
					GL10.GL_REPEAT);
				// 載入點陣圖生成紋理
				GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
			}
			finally
			{
				// 生成紋理之後,回收點陣圖
				if (bitmap != null)
					bitmap.recycle();
			}
		}

紋理的裝載則是在方法void onSurfaceCreated(GL10 gl, EGLConfig config) 中

在Renderer的 void onDrawFrame(GL10 gl)方法中要啟用紋理的陣列資料,座標資料,執行紋理貼圖

			// 啟用貼圖座標陣列資料
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			// 設定貼圖的的座標資料
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, cubeTexturesBuffer);
			// 執行紋理貼圖
			gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);

完整程式碼如下
public class Texture3D extends Activity
	implements OnGestureListener
{
	// 定義旋轉角度
	private float anglex = 0f;
	private float angley = 0f;
	static final float ROTATE_FACTOR = 60;
	// 定義手勢檢測器例項
	GestureDetector detector;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		// 建立一個GLSurfaceView,用於顯示OpenGL繪製的圖形
		GLSurfaceView glView = new GLSurfaceView(this);
		// 建立GLSurfaceView的內容繪製器
		MyRenderer myRender = new MyRenderer(this);
		// 為GLSurfaceView設定繪製器
		glView.setRenderer(myRender);
		setContentView(glView);
		// 建立手勢檢測器
		detector = new GestureDetector(this);
	}

	@Override
	public boolean onTouchEvent(MotionEvent me)
	{
		// 將該Activity上的觸碰事件交給GestureDetector處理
		return detector.onTouchEvent(me);
	}

	@Override
	public boolean onFling(MotionEvent event1, MotionEvent event2,
		float velocityX, float velocityY)
	{
		velocityX = velocityX > 4000 ? 4000 : velocityX;
		velocityX = velocityX < -4000 ? -4000 : velocityX;
		velocityY = velocityY > 4000 ? 4000 : velocityY;
		velocityY = velocityY < -4000 ? -4000 : velocityY;
		// 根據橫向上的速度計算沿Y軸旋轉的角度
		angley += velocityX * ROTATE_FACTOR / 4000;
		// 根據縱向上的速度計算沿X軸旋轉的角度
		anglex += velocityY * ROTATE_FACTOR / 4000;
		return true;
	}

	@Override
	public boolean onDown(MotionEvent arg0)
	{
		return false;
	}

	@Override
	public void onLongPress(MotionEvent event)
	{
	}

	@Override
	public boolean onScroll(MotionEvent event1, MotionEvent event2,
		float distanceX, float distanceY)
	{
		return false;
	}

	@Override
	public void onShowPress(MotionEvent event)
	{
	}

	@Override
	public boolean onSingleTapUp(MotionEvent event)
	{
		return false;
	}

	public class MyRenderer implements Renderer
	{
		// 立方體的頂點座標(一共是36個頂點,組成12個三角形)
		private float[] cubeVertices = { -0.6f, -0.6f, -0.6f, -0.6f, 0.6f,
			-0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, -0.6f, -0.6f,
			-0.6f, -0.6f, -0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f,
			0.6f, 0.6f, 0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f,
			0.6f, -0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f,
			0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f, 0.6f,
			-0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f,
			0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f,
			-0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f,
			0.6f, 0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f,
			-0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f, 0.6f,
			-0.6f, 0.6f, -0.6f, };
		// 定義立方體所需要的6個面(一共是12個三角形所需的頂點)
		private byte[] cubeFacets = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
			13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
			30, 31, 32, 33, 34, 35, };
		// 定義紋理貼圖的座標資料
		private float[] cubeTextures = { 1.0000f, 1.0000f, 1.0000f, 0.0000f,
			0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f,
			1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
			1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f,
			1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f,
			0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f,
			1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f,
			0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f,
			0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
			0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f,
			0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f };

		private Context context;
		private FloatBuffer cubeVerticesBuffer;
		private ByteBuffer cubeFacetsBuffer;
		private FloatBuffer cubeTexturesBuffer;
		// 定義本程式所使用的紋理
		private int texture;

		public MyRenderer(Context main)
		{
			this.context = main;
			// 將立方體的頂點位置資料陣列包裝成FloatBuffer;
			cubeVerticesBuffer = FloatBuffer.wrap(cubeVertices);
			// 將立方體的6個面(12個三角形)的陣列包裝成ByteBuffer
			cubeFacetsBuffer = ByteBuffer.wrap(cubeFacets);
			// 將立方體的紋理貼圖的座標資料包裝成FloatBuffer
			cubeTexturesBuffer = FloatBuffer.wrap(cubeTextures);
		}

		@Override
		public void onSurfaceCreated(GL10 gl, EGLConfig config)
		{
			// 關閉抗抖動
			gl.glDisable(GL10.GL_DITHER);
			// 設定系統對透視進行修正
			gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
			gl.glClearColor(0, 0, 0, 0);
			// 設定陰影平滑模式
			gl.glShadeModel(GL10.GL_SMOOTH);
			// 啟用深度測試
			gl.glEnable(GL10.GL_DEPTH_TEST);
			// 設定深度測試的型別
			gl.glDepthFunc(GL10.GL_LEQUAL);
			// 啟用2D紋理貼圖
			gl.glEnable(GL10.GL_TEXTURE_2D);
			// 裝載紋理
			loadTexture(gl);
		}

		@Override
		public void onSurfaceChanged(GL10 gl, int width, int height)
		{
			// 設定3D視窗的大小及位置
			gl.glViewport(0, 0, width, height);
			// 將當前矩陣模式設為投影矩陣
			gl.glMatrixMode(GL10.GL_PROJECTION);
			// 初始化單位矩陣
			gl.glLoadIdentity();
			// 計算透視視窗的寬度、高度比
			float ratio = (float) width / height;
			// 呼叫此方法設定透視視窗的空間大小。
			gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
		}

		public void onDrawFrame(GL10 gl)
		{
			// 清除螢幕快取和深度快取
			gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
			// 啟用頂點座標資料
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			// 啟用貼圖座標陣列資料
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			// 設定當前矩陣模式為模型檢視。
			gl.glMatrixMode(GL10.GL_MODELVIEW);
			// --------------------繪製第一個圖形---------------------
			gl.glLoadIdentity();
			// 把繪圖中心移入螢幕2個單位
			gl.glTranslatef(0f, 0.0f, -2.0f);
			// 旋轉圖形
			gl.glRotatef(angley, 0, 1, 0);
			gl.glRotatef(anglex, 1, 0, 0);
			// 設定頂點的位置資料
			gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeVerticesBuffer);
			// 設定貼圖的的座標資料
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, cubeTexturesBuffer);
			// 執行紋理貼圖
			gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
			// 按cubeFacetsBuffer指定的面繪製三角形
			gl.glDrawElements(GL10.GL_TRIANGLES, cubeFacetsBuffer.remaining(),
				GL10.GL_UNSIGNED_BYTE, cubeFacetsBuffer);
			// 繪製結束
			gl.glFinish();
			// 禁用頂點、紋理座標陣列
			gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			// 遞增角度值以便每次以不同角度繪製
		}

		private void loadTexture(GL10 gl)
		{
			Bitmap bitmap = null;
			try
			{
				// 載入點陣圖
				bitmap = BitmapFactory.decodeResource(context.getResources(),
					R.drawable.sand);
				int[] textures = new int[1];
				// 指定生成N個紋理(第一個引數指定生成1個紋理),
				// textures陣列將負責儲存所有紋理的代號。
				gl.glGenTextures(1, textures, 0);
				// 獲取textures紋理陣列中的第一個紋理
				texture = textures[0];
				// 通知OpenGL將texture紋理繫結到GL10.GL_TEXTURE_2D目標中
				gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
				// 設定紋理被縮小(距離視點很遠時被縮小)時候的濾波方式
				gl.glTexParameterf(GL10.GL_TEXTURE_2D,
					GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
				// 設定紋理被放大(距離視點很近時被方法)時候的濾波方式
				gl.glTexParameterf(GL10.GL_TEXTURE_2D,
					GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
				// 設定在橫向、縱向上都是平鋪紋理
				gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
					GL10.GL_REPEAT);
				gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
					GL10.GL_REPEAT);
				// 載入點陣圖生成紋理
				GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
			}
			finally
			{
				// 生成紋理之後,回收點陣圖
				if (bitmap != null)
					bitmap.recycle();
			}
		}
	}
}