前言
1.本系列借花獻佛,結合了很多前人的文章以及書籍,我儘可能去總結並用我的思想進行加工
2.OpenGL一直是我的心結,也是時候去解開了,本系列稱不上原創,但每行程式碼都有著我思考的痕跡
3.本系列所有的圖片都是[張風捷特烈]所畫,如果有什麼錯誤還請指出,定會最快改正
4.本系列文章允許轉載、擷取、公眾號釋出,請保留前言部分,希望廣大讀者悉心指教
NPC:開場詞
傳說,在這片程式碼大陸上,存在一個古老的種族,它們擁有無盡的力量,卻罕有人能夠駕馭
多媒體王國中存在一個隱蔽的角落,是這個種族的棲息之地,很少有人敢冒犯那裡
Android多媒體領域有一處:被後人稱為黑龍洞穴--OpenGL ES
,其中埋藏著圖形界的無限財富
勇士們,舉起手中的劍,進發!
副本一: 黑龍洞口
NPC:黑龍洞口一片漆黑,其中隱藏著什麼規律,勇士們,一起尋找吧!
1.第一關卡:繪製全屏的紅色
1.1:GLSurfaceView的使用
/**
* 作者:張風捷特烈<br/>
* 時間:2019/1/9 0009:18:25<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:GL測試檢視
*/
public class GLView extends GLSurfaceView {
private GLRenderer mRenderer;
public GLView(Context context) {
this(context,null);
}
public GLView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setEGLContextClientVersion(2);//設定OpenGL ES 2.0 context
mRenderer = new GLRenderer();
setRenderer(mRenderer);//設定渲染器
}
}
複製程式碼
1.2:LSurfaceView.Renderer
的使用
/**
* 作者:張風捷特烈<br/>
* 時間:2019/1/9 0009:18:56<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:GL渲染類
*/
public class GLRenderer implements GLSurfaceView.Renderer {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);//GL視口
}
@Override
public void onDrawFrame(GL10 gl) {
//清除顏色快取和深度快取
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
}
}
複製程式碼
1.3:Activity中
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GLView(this));
}
}
複製程式碼
2.第二關卡:三角形的繪製
2.1:三角形
/**
* 作者:張風捷特烈<br/>
* 時間:2019/1/9 0009:20:09<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:三角形
*/
public class Triangle {
private FloatBuffer vertexBuffer;//頂點緩衝
private final String vertexShaderCode =//頂點著色程式碼
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =//片元著色程式碼
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final int mProgram;
private int mPositionHandle;//位置控制程式碼
private int mColorHandle;//顏色控制程式碼
private final int vertexCount = sCoo.length / COORDS_PER_VERTEX;//頂點個數
private final int vertexStride = COORDS_PER_VERTEX * 4; // 3*4=12
// 陣列中每個頂點的座標數
static final int COORDS_PER_VERTEX = 3;
static float sCoo[] = { //以逆時針順序
0.0f, 0.0f, 0.0f, // 頂部
-1.0f, -1.0f, 0.0f, // 左下
1.0f, -1.0f, 0.0f // 右下
};
// 顏色,rgba
float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
public Triangle() {
//初始化頂點位元組緩衝區
ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每個浮點數:座標個數* 4位元組
bb.order(ByteOrder.nativeOrder());//使用本機硬體裝置的位元組順序
vertexBuffer = bb.asFloatBuffer();// 從位元組緩衝區建立浮點緩衝區
vertexBuffer.put(sCoo);// 將座標新增到FloatBuffer
vertexBuffer.position(0);//設定緩衝區以讀取第一個座標
int vertexShader = GLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER,//頂點著色
vertexShaderCode);
int fragmentShader = GLRenderer.loadShader
(GLES20.GL_FRAGMENT_SHADER,//片元著色
fragmentShaderCode);
mProgram = GLES20.glCreateProgram();//建立空的OpenGL ES 程式
GLES20.glAttachShader(mProgram, vertexShader);//加入頂點著色器
GLES20.glAttachShader(mProgram, fragmentShader);//加入片元著色器
GLES20.glLinkProgram(mProgram);//建立可執行的OpenGL ES專案
}
public void draw() {
// 將程式新增到OpenGL ES環境中
GLES20.glUseProgram(mProgram);
//獲取頂點著色器的vPosition成員的控制程式碼
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
//啟用三角形頂點的控制程式碼
GLES20.glEnableVertexAttribArray(mPositionHandle);
//準備三角座標資料
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// 獲取片元著色器的vColor成員的控制程式碼
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
//為三角形設定顏色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
//繪製三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
//禁用頂點陣列
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
複製程式碼
2.2:GL渲染類
/**
* 作者:張風捷特烈<br/>
* 時間:2019/1/9 0009:18:56<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:GL渲染類
*/
public class GLRenderer implements GLSurfaceView.Renderer {
Triangle mTriangle;
/**
* 載入作色器
* @param type 頂點著色 {@link GLES20.GL_VERTEX_SHADER}
* 片元著色 {@link GLES20.GL_FRAGMENT_SHADER}
* @param shaderCode 著色程式碼
* @return 作色器
*/
public static int loadShader(int type, String shaderCode){
int shader = GLES20.glCreateShader(type);//建立著色器
GLES20.glShaderSource(shader, shaderCode);//新增著色器原始碼
GLES20.glCompileShader(shader);//編譯
return shader;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba
mTriangle = new Triangle();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);//GL視口
}
@Override
public void onDrawFrame(GL10 gl) {
//清除顏色快取和深度快取
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
mTriangle.draw();
}
}
複製程式碼
2.3:著色器
NPC:勇者,你陣亡了沒...如果現在退出還來得及,這將是一篇巨集偉的戰鬥史詩
如果你還想繼續,舉起你手中的劍,同我一起,進發!!!
/**
* 載入作色器
* @param type 頂點著色 {@link GLES20.GL_VERTEX_SHADER}
* 片元著色 {@link GLES20.GL_FRAGMENT_SHADER}
* @param shaderCode 著色程式碼
* @return 作色器
*/
public static int loadShader(int type, String shaderCode){
int shader = GLES20.glCreateShader(type);//建立著色器
GLES20.glShaderSource(shader, shaderCode);//新增著色器原始碼
GLES20.glCompileShader(shader);//編譯
return shader;
}
複製程式碼
2.4:渲染器程式
private final String vertexShaderCode =//頂點著色程式碼
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =//片元著色程式碼
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
int vertexShader = GLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER,//頂點著色
vertexShaderCode);
int fragmentShader = GLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER,//片元著色
fragmentShaderCode);
mProgram = GLES20.glCreateProgram();//建立空的OpenGL ES 程式
GLES20.glAttachShader(mProgram, vertexShader);//加入頂點著色器
GLES20.glAttachShader(mProgram, fragmentShader);//加入片元著色器
GLES20.glLinkProgram(mProgram);//建立可執行的OpenGL ES專案
複製程式碼
2.5:頂點緩衝
private FloatBuffer vertexBuffer;//頂點緩衝
private final int vertexCount = sCoo.length / COORDS_PER_VERTEX;//頂點個數
private final int vertexStride = COORDS_PER_VERTEX * 4; // 3*4=12
static final int COORDS_PER_VERTEX = 3;//陣列中每個頂點的座標數
static float sCoo[] = { //以逆時針順序
0.0f, 0.0f, 0.0f, // 頂部
-1.0f, -1.0f, 0.0f, // 左下
1.0f, -1.0f, 0.0f // 右下
};
//初始化頂點位元組緩衝區
ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每個浮點數:座標個數* 4位元組
bb.order(ByteOrder.nativeOrder());//使用本機硬體裝置的位元組順序
vertexBuffer = bb.asFloatBuffer();// 從位元組緩衝區建立浮點緩衝區
vertexBuffer.put(sCoo);// 將座標新增到FloatBuffer
vertexBuffer.position(0);//設定緩衝區以讀取第一個座標
複製程式碼
2.6: 繪製
public void draw() {
// 將程式新增到OpenGL ES環境中
GLES20.glUseProgram(mProgram);
//獲取頂點著色器的vPosition成員的控制程式碼
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
//啟用三角形頂點的控制程式碼
GLES20.glEnableVertexAttribArray(mPositionHandle);
//準備三角座標資料
GLES20.glVertexAttribPointer(
mPositionHandle,//int indx, 索引
COORDS_PER_VERTEX,//int size,大小
GLES20.GL_FLOAT,//int type,型別
false,//boolean normalized,//是否標準化
vertexStride,// int stride,//跨度
vertexBuffer);// java.nio.Buffer ptr//緩衝
// 獲取片元著色器的vColor成員的控制程式碼
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
//為三角形設定顏色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
//繪製三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
//禁用頂點陣列:
//禁用index指定的通用頂點屬性陣列。
// 預設情況下,禁用所有客戶端功能,包括所有通用頂點屬性陣列。
// 如果啟用,將訪問通用頂點屬性陣列中的值,
// 並在呼叫頂點陣列命令(如glDrawArrays或glDrawElements)時用於呈現
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
複製程式碼
副本二---龍之怒色
1.第一關卡:簡單認識OpenGL ES 著色指令碼語言
GLSL(OpenGL Shader Language)
1.一種面相過程的高階語言
2.基於C/C++的語法(子集)及流程控制
3.完美支援向量和矩陣的操作
4.通過型別限定符來管理輸入與輸出
複製程式碼
1.1:檔案的格式
沒有統一的擴充名,經過百度,感覺這種方式比較符合我的審美
而且AndroidStudio支援這些擴充名,你都叫.glsl
也可以,能分清就像
.vert - 頂點著色器
.tesc - 曲面細分控制著色器
.tese - 曲面細分評估著色器
.geom - 幾何著色器
.frag - 片元著色器
.comp - 計算著色器
複製程式碼
原生資料型別
標量:一維的數值操作
float 浮點型
bool 布林型
int 整型
|--- 支援 8進位制(0開頭) 16進位制(0x開頭)
複製程式碼
向量:儲存及操作 顏色、位置、紋理座標等
vec2 二維向量型-浮點型
vec3 三維向量型-浮點型
vec4 四維向量型-浮點型
ivec2 二維向量型-整型
ivec3 三維向量型-整型
ivec4 四維向量型-整型
bvec2 二維向量型-布林型
bvec3 三維向量型-布林型
bvec4 四維向量型-布林型
複製程式碼
矩陣:根據矩陣的運算進行變換操作
mat2 2X2矩陣-浮點型
mat3 3X3矩陣-浮點型
mat4 4X4矩陣-浮點型
複製程式碼
取樣器
sampler2D 二維紋理
sampler3D 三維紋理
samplerCube 立方貼圖紋理
複製程式碼
結構體:例如
struct ball{
vec3 color;
vec3 position;
}
複製程式碼
陣列
vec3 pos[]; //宣告不定大小的三維向量陣列
vec3 pos[6];//宣告6個三維向量陣列
複製程式碼
限定符
attribute 頂點的變數,如頂點位置,顏色
uniform
varying 用於從定點著色器傳遞到片元作色器的變數
const
precision 精度
|---lowp
|---mediump
|---highp
複製程式碼
2.第二關卡:資原始檔的讀取
載入著色指令碼的程式碼差不多,封裝一下,寫個GLUtils吧:
/**
* 作者:張風捷特烈<br/>
* 時間:2019/1/10 0010:10:58<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:OpenGL ES 輔助工具
*/
public class GLUtils {
//從指令碼中載入shader內容的方法
public static int loadShaderAssets(Context ctx, int type, String name) {
String result = null;
try {
InputStream in = ctx.getAssets().open(name);
int ch = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((ch = in.read()) != -1) {
baos.write(ch);
}
byte[] buff = baos.toByteArray();
baos.close();
in.close();
result = new String(buff, "UTF-8");
result = result.replaceAll("\\r\\n", "\n");
} catch (Exception e) {
e.printStackTrace();
}
return loadShader(type, result);
}
/**
* 載入作色器
*
* @param type 著色器型別 頂點著色 {@link GLES20.GL_VERTEX_SHADER}
* 片元著色 {@link GLES20.GL_FRAGMENT_SHADER}
* @param shaderCode 著色程式碼
* @return 作色器
*/
public static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);//建立著色器
if (shader == 0) {//載入失敗直接返回
return 0;
}
GLES20.glShaderSource(shader, shaderCode);//載入著色器原始碼
GLES20.glCompileShader(shader);//編譯
return checkCompile(type, shader);
}
/**
* 檢查shader程式碼是否編譯成功
*
* @param type 著色器型別
* @param shader 著色器
* @return 著色器
*/
private static int checkCompile(int type, int shader) {
int[] compiled = new int[1];//存放編譯成功shader數量的陣列
//獲取Shader的編譯情況
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {//若編譯失敗則顯示錯誤日誌並
Log.e("ES20_COMPILE_ERROR",
"Could not compile shader " + type + ":" + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);//刪除此shader
shader = 0;
}
return shader;
}
}
複製程式碼
3.第三關卡:tri.frag
和tri.vert
的分析
3.1:先看片元:tri.frag
第一句是宣告片元的精度
第二句是宣告片元的顏色:一個vec4的變數--vColor
gl_FragColor = vColor;
gl_FragColor是gl內定名,將vColor值賦給它
precision mediump float;
uniform vec4 vColor;
void main() {
gl_FragColor = vColor;
}
複製程式碼
單看一下著色的操作流程:
所以從Java程式碼來看,重點在color,它是一個四值陣列,每個值0~1
分別對應r,g,b,a
四值,即紅,綠,藍,透明
四個顏色維度
// 顏色,rgba
float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
複製程式碼
更換顏色:
rgba 132,197,240,255---->0.5176471f, 0.77254903f, 0.9411765f, 1.0f
3.2:再看定點:tri.vert
定義了一個四維的向量給gl_Position
attribute vec4 vPosition;
void main() {
gl_Position = vPosition;
}
複製程式碼
關於頂點的緩衝 初始化階段將頂點資料經過基本處理
static float sCoo[] = { //以逆時針順序
0.0f, 0.0f, 0.0f, // 頂部
-1.0f, -1.0f, 0.0f, // 左下
1.0f, -1.0f, 0.0f // 右下
};
/**
* 緩衝資料
*/
private void bufferData() {
ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每個浮點數:座標個數* 4位元組
bb.order(ByteOrder.nativeOrder());//使用本機硬體裝置的位元組順序
vertexBuffer = bb.asFloatBuffer();// 從位元組緩衝區建立浮點緩衝區
vertexBuffer.put(sCoo);// 將座標新增到FloatBuffer
vertexBuffer.position(0);//設定緩衝區以讀取第一個座標
}
複製程式碼
每三個數是一個頂點,分別代表(x,y,z),先卡z=0,也就是二維座標系
經過三個點的測試,可以發現是一箇中心在原點,左右跨度為1的座標系
變動座標
4.第三關卡:頂點著色
剛才是給片元進行著色的,現在看看怎麼給頂點著色,肯定要有頂點變數
前面關於修飾關鍵字:varying 用於從定點著色器傳遞到片元作色器的變數
4.1:頂點程式碼:tri.vert
attribute vec3 vPosition;//頂點座標
uniform mat4 uMVPMatrix; //總變換矩陣
attribute vec4 aColor;//頂點顏色
varying vec4 vColor;//片元顏色
void main() {
gl_Position = uMVPMatrix*vec4(vPosition,1);
vColor = aColor;//將頂點顏色傳給片元
}
複製程式碼
4.2:片元程式碼:tri.frag
precision mediump float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
複製程式碼
4.3:使用:Triangle.java
三個點,第三個顏色,頂點+緩衝,跟頂點座標一個套路,取
黃、藍、綠
三色
//成員變數
private FloatBuffer mColorBuffer;//顏色緩衝
static final int COLOR_PER_VERTEX = 4;//向量維度
private final int vertexColorStride = COLOR_PER_VERTEX * 4; // 4*4=16
float colors[] = new float[]{
1f, 1f, 0.0f, 1.0f,//黃
0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
0.19607843f, 1.0f, 0.02745098f, 1.0f//綠
};
//注意顏色控制程式碼不是uniform了,獲取片元著色器的vColor成員的控制程式碼
mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
//啟用三角形頂點顏色的控制程式碼
GLES20.glEnableVertexAttribArray(mColorHandle);
//準備三角頂點顏色資料
GLES20.glVertexAttribPointer(
mColorHandle,
COLOR_PER_VERTEX,
GLES20.GL_FLOAT,
false,
vertexColorStride,
mColorBuffer);
複製程式碼
副本三---龍之赤瞳
先看這個圖,按這樣來畫個人臉,豈不是會扁掉?這怎麼能忍
1.第一關卡:相機--Matrix.setLookAtM
一共11個引數,嚇得我一抖,經過百度,再加上我
神級的Ps技能
,繪圖如下
主要有三個點eye(相機/眼睛位置),center(觀察物的位置),up(抬頭的感覺,意會一下...)
public static void setLookAtM(float[] rm, int rmOffset,
float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY,float upZ) {
複製程式碼
2.第二關卡:透視投影--Matrix.frustumM
八個引數,還好還好,也不是太多...
Matrix.frustumM(float[] m, int offset,
float left, float right, float bottom, float top,
float near, float far)
複製程式碼
3.第三關卡:修正視野,讓x,y看起來一致
3.1.GLRenderer中:
//Model View Projection Matrix--模型檢視投影矩陣
private final float[] mMVPMatrix = new float[16];
//投影矩陣 mProjectionMatrix
private final float[] mProjectionMatrix = new float[16];
//檢視矩陣 mViewMatrix
private final float[] mViewMatrix = new float[16];
---->[GLRenderer#onSurfaceChanged]-------
float ratio = (float) width / height;
//透視投影矩陣--截錐
Matrix.frustumM(mProjectionMatrix, 0,
-ratio, ratio, -1, 1,
3, 7);
// 設定相機位置(檢視矩陣)
Matrix.setLookAtM(mViewMatrix, 0,
0, 0, -3,
0f, 0f, 0f,
0f, 1.0f, 0.0f);
---->[GLRenderer#onDrawFrame]-------
// 計算投影和檢視轉換
Matrix.multiplyMM(
mMVPMatrix, 0,
mProjectionMatrix, 0,
mViewMatrix, 0);
mTriangle.draw(mMVPMatrix);
複製程式碼
3.2:tri.vert
:為頂點新增矩陣變換
attribute vec3 vPosition;//頂點座標
uniform mat4 uMVPMatrix; //總變換矩陣
void main() {
gl_Position = uMVPMatrix*vec4(vPosition,1);
}
複製程式碼
3.3:獲取控制程式碼,修正頂點:Triangle.java
//獲取程式中總變換矩陣uMVPMatrix成員的控制程式碼
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
---->[Triangle#draw]-------------
//對頂點進行矩陣變換
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mvpMatrix, 0);
複製程式碼
副本四--龍之振翼
1.第一關卡:旋轉30°
對
mMVPMatrix
再進行矩陣變換就行了
//變換矩陣
private float[] mOpMatrix = new float[16];
---->[GLRenderer#onDrawFrame]-------
//mOpMatrix旋轉變換
Matrix.setRotateM(mOpMatrix, 0, 30, 0, 0, -1);
//使用mOpMatrix對mMVPMatrix進行變換
Matrix.multiplyMM(
mMVPMatrix, 0,
mViewMatrix, 0,
mOpMatrix, 0);
Matrix.multiplyMM(
mMVPMatrix, 0,
mProjectionMatrix, 0,
mMVPMatrix, 0);
複製程式碼
隱藏關卡--Matrix.multiplyMM
我知道你看得一臉懵X,現在看看multiplyMM是個什麼東西
怎麼看?當然先看原始碼啦,這是目前OpenGl ES 裡我見過註釋最多的...
將兩個4x4矩陣相乘,並將結果儲存在第三個4x4矩陣中。其中:result = lhs x rhs。
由於矩陣相乘的工作方式,結果矩陣的效果相當於先被右邊的矩陣乘,再被左邊的矩陣乘。
這跟你期望的情況是相反的。
result 儲存結果的浮點陣列
lhs 儲存左側矩陣的浮點陣列。
rhs 儲存右側矩陣的浮點陣列。
三個對應的offset--偏移量
public static native void multiplyMM(float[] result, int resultOffset,
float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);
複製程式碼
這裡都是用16個float的陣列成的矩陣,寫個方法列印出來再說
public static void logM(float[] matrix) {
logM(matrix, "Matrix");
}
/**
* 列印方陣陣列
*
* @param matrix
* @param name
*/
public static void logM(float[] matrix, String name) {
int wei = (int) Math.sqrt(matrix.length);
StringBuffer sb = new StringBuffer("\n[");
for (int i = 0; i < matrix.length; i++) {
sb.append(matrix[i]);
if ((i + 1) % wei == 0) {
if (i == matrix.length - 1) {
sb.append("]");
continue;
}
sb.append("\n");
continue;
}
sb.append(" , ");
}
Log.e("Matrix_TAG", name + ": " + sb.toString());
}
複製程式碼
現在回頭再來看看:
mOpMatrix本來全是0,經過setRotateM之後變成圖中第一個矩陣
第一個Matrix.multiplyMM
將mOpMatrix
矩陣作用於mViewMatrix
上,獲得結果矩陣:mMVPMatrix
第二個Matrix.multiplyMM
將mMVPMatrix
矩陣作用於mProjectionMatrix
上,獲得結果矩陣:mMVPMatrix
最後根據頂點變換矩陣的控制程式碼,將mMVPMatrix在tri.vert中作用在頂點上
//變換矩陣
private float[] mOpMatrix = new float[16];
//mOpMatrix旋轉變換
Matrix.setRotateM(mOpMatrix, 0, 30, 0, 0, -1);
//使用mOpMatrix對mMVPMatrix進行變換
Matrix.multiplyMM(
mMVPMatrix, 0,
mViewMatrix, 0,
mOpMatrix, 0);
Matrix.multiplyMM(
mMVPMatrix, 0,
mProjectionMatrix, 0,
mMVPMatrix, 0);
複製程式碼
2.第二關卡:不停旋轉
當GLSurfaceView的
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY)
;時
Renderer
的onDrawFrame(GL10 gl) {
會不斷執行,更新的時間間隔和手機有關
我的真機在13~19ms
之間,模擬器在16~48ms
之間,看了一下,轉一圈用6s,
即6000ms,一共360°,每次+1°,使用平均每度(每次重新整理)用了16.667ms,好吧,完美的60fps
private int currDeg = 0;
---->[GLRenderer#onDrawFrame]-------
//初始化變換矩陣
Matrix.setRotateM(mOpMatrix, 0, currDeg, 0, 0, -1);
Matrix.multiplyMM(mMVPMatrix, 0,
mViewMatrix, 0,
mOpMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0,
mProjectionMatrix, 0,
mMVPMatrix, 0);
mTriangle.draw(mMVPMatrix);
currDeg++;
if (currDeg == 360) {
currDeg = 0;
}
複製程式碼
3.第二關卡:不停旋轉著縮小
你拍照的時候怎麼讓成像縮小?----往後退唄!
根據後退為正,可以推測出座標系是一個右手系,也就是z軸朝向我們
執行很簡單:Matrix.translateM
就可以將mOpMatrix進行平移操作
以我們的視角(參考系):你可以想象成圖形(觀察物)一邊旋轉一邊原離我們,也可以反過來想想
引擎推動的不是飛船而是宇宙。飛船壓根就沒動過。
--如果對矩陣有興趣,建議把這篇看十遍
//設定沿Z軸位移
Matrix.translateM(mOpMatrix, 0, 0, 0, currDeg/90.f);
複製程式碼
NPC: 恭喜您,完成第四副本,現在您獲得OpenGL-ES 新手戰士的稱號,請留下名號:
我(輸入):張風捷特烈
NPC: 張風捷特烈,是否繼續前行,下面的關卡將更加艱難
我:(點選確定) 執劍向前
NPC: 尊敬的勇者-張風捷特烈,祝您一路平安,成功斬殺黑龍...
第一集結束,下一集:"謎團立方" 敬請期待
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1-github | 2018-1-11 | Android多媒體之GL-ES戰記第一集--勇者集結 |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的掘金 | 個人網站 |
本文參考:
1.《Android 3D遊戲開發技術寶典 OpenGL ES 2.0》
2.OpenGL ES 學習記錄
3.opengl-tutorial:OpenGL基礎知識
4.廣大網友的文章零散參考,就不一一列舉了