【OpenGL ES】 Android OpenGL ES -- 透视投影 和 正交投影
?
博客地址 :?http://blog.csdn.net/shulianghan/article/details/46680803
源碼下載 :?http://download.csdn.net/detail/han1202012/8903437
?
?
正交投影效果 :?
?
透視投影效果 :?
?
?
?
?
一. 投影簡介
?
?
?
?
?
1. 攝像機位置
?
?
?
?
攝像機參數 :?
-- 攝像機位置 : 攝像機的 三維坐標位置 x, y, z 坐標;
-- 觀察方向 : 攝像機鏡頭的朝向, 是一個三維向量, 指向一個三維坐標方向;
-- up 方向 : 有了位置 和 朝向, 此時攝像機可以 360 度旋轉, 這是我們需要一個 up 方向, 將攝像機固定在一個位置一個方向;
?
設置攝像機的方法 :?
?
void android.opengl.Matrix.setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)--?float[] rm 參數 : 生成矩陣元素的 float[] 數組;
?
--?int rmOffset 參數 : 矩陣元素數組的起始偏移量;
--?float eyeX, float eyeY, float eyeZ 參數 : 攝像機位置的 x, y, z 三維坐標;
--?float centerX, float centerY, float centerZ 參數 : 攝像機鏡頭朝向的點, 該點與攝像機位置連線的方向就是攝像機方向;
--?float upX, float upY, float upZ 參數 : 攝像機 up 方向, 該點與攝像機連線的方向, 就是攝像機的 up 方向;
?
?
?
2. 正交投影簡介
?
?
?
?
投影簡介 :?
-- 視景體 : 管線會確定的一個可視空間區域, 由 上平面(up), 下平面(down), 左平面(left), 右平面(right), 遠平面(far), 近平面(near) 六個平面組成;
-- 視景體與投影 : 視景體內的物體會投影到近平面, 視景體之外的內容會被裁減掉, 例如眼睛看不到的范圍就是處于視景體外即被裁減掉的;
?
正交投影 : 正交投影屬于平行投影, 投影線平行, 視景體是長方形的, 投影的內容不會出現近大遠小的效果;
-- 投影線 : 物體頂點 與 近平面的對應的物體頂點 投影的連線;
?
正交投影方法 : Matrix.orthoM() 方法設置正交投影;
?
void android.opengl.Matrix.orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)--?float[] m 參數 : 生成矩陣元素的 float[] 數組;
?
--?int mOffset 參數 : 矩陣數組的起始偏移量;
--?float left, float right, float bottom, float top 參數 : 近平面的 左, 右, 下, 上 的值;
--?float near 參數 : 近平面 與 視點之間的距離;
-- float far 參數 : 遠平面 與 視點之間的距離;
?
視口 : 視景體中的物體投影到近平面后, 最終會映射到顯示屏的視口中, 視口就相當于眼睛 或者 手機屏幕的一部分;
-- 說明 : 視口并不是占手機全部屏幕, 是顯示投影的部分, 也可以是一個 View 組件;
?
視口設置方法 :?
?
void android.opengl.GLES20.glViewport(int x, int y, int width, int height)--?int x, int y 參數 : x, y 是視口在手機屏幕左上角的坐標;
?
--?int width, int height 參數 : 視口的寬度 與 高度;
?
?
3. 透視投影簡介
?
?
?
透視投影 : 與現實世界觀察物體一樣, 有 近大遠小 的效果, 這種投影更加真實;
-- 投影線介紹 : 透視投影的投影線不平行, 相交于視點;
-- 視景體 : 透視投影中視景體是錐臺形區域;
-- 用處 : 所有的 3D 游戲都采用了透視投影的效果, 我們控制物體向前行走, 遠處的物體不斷變大就是這種效果;
?
?
?
?
?
?
二. 正交透視投影源碼詳解
?
?
?
?
?
?
1. 源碼結構詳解
?
?
?
源碼組成 :?
--?MatrixState : 矩陣相關的輔助類;
--?OrthogonalProjectionActivity : 顯示具體 OpenGL 圖像的 Activity;
--?ProjectionGLSurfaceView : 自定義的 GLSurfaceView, 該 View 可以顯示 OpenGL 圖像內容;
--?ShaderUtil : 著色器工具類;
--?SixPointedStar : 具體的圖形類, 如何生成該圖形;
?
?
?
?
2. MatrixState 詳解
?
?
?
?
?
(1) 設置攝像機參數
?
?
?
設置攝像機參數 :?
-- 相關內容 :
?
/*** 設置攝像機的參數* * @param cx* 攝像機位置的 x 坐標* @param cy* 攝像機位置的 y 坐標* @param cz* 攝像機位置的 z 坐標* @param tx* 攝像機朝向 x 坐標* @param ty* 攝像機朝向 y 坐標* @param tz* 攝像機朝向 z 坐標* @param upx* 攝像機上方朝向 x 坐標* @param upy* 攝像機上方朝向 y 坐標* @param upz* 攝像機上方朝向 z 坐標*/public static void setCamera(float cx, float cy, float cz, float tx,float ty, float tz, float upx, float upy, float upz) {Matrix.setLookAtM(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);}?
-- 方法作用 : 設置了攝像機的相關參數;
?
?
(2) 設置正交投影參數
?
?
?
代碼解析 :?
-- 作用 : 設置正交投影的近平面相關信息, 近平面與遠平面距離;
-- 代碼相關內容 :?
?
/*** 設置正交投影的參數* * @param left* 近平面的 left* @param right* 近平面的 right* @param bottom* 近平面的 bottom* @param top* 近平面的 top* @param near* 近平面的距離* @param far* 遠平面的距離*/public static void setProjectOrtho(float left, float right, float bottom,float top, float near, float far) {Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);}?
?
?
?
?
(3) 設置透視投影參數
?
?
?
代碼詳解 :?
-- 作用 : 設置透視投影 近平面 以及近平面 遠平面與視點間的距離;
-- 代碼內容 :?
?
/*** 設置正交投影的參數* * @param left* 近平面的 left* @param right* 近平面的 right* @param bottom* 近平面的 bottom* @param top* 近平面的 top* @param near* 近平面的距離* @param far* 遠平面的距離*/public static void setProjectOrtho(float left, float right, float bottom,float top, float near, float far) {Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);}?
?
?
?
?
?
(4) 獲取物體的總變換矩陣
?
?
?
代碼詳解 :?
-- 作用 : 將攝像機矩陣, 投影矩陣, 著色矩陣相乘, 就是最終矩陣;
-- 代碼內容 :?
?
/*** 獲取物體的總變換矩陣* * @param spec* @return*/public static float[] getFinalMatrix(float[] spec) {mMVPMatrix = new float[16];/** 矩陣乘法計算, 將兩個矩陣相乘, 并存入到第三個矩陣中* 六個參數 : * ①② 參數 : 結果矩陣, 結果矩陣起始位移* ③④ 參數 : 左矩陣, 結果矩陣起始位移* ⑤⑥ 參數 : 右矩陣, 結果矩陣起始位移*/Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);return mMVPMatrix;}?
?
?
?
?
?
(5)?MatrixState 源碼
?
?
?
源碼內容 :?
?
package cn.org.octopus.opengl.projection;import android.opengl.Matrix;/*** 存儲矩陣狀態的類* * @author octopus**/ public class MatrixState {private static float[] mProjMatrix = new float[16]; // 4x4矩陣 投影用private static float[] mVMatrix = new float[16]; // 攝像機位置朝向9參數矩陣private static float[] mMVPMatrix; // 最后起作用的總變換矩陣/*** 設置攝像機的參數* * @param cx* 攝像機位置的 x 坐標* @param cy* 攝像機位置的 y 坐標* @param cz* 攝像機位置的 z 坐標* @param tx* 攝像機朝向 x 坐標* @param ty* 攝像機朝向 y 坐標* @param tz* 攝像機朝向 z 坐標* @param upx* 攝像機上方朝向 x 坐標* @param upy* 攝像機上方朝向 y 坐標* @param upz* 攝像機上方朝向 z 坐標*/public static void setCamera(float cx, float cy, float cz, float tx,float ty, float tz, float upx, float upy, float upz) {Matrix.setLookAtM(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);}/*** 設置透視投影參數* * @param left* 近平面的 left* @param right* 近平面的 right* @param bottom* 近平面的 bottom* @param top* 近平面的 top* @param near* 近平面與視點的距離* @param far* 遠平面與視點的距離*/public static void setProjectFrustum(float left, float right, float bottom,float top, float near, float far) {Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);}/*** 設置正交投影的參數* * @param left* 近平面的 left* @param right* 近平面的 right* @param bottom* 近平面的 bottom* @param top* 近平面的 top* @param near* 近平面的距離* @param far* 遠平面的距離*/public static void setProjectOrtho(float left, float right, float bottom,float top, float near, float far) {Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);}/*** 獲取物體的總變換矩陣* * @param spec* @return*/public static float[] getFinalMatrix(float[] spec) {mMVPMatrix = new float[16];/** 矩陣乘法計算, 將兩個矩陣相乘, 并存入到第三個矩陣中* 六個參數 : * ①② 參數 : 結果矩陣, 結果矩陣起始位移* ③④ 參數 : 左矩陣, 結果矩陣起始位移* ⑤⑥ 參數 : 右矩陣, 結果矩陣起始位移*/Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);return mMVPMatrix;} }?
?
?
?
?
?
?
?
3.?ShaderUtil?著色工具詳解
?
?
?
該代碼在?http://blog.csdn.net/shulianghan/article/details/17020359 中詳細的講解;
?
?
(1) 源碼
?
?
ShaderUtil?源碼 :?
?
?
package cn.org.octopus.opengl.projection;import java.io.ByteArrayOutputStream; import java.io.InputStream;import android.content.res.Resources; import android.opengl.GLES20; import android.util.Log;/** 這個工具類用來加載定點著色器與片元著色器*/ public class ShaderUtil {/*** 加載著色器方法* * 流程 : * * ① 創建著色器* ② 加載著色器腳本* ③ 編譯著色器* ④ 獲取著色器編譯結果* * @param shaderType 著色器類型,頂點著色器(GLES20.GL_FRAGMENT_SHADER), 片元著色器(GLES20.GL_FRAGMENT_SHADER)* @param source 著色腳本字符串* @return 返回的是著色器的引用, 返回值可以代表加載的著色器*/public static int loadShader(int shaderType , String source){//1.創建一個著色器, 并記錄所創建的著色器的id, 如果id==0, 那么創建失敗int shader = GLES20.glCreateShader(shaderType);if(shader != 0){//2.如果著色器創建成功, 為創建的著色器加載腳本代碼GLES20.glShaderSource(shader, source);//3.編譯已經加載腳本代碼的著色器GLES20.glCompileShader(shader);int[] compiled = new int[1];//4.獲取著色器的編譯情況, 如果結果為0, 說明編譯失敗GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);if(compiled[0] == 0){Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));//編譯失敗的話, 刪除著色器, 并顯示logGLES20.glDeleteShader(shader);shader = 0;}}return shader;}/*** 檢查每一步的操作是否正確* * 使用GLES20.glGetError()方法可以獲取錯誤代碼, 如果錯誤代碼為0, 那么就沒有錯誤* * @param op 具體執行的方法名, 比如執行向著色程序中加入著色器, * 使glAttachShader()方法, 那么這個參數就是"glAttachShader"*/public static void checkGLError(String op){int error;//錯誤代碼不為0, 就打印錯誤日志, 并拋出異常while( (error = GLES20.glGetError()) != GLES20.GL_NO_ERROR ){Log.e("ES20_ERROR", op + ": glError " + error);throw new RuntimeException(op + ": glError " + error);}}/*** 創建著色程序* * ① 加載頂點著色器* ② 加載片元著色器* ③ 創建著色程序* ④ 向著色程序中加入頂點著色器* ⑤ 向著色程序中加入片元著色器* ⑥ 鏈接程序* ⑦ 獲取鏈接程序結果* * @param vertexSource 定點著色器腳本字符串* @param fragmentSource 片元著色器腳本字符串* @return*/public static int createProgram(String vertexSource , String fragmentSource){//1. 加載頂點著色器, 返回0說明加載失敗int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);if(vertexShader == 0)return 0;//2. 加載片元著色器, 返回0說明加載失敗int fragShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);if(fragShader == 0)return 0;//3. 創建著色程序, 返回0說明創建失敗int program = GLES20.glCreateProgram();if(program != 0){//4. 向著色程序中加入頂點著色器GLES20.glAttachShader(program, vertexShader);checkGLError("glAttachShader");//5. 向著色程序中加入片元著色器GLES20.glAttachShader(program, fragShader);checkGLError("glAttachShader");//6. 鏈接程序GLES20.glLinkProgram(program);int[] linkStatus = new int[1];//獲取鏈接程序結果GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);if(linkStatus[0] != GLES20.GL_TRUE){Log.e("ES20.ERROR", "鏈接程序失敗 : ");Log.e("ES20.ERROR", GLES20.glGetProgramInfoLog(program));//如果鏈接程序失敗刪除程序GLES20.glDeleteProgram(program);program = 0;} }return program;}/*** 從assets中加載著色腳本, 最終獲得一個著色器腳本字符串* * ① 打開assets目錄中的文件輸入流* ② 創建帶緩沖區的輸出流* ③ 逐個字節讀取文件數據, 放入緩沖區* ④ 將緩沖區中的數據轉為字符串* * @param fileName assets目錄中的著色腳本文件名* @param resources 應用的資源* @return*/public static String loadFromAssetsFile(String fileName, Resources resources){String result = null;try {//1. 打開assets目錄中讀取文件的輸入流, 相當于創建了一個文件的字節輸入流InputStream is = resources.getAssets().open(fileName);int ch = 0;//2. 創建一個帶緩沖區的輸出流, 每次讀取一個字節, 注意這里字節讀取用的是int類型ByteArrayOutputStream baos = new ByteArrayOutputStream();//3. 逐個字節讀取數據, 并將讀取的數據放入緩沖器中while((ch = is.read()) != -1){baos.write(ch);}//4. 將緩沖區中的數據轉為字節數組, 并將字節數組轉換為字符串byte[] buffer = baos.toByteArray();baos.close();is.close();result = new String(buffer, "UTF-8");result = result.replaceAll("\\r\\n", "\n");} catch (Exception e) {e.printStackTrace();}return result;} }?
?
?
?
?
?
?
4.?SixPointedStar?六角形形成類
?
?
?
?
(1) 源碼
?
?
?
?
package cn.org.octopus.opengl.projection;import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.List;import android.opengl.GLES20; import android.opengl.Matrix;/*** 單個六角星元素* * @author octopus**/ public class SixPointedStar {int mProgram; // 自定義渲染管線著色器程序idstatic float[] mMMatrix = new float[16]; // 具體物體的3D變換矩陣,包括旋轉、平移、縮放int muMVPMatrixHandle; // 總變換矩陣引用int maPositionHandle; // 頂點位置屬性引用int maColorHandle; // 頂點顏色屬性引用String mVertexShader; // 頂點著色器代碼腳本String mFragmentShader; // 片元著色器代碼腳本FloatBuffer mVertexBuffer; // 頂點坐標數據緩沖FloatBuffer mColorBuffer; // 頂點著色數據緩沖int vCount = 0; // 頂點個數public float yAngle = 0; // 繞y軸旋轉的角度public float xAngle = 0; // 繞z軸旋轉的角度final float UNIT_SIZE = 1;public SixPointedStar(ProjectionGLSurfaceView mv, float r, float R, float z) {// 調用初始化頂點數據的initVertexData方法initVertexData(R, r, z);// 調用初始化著色器的intShader方法initShader(mv);}/*** 自定義初始化頂點數據的initVertexData方法* @param R 外圓半徑, 最外面6個點組成的圓* @param r 內圓半徑, 最里面6個點組成的圓, 6個凹槽處的點* @param z 深度*/public void initVertexData(float R, float r, float z) {List<Float> flist = new ArrayList<Float>();float tempAngle = 360 / 6;// 每 60 度繪制一個四邊形, 每個四邊形由 2 個三角形組成, 箭頭形的平行四邊形for (float angle = 0; angle < 360; angle += tempAngle) {// 第一個三角形, (angle = 60度時, 這是處于 60 ~ 90度的三角形)// 第一個中心點, 正中心的點flist.add(0f); //屏幕中心flist.add(0f); //屏幕中心flist.add(z); //深度, z軸, 垂直于屏幕// 第二個點, (angle = 60度時 第一象限 60度 右上的點)flist.add((float) (R * UNIT_SIZE * Math.cos(Math.toRadians(angle)))); // 公式 : R / x = cos60, x = R * cos60flist.add((float) (R * UNIT_SIZE * Math.sin(Math.toRadians(angle)))); // 公式 : R / y = cos60, y = R * sin60flist.add(z); //深度// 第三個點, 順時針方向的三角形的另一個點flist.add((float) (r * UNIT_SIZE * Math.cos(Math.toRadians(angle + tempAngle / 2))));flist.add((float) (r * UNIT_SIZE * Math.sin(Math.toRadians(angle+ tempAngle / 2))));flist.add(z);// 第二個三角形// 第一個中心點, 最中心的點flist.add(0f);flist.add(0f);flist.add(z);// 第二個點, (angle = 60度時, 這是處于 90 ~ 120 的三角形)flist.add((float) (r * UNIT_SIZE * Math.cos(Math.toRadians(angle+ tempAngle / 2))));flist.add((float) (r * UNIT_SIZE * Math.sin(Math.toRadians(angle+ tempAngle / 2))));flist.add(z);// 第三個點flist.add((float) (R * UNIT_SIZE * Math.cos(Math.toRadians(angle+ tempAngle))));flist.add((float) (R * UNIT_SIZE * Math.sin(Math.toRadians(angle+ tempAngle))));flist.add(z);}//頂點個數, 集合個數 / 3vCount = flist.size() / 3;//創建一個頂點數組, 大小為頂點集合的大小, 將頂點數組的元素拷貝到頂點集合中float[] vertexArray = new float[flist.size()];for (int i = 0; i < vCount; i++) {vertexArray[i * 3] = flist.get(i * 3);vertexArray[i * 3 + 1] = flist.get(i * 3 + 1);vertexArray[i * 3 + 2] = flist.get(i * 3 + 2);}//創建一個字節數組緩沖, 大小為 頂點個數 * 4ByteBuffer vbb = ByteBuffer.allocateDirect(vertexArray.length * 4);// 設置字節順序為本地操作系統順序vbb.order(ByteOrder.nativeOrder()); //將 byte 緩沖 轉為 float 緩沖, 賦值給 頂點數據緩沖mVertexBuffer = vbb.asFloatBuffer();mVertexBuffer.put(vertexArray);//設置緩沖區的起始位置mVertexBuffer.position(0);/** 下面是初始化頂點顏色數據*///共有 vCount 個頂點, 每個頂點顏色值是 4個分別是 RGBAfloat[] colorArray = new float[vCount * 4];//中心點設置一個顏色, 其它點設置一個顏色for (int i = 0; i < vCount; i++) {if (i % 3 == 0) {// 中心點為白色colorArray[i * 4] = 1;colorArray[i * 4 + 1] = 1;colorArray[i * 4 + 2] = 1;colorArray[i * 4 + 3] = 0;} else {// 邊上的點為淡藍色colorArray[i * 4] = 0.45f;colorArray[i * 4 + 1] = 0.75f;colorArray[i * 4 + 2] = 0.75f;colorArray[i * 4 + 3] = 0;}}ByteBuffer cbb = ByteBuffer.allocateDirect(colorArray.length * 4);cbb.order(ByteOrder.nativeOrder()); // 設置字節順序為本地操作系統順序//將顏色Byte緩沖轉為 Float緩沖mColorBuffer = cbb.asFloatBuffer();//將顏色緩沖數據放入 顏色數據緩沖成員變量中mColorBuffer.put(colorArray);mColorBuffer.position(0);// 特別提示:由于不同平臺字節順序不同數據單元不是字節的一定要經過ByteBuffer// 轉換,關鍵是要通過ByteOrder設置nativeOrder(),否則有可能會出問題}/*** 初始化著色器* ① 加載頂點著色器與片元著色器腳本* ② 基于加載的著色器創建著色程序* ③ 根據著色程序獲取 頂點屬性引用 頂點顏色引用 總變換矩陣引用* @param mv*/public void initShader(ProjectionGLSurfaceView mv) {/* * mVertextShader是頂點著色器腳本代碼 * 調用工具類方法獲取著色器腳本代碼, 著色器腳本代碼放在assets目錄中 * 傳入的兩個參數是 腳本名稱 和 應用的資源 * 應用資源Resources就是res目錄下的那寫文件 */ //① 加載頂點著色器的腳本內容mVertexShader = ShaderUtil.loadFromAssetsFile("vertex_projection.sh",mv.getResources());//② 加載片元著色器的腳本內容mFragmentShader = ShaderUtil.loadFromAssetsFile("frag_projection.sh",mv.getResources());//③ 基于頂點著色器與片元著色器創建程序, 傳入頂點著色器腳本 和 片元著色器腳本 注意順序不要錯mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);/* * 從著色程序中獲取 屬性變量 頂點坐標(顏色)數據的引用 * 其中的"aPosition"是頂點著色器中的頂點位置信息 * 其中的"aColor"是頂點著色器的顏色信息 */ //④ 獲取程序中頂點位置屬性引用idmaPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");//⑤ 獲取程序中頂點顏色屬性引用idmaColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");//⑥ 獲取程序中總變換矩陣引用idmuMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");}/*** 六角星繪制自身方法* * ① 設置繪制使用的著色程序* ② 初始化總變換矩陣* ③ 設置位移* ④ 設置旋轉* ⑤ 應用最終變換矩陣* ⑥ 指定頂點與顏色位置緩沖數據* ⑦ 開始繪制*/public void drawSelf() {// 制定使用某套shader程序GLES20.glUseProgram(mProgram);// 初始化變換矩陣, 第二參數是矩陣起始位, 第三參數 旋轉的角度, 四五六參數 旋轉的軸Matrix.setRotateM(mMMatrix, 0, 0, 0, 1, 0);// 設置沿Z軸正向位移1Matrix.translateM(mMMatrix, 0, 0, 0, 1);// 設置繞y軸旋轉Matrix.rotateM(mMMatrix, 0, yAngle, 0, 1, 0);// 設置繞z軸旋轉Matrix.rotateM(mMMatrix, 0, xAngle, 1, 0, 0);// 將最終變換矩陣傳入shader程序GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false,MatrixState.getFinalMatrix(mMMatrix), 0);// 為畫筆指定頂點位置數據GLES20.glVertexAttribPointer(maPositionHandle, // 頂點位置數據引用3, // 每 3 個元素代表一個坐標GLES20.GL_FLOAT, // 坐標的單位是浮點型false, // 3 * 4, // 每組數據有多少字節mVertexBuffer); // 頂點數據緩沖區// 為畫筆指定頂點著色數據GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false,4 * 4, mColorBuffer);// 允許頂點位置數據數組GLES20.glEnableVertexAttribArray(maPositionHandle);GLES20.glEnableVertexAttribArray(maColorHandle);// 繪制六角星GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);} }?
?
?
?
?
?
?
5.?ProjectionGLSurfaceView?自定義View顯示類
?
?
?
?
?
(1) 正交透視投影設置
?
?
?
關鍵成員變量 :?
?
public static boolean isOrth-- 正交投影 : 設置為 true, 時為正交投影;
?
-- 透視投影 : 設置為 false 時, 為透視投影;
?
?
(3) 源碼
?
?
?
?
源碼 :?
?
package cn.org.octopus.opengl.projection;import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10;import android.content.Context; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.view.MotionEvent;/*** 自定義顯示 OpenGL 圖形的 SurfaceView* * ① 初始化 SurfaceView* a. 設置 OpenGL ES 版本* b. 創建場景渲染器* c. 設置場景渲染器* d. 設置場景渲染器模式 * ② 自定義場景渲染器* a. 創建時 設置背景 -> 創建繪制元素 -> 打開深度檢測* b. 場景改變時 設置視口參數 -> 設置投影參數 -> 設置攝像機參數* c. 繪制時 清楚顏色,深度緩沖 -> 繪制元素* @author octopus**/ public class ProjectionGLSurfaceView extends GLSurfaceView {public static boolean isOrth = true;private final float TOUCH_SCALE_FACTOR = 180.0f / 320; // 角度縮放比例private SceneRenderer mRenderer; // 場景渲染器private float mPreviousY; //上次觸摸位置的Y坐標private float mPreviousX; //上次觸摸位置的X坐標/*** 初始化 GLSurfaceView* ① 設置 OpenGL ES 的版本* ② 創建場景渲染器* ③ 設置場景渲染器* ④ 設置場景渲染模式* @param context*/public ProjectionGLSurfaceView(Context context) {super(context);this.setEGLContextClientVersion(2); // 設置OpenGL ES 版本為 2.0mRenderer = new SceneRenderer(); // 創建場景渲染器setRenderer(mRenderer); // 設置場景渲染器setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); // 設置場景渲染模式}// 觸摸方法@Overridepublic boolean onTouchEvent(MotionEvent e) {float y = e.getY(); //獲取當前觸摸的 y 坐標float x = e.getX(); //獲取當前觸摸的 x 坐標switch (e.getAction()) { //獲取觸摸類型case MotionEvent.ACTION_MOVE:float dy = y - mPreviousY;// 計算 y 方向的位移float dx = x - mPreviousX;// 計算 x 方向的位移for (SixPointedStar h : mRenderer.ha) {h.yAngle += dx * TOUCH_SCALE_FACTOR;// 設置六角星繞 x 軸旋轉角度h.xAngle += dy * TOUCH_SCALE_FACTOR;// 設置六角星繞 y 軸旋轉角度}}mPreviousY = y;// 將本次觸摸的 y 坐標記錄為歷史坐標mPreviousX = x;// 將本次觸摸的 x 坐標記錄為歷史坐標return true;}/*** 場景渲染器* 創建六角星數組中得六角星對象, 將六角星顯示在屏幕中* @author octopus**/private class SceneRenderer implements GLSurfaceView.Renderer {SixPointedStar[] ha = new SixPointedStar[6];// 六角星數組/*** ① 清楚深度緩沖 與 顏色緩沖* ② 重新繪制各個元素*/public void onDrawFrame(GL10 gl) {// 清除深度緩沖與顏色緩沖GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT| GLES20.GL_COLOR_BUFFER_BIT);// 循環繪制各個六角星for (SixPointedStar h : ha) {h.drawSelf();}}/*** Surface 改變時* ① 設置視口參數* ② 設置投影參數* ③ 設置攝像機參數*/public void onSurfaceChanged(GL10 gl, int width, int height) {// 設置視口的大小及位置GLES20.glViewport(0, 0, width, height);// 設置視口的寬高比, 注意視口的長寬比與近平面的長寬比需要相同, 否則顯示內容會變形float ratio = (float) width / height;// 設置正交投影, 如果是透視投影, 就在這里使用透視投影if(isOrth){//設置正交投影MatrixState.setProjectOrtho(-ratio, ratio, -1, 1, 1, 10);}else{//設置透視投影MatrixState.setProjectFrustum(-ratio*0.4f, ratio*0.4f, -1*0.4f, 1*0.4f, 1, 50);}// 設置攝像機位置MatrixState.setCamera(0, 0, 3f, 0, 0, 0f, 0f, 1.0f, 0.0f);}/*** 創建時回調* ① 設置北京顏色* ② 創建繪制元素* ③ 打開深度檢測*/public void onSurfaceCreated(GL10 gl, EGLConfig config) {// 設置屏幕的背景顏色GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);float distance = 0f;if(isOrth){distance = -1.0f;}else{distance = -1.0f;}// 創建六角星數組中得各個六角星for (int i = 0; i < ha.length; i++) {ha[i] = new SixPointedStar(ProjectionGLSurfaceView.this, 0.2f, 0.5f,distance * i);}// 打開深度檢測GLES20.glEnable(GLES20.GL_DEPTH_TEST);}} }?
?
?
?
?
?
?
6.?OrthogonalProjectionActivity?類
?
?
?
?
?
源碼 :?
?
package cn.org.octopus.opengl.projection; import cn.org.octopus.opengl.R; import cn.org.octopus.opengl.utils.DLog; import android.app.Activity; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout;/*** Activity 顯示 OpenGL 流程* ① 設置屏幕參數* ② 初始化 GLSurfaceView* ③ 設置顯示 GLSurface* * 在onResume 和 onPause 中分別調用 GLSurfaceView 的 onResume 和 onPause 方法* @author octopus**/ public class OrthogonalProjectionActivity extends Activity {public static final String TAG = "octopus.OrthogonalProjectionActivity";private ProjectionGLSurfaceView mGLSurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); //① 設置屏幕參數requestWindowFeature(Window.FEATURE_NO_TITLE); //設置無標題getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN , //設置全屏充滿WindowManager.LayoutParams.FLAG_FULLSCREEN);setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //設置屏幕為豎屏//② 初始化GLSurfaceViewmGLSurfaceView = new ProjectionGLSurfaceView(this);//③ 設置顯示 GLSurfaceViewsetContentView(mGLSurfaceView); //設置界面顯示該 GLSurfaceViewmGLSurfaceView.requestFocus(); //獲取焦點mGLSurfaceView.setFocusableInTouchMode(true); //設置為可觸控 }public void onClick(View view) {DLog.i(TAG, "點擊了按鈕");int id = view.getId();switch (id) {case R.id.bt_switch_orth:ProjectionGLSurfaceView.isOrth = true;break;case R.id.bt_switch_flu:ProjectionGLSurfaceView.isOrth = false;break;default:break;}}@Overrideprotected void onResume() {super.onResume();mGLSurfaceView.onResume(); // GLSurfaceView 根據 Acivity 周期變化}@Overrideprotected void onPause() {super.onPause();mGLSurfaceView.onPause(); // GLSurfaceView 根據 Acivity 周期變化} }?
?
?
?
?
?
?
?
.
?
博客地址?:?http://blog.csdn.net/shulianghan/article/details/46680803
源碼下載?:?http://download.csdn.net/detail/han1202012/8903437
?
博客地址?:?http://blog.csdn.net/shulianghan/article/details/46680803
源碼下載?:?http://download.csdn.net/detail/han1202012/8903437
?
總結
以上是生活随笔為你收集整理的【OpenGL ES】 Android OpenGL ES -- 透视投影 和 正交投影的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android应用开发】 Androi
- 下一篇: 【APUE】文件 I/O 操作