視野限制了人對這個宇宙的認知,但沒有視野,人將會一無所知
上集說到勇者墜入
黑暗之淵
,憑藉對世界的認知構建出了世界系
到此為止,OpenGL的世界觀已經映入腦海,新手十二副本已經通過
接下來等待他們的將是什麼,普通副本開啟….
普通副本一:斗轉星移
第一關卡:繪製矩形
這關簡單,回頭看看,是不是感覺清晰了許多,下面列一下關鍵點,其他不說了
這個矩形可不是一開始摸黑瞎畫的,而是軸系下可感的矩形,地址:matrix.MatrixRectangle
視角移回正方向0f, 0f, -6
,這時候畫出的是後面
//頂點座標private float mVertex[] = {
//以逆時針順序 -1f, 1f, 1.0f, // p0 -1f, -1f, 1.0f, // p1 1f, -1f, 1.0f, // p2 1f, 1f, 1.0f, //p3
};
//索引陣列private short[] idx = {
0, 1, 3, 1, 2, 3
};
//頂點顏色float colors[] = new float[]{
1f, 1f, 0.0f, 1f,//黃 0.05882353f, 0.09411765f, 0.9372549f,1f,//藍 0.19607843f, 1.0f, 0.02745098f, 1f,//綠 1.0f, 0.0f, 1.0f,1f,//紫色
};
複製程式碼
2.第二關卡:封裝矩陣變換
檢視的矩陣變換和投影矩陣感覺在
WorldRenderer
裡也有點麻煩
封裝一下吧,還是那四個矩陣
/** * 作者:張風捷特烈<
br/>
* 時間:2019/1/14/014:11:29<
br/>
* 郵箱:1981462002@qq.com<
br/>
* 說明:矩陣變化棧 */public class MatrixStack {
private static float[] mProjectionMatrix = new float[16];
//投影矩陣 private static float[] mViewMatrix = new float[16];
//相機矩陣 private static float[] mOpMatrix;
//變換矩陣 //獲取具體物體的總變換矩陣 private static float[] mMVPMatrix = new float[16];
/** * 獲取不變換初始矩陣 */ public static void reset() {
//mOpMatrix轉化為單位陣 mOpMatrix = new float[]{
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
};
} /** * 設定沿xyz軸移動 * * @param x 移動的 x 分量 * @param y 移動的 y 分量 * @param z 移動的 z 分量 */ public static void translate(float x, float y, float z) {
Matrix.translateM(mOpMatrix, 0, x, y, z);
} /** * 設定沿(x,y,z)點旋轉 * * @param deg 角度 * @param x 旋轉點的 x 分量 * @param y 旋轉點的 y 分量 * @param z 旋轉點的 z 分量 */ public static void rotate(float deg, float x, float y, float z) {
Matrix.rotateM(mOpMatrix, 0, deg, x, y, z);
} /** * 設定縮放 * @param x 縮放的 x 分量 * @param y 縮放的 y 分量 * @param z 縮放的 z 分量 */ public static void scale(float x, float y, float z) {
Matrix.scaleM(mOpMatrix, 0, x, y, z);
} /** * 相機的位置 * @param cx 攝像機位置x * @param cy 攝像機位置y * @param cz 攝像機位置z * @param tx 攝像機目標點x * @param ty 攝像機目標點y * @param tz 攝像機目標點z * @param upx 攝像機UP向量X分量 * @param upy 攝像機UP向量Y分量 * @param upz 攝像機UP向量Z分量 */ public static void lookAt(float cx, float cy, float cz, float tx, float ty, float tz, float upx, float upy, float upz) {
Matrix.setLookAtM(mViewMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);
} /** * 設定透視投影引數 * @param left near面的left * @param right near面的right * @param bottom near面的bottom * @param top near面的top * @param near near面距頂點長 * @param far far面距頂點長 */ public static void frustum( float left, float right, float bottom, float top, float near, float far) {
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
} /** * 檢視棧頂的變換矩陣 * * @return mMVPMatrix */ public static float[] peek() {
submit();
return mMVPMatrix;
} /** * 提交變換 */ private static void submit() {
Matrix.multiplyMM( mMVPMatrix, 0, mViewMatrix, 0, mOpMatrix, 0);
Matrix.multiplyMM( mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
} //獲取具體物體的變換矩陣 public static float[] getOpMatrix() {
return mOpMatrix;
}
}複製程式碼
MatrixStack使用
---->
[WorldRenderer#onSurfaceChanged]------------MatrixStack.frustum( -ratio, ratio, -1, 1, 3, 9);
MatrixStack.lookAt(2, 2, -6, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
MatrixStack.initStack();
---->
[WorldRenderer#onDrawFrame]------------MatrixStack.rotate(currDeg, 0, 1, 0);
mWorldShape.draw(MatrixStack.peek());
複製程式碼
3.第三關卡:操作矩陣的狀態棧
想一下,如果我平移畫一個立方,mOpMatrix會變化,
我再平移畫一個立方時mOpMatrix會在上一個mOpMatrix的基礎上進行變換
這種情況下我們是希望mOpMatrix在畫完後回到之前狀態的,這就涉及棧的概念
此處沒用Java的Stack類,因為元素是操作矩陣,即float[],不好放
3.1:沒有恢復狀態時
一個x方向-1.5,另一個x方向+1.5
MatrixStack.translate(-1.5f, 0, 0);
mWorldShape.draw(MatrixStack.peek());
MatrixStack.translate(1.5f, 0, 0);
mWorldShape.draw(MatrixStack.peek());
複製程式碼
3.2:MatrixStack新增恢復機制
//預設棧深為10,棧中元素為float[16]--即變換矩陣private static float[][] mStack = new float[10][16];
private static int stackTop = -1;
//棧頂無元素時為-1/** * 矩陣操作準備入棧---儲存mOpMatrix狀態 */public static void save() {
stackTop++;
//棧頂+1 //op矩陣入棧頂 System.arraycopy(mOpMatrix, 0, mStack[stackTop], 0, 16);
}/** * 棧頂出棧--恢復mOpMatrix */public static void restore() {
System.arraycopy(mStack[stackTop], 0, mOpMatrix, 0, 16);
stackTop--;
//棧頂下移
}複製程式碼
3.3:使用狀態恢復機制
一個x方向-1.5,另一個x方向+1.5,這才是我們想要的效果
MatrixStack.save();
MatrixStack.translate(-1.5f, 0, 0);
mWorldShape.draw(MatrixStack.peek());
MatrixStack.restore();
MatrixStack.save();
MatrixStack.translate(1.5f, 0, 0);
mWorldShape.draw(MatrixStack.peek());
MatrixStack.restore();
複製程式碼
4.第四關卡:總結移動旋轉縮放
4.1:注意setRotateM
與rotateM
的區別
一開始寫成setRotateM了,效果疊加不上,然後debug一下,發現:
原始碼中setRotateM會將一些之置零或1,也就是重置之前的變換,所以
public static void setRotateM(float[] rm, int rmOffset, float a, float x, float y, float z) {
rm[rmOffset + 3] = 0;
rm[rmOffset + 7] = 0;
rm[rmOffset + 11]= 0;
rm[rmOffset + 12]= 0;
rm[rmOffset + 13]= 0;
rm[rmOffset + 14]= 0;
rm[rmOffset + 15]= 1;
複製程式碼
4.2.使用:
MatrixStack.save();
MatrixStack.translate(-1.5f, 0, 0);
MatrixStack.rotate(currDeg, -1.5f, 0, 0);
mWorldShape.draw(MatrixStack.peek());
MatrixStack.restore();
MatrixStack.save();
MatrixStack.translate(1.5f, 0, 0);
MatrixStack.rotate(currDeg, 0, 1, 0);
MatrixStack.scale(0.5f,1,0.5f);
mWorldShape.draw(MatrixStack.peek());
MatrixStack.restore();
複製程式碼
普通副本二:變心之陣
剛才我們用的是Matrix自帶的變換方法,自帶的靈活性肯定欠佳
下面我們來看一下這16個數字是怎麼讓圖形變換的
1.第一關卡:Matrix.translateM分析
怎麼分析呢?廢話,當然是看原始碼了
/** * Translates matrix m by x, y, and z in place. * * @param m matrix * @param mOffset index into m where the matrix starts * @param x translation factor x * @param y translation factor y * @param z translation factor z */public static void translateM( float[] m, int mOffset, float x, float y, float z) {
for (int i=0 ;
i<
4 ;
i++) {
int mi = mOffset + i;
m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;
}
}複製程式碼
mOffset可以看出是索引的偏移量,正常情況下都是0,所以不管他
核心的就是四個for內語句,i從0~3,然後就是這句迷之程式碼
m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;
mOffset為0時,簡化一下:m[12 + i] += m[i] * x + m[4 + i] * y + m[8 + i] * z;
感覺還是迷之程式碼的,沒關係,再分析一下i=0: m[12] += m[0]*x + m[4]*y + m[8]*zi=1: m[13] += m[1]*x + m[5]*y + m[9]*zi=2: m[14] += m[2]*x + m[6]*y + m[10]*zi=3: m[15] += m[3]*x + m[7]*y + m[11]*zWhy ?複製程式碼
2.第二關卡:座標變換的源頭
所以的根源在於這句話,它代表了什麼意思?
注意:OpenGL 中的向量是列主元,也就是豎著排
然後算出gl_Position的結果會是一個四維向量
這也就是m12,m13,m14,m15為什麼特別
,m0,m1,m2,m3為什麼和x息息相關
再換個角度來看,gl_Position中的四個維度分別代表:x,y,z,wgl_Position.x = m0*x + m4*y + m8*z + m12gl_Position.y = m1*x + m5*y + m9*z + m13gl_Position.z = m2*x + m6*y + m10*z + m14 這也說明 m12 m13 m14 三個數分別控制x,y,z的位移 複製程式碼
現在再看translateM,可以看出它的作用是改變第四行
我們傳入的是行向量,傳入渲染器中變成列向量,相當於轉置,
也就是處理時uMVPMatrix的第四列,這樣一來路就走通了,
translateM通過改變第四行的向量來操作頂點的位置,yes!
3.第三關卡:Matrix.scaleM分析
public static void scaleM(float[] m, int mOffset, float x, float y, float z) {
for (int i=0 ;
i<
4 ;
i++) {
int mi = mOffset + i;
m[ mi] *= x;
m[ 4 + mi] *= y;
m[ 8 + mi] *= z;
}
}這個就簡單了:i=0: m[0] = m[0]*x m[4] = m[4]*y m[8] = m[8]*zi=1: m[1] = m[1]*x m[5] = m[5]*y m[9] = m[9]*zi=2: m[2] = m[2]*x m[6] = m[4]*y m[10] = m[10]*zi=3: m[3] = m[3]*x m[7] = m[7]*y m[11] = m[11]*z>
這樣一看是不是豁然開朗複製程式碼
旋轉還是算了吧,沒幾張草稿紙還真算不清,原始碼看著也挺複雜
等以後哪天閒到懷疑人生的時候再來慢慢算算吧。
副本三:形之累變
在一個圓環上等分點,可以形成若干三角形,由此可以拼合出圖形
這個副本是為了練習一下規律型的運算,發現規律,加以使用
1.第一關卡:環
/** * 初始化頂點座標與顏色 * @param splitCount 分割點數 * @param r 內圓半徑 * @param R 外圈半徑 */public void initVertex(int splitCount, float r, float R) {
//頂點座標資料的初始化 verticeCount = splitCount * 2 + 2;
float[] vertices = new float[verticeCount * 3];
//座標資料 float thta = 360.f / splitCount;
for (int i = 0;
i <
vertices.length;
i += 3) {
int n = i / 3;
if (n % 2 == 0) {//偶數點--內圈 vertices[i] = (float) (r * Math.cos(Change.rad((n / 2) * thta)));
//x vertices[i + 1] = (float) (r * Math.sin(Change.rad((n / 2) * thta)));
//y vertices[i + 2] = 0;
//z
} else {//奇數點--外圈 vertices[i] = (float) (R * Math.cos(Change.rad((n / 2) * thta)));
//x vertices[i + 1] = (float) (R * Math.sin(Change.rad((n / 2) * thta)));
//y vertices[i + 2] = 0;
//z
}
} //i+8 表示 每次跨度兩個點 //橙色:0.972549f,0.5019608f,0.09411765f,1.0f float colors[] = new float[verticeCount * 4];
for (int i = 0;
i <
colors.length;
i += 8) {
colors[i + 0] = 1;
colors[i + 1] = 1;
colors[i + 2] = 1;
colors[i + 3] = 1;
colors[i + 4] = 0.972549f;
colors[i + 5] = 0.5019608f;
colors[i + 6] = 0.09411765f;
colors[i + 7] = 1.0f;
} vertexBuffer = GLUtil.getFloatBuffer(vertices);
mColorBuffer = GLUtil.getFloatBuffer(colors);
}複製程式碼
這種情況下,使用
GL_TRIANGLE_STRIP
時極好的,相鄰三點組成三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, verticeCount);
複製程式碼
2.第二關卡:GLES20.GL_TRIANGLE_FAN
三角形
fan 即扇子,一箇中心,連線其他頂點,好處是比較節省頂點
這樣可以繪製任意正多邊形
/** * 初始化頂點座標與顏色 * * @param splitCount 分割點數 * @param r 內圓半徑 */public void initVertex(int splitCount, float r) {
//頂點座標資料的初始化 verticeCount = splitCount + 2;
float[] vertices = new float[verticeCount * 3];
//座標資料 float thta = 360.f / splitCount;
vertices[0] = 0;
vertices[1] = 0;
vertices[2] = 0;
for (int n = 1;
n <
= verticeCount - 1;
n++) {
vertices[n * 3] = (float) (r * Math.cos(Change.rad((n - 1) * thta)));
//x vertices[n * 3 + 1] = (float) (r * Math.sin(Change.rad((n - 1) * thta)));
//y vertices[n * 3 + 2] = 0;
//z
} //頂點顏色 //橙色:0.972549f,0.5019608f,0.09411765f,1.0f float colors[] = new float[verticeCount * 4];
colors[0] = 1;
colors[1] = 1;
colors[2] = 1;
colors[3] = 1;
for (int i = 1;
i <
= verticeCount - 1;
i++) {
colors[4 * i] = 0.972549f;
colors[4 * i + 1] = 0.5019608f;
colors[4 * i + 2] = 0.09411765f;
colors[4 * i + 3] = 1.0f;
} vertexBuffer = GLUtil.getFloatBuffer(vertices);
mColorBuffer = GLUtil.getFloatBuffer(colors);
}複製程式碼
繪製時使用:
GLES20.GL_TRIANGLE_FAN
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, verticeCount);
複製程式碼
3.第三關卡:總結一下幾種繪製方式
glDrawArrays:---->
[繪製點線]-------GLES20.GL_POINTS 繪製點GLES20.GL_LINES 兩點一線GLES20.GL_LINE_STRIP 相鄰兩點一線(不連首尾)GLES20.GL_LINE_LOOP 相鄰兩點一線(連首尾)---->
[繪製三角形]-------GLES20.GL_TRIANGLES 三點一個GLES20.GL_TRIANGLE_STRIP 相鄰三點一個GLES20.GL_TRIANGLE_FAN 第一點中心,散射到其他點glDrawElements:---->
[頂點索引繪製]------GLES20.glDrawElements(GLES20.GL_TRIANGLES, idx.length, GLES20.GL_UNSIGNED_SHORT, idxBuffer);
複製程式碼
普通副本四:世界之幕
1.第一關卡:再看投影矩陣
下面是
視點:(2,2,-6) far 9 near 3
的單位立方,結合上面的圖來看看:
near 和 far兩個面決定了視野,即兩面間的內容可見
near面的上下左右的長度也決定這物體的高矮胖瘦,比如左右減半後,
你應該能想了視野被”壓扁”了,物體也會隨之變扁
MatrixStack.frustum( -ratio/2, ratio/2, -1, 1, 3f, 9);
複製程式碼
2.第二關卡:near面與平移變換
near面越近,成像越小,你可以根據那個圖,自己想想
當觀察到的事物在near和far面組成的稜臺之外,便不可見
所以說眼睛限制了你對這個宇宙的認知,但沒有眼睛你會一無所知
3.平行投影
這個比較簡單,也就是沒有透視,立方的對線都是平行的,引數和透視投影一致
3D一般都是透視投影,既然有這個API,應該有特定的用途吧
public static void orthoM( float left, float right, float bottom, float top, float near, float far) {
Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
}複製程式碼
好了,本集結束,下一集:宇宙之光
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1-github | 2018-1-15 | Android多媒體之GL-ES戰記第四集–移形換影 |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的掘金 | 個人網站 |
3.宣告
1—-本文由張風捷特烈原創,轉載請註明
2—-歡迎廣大程式設計愛好者共同交流
3—-個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4—-看到這裡,我在此感謝你的喜歡與支援