视野限制了人对这个宇宙的认知,但没有视野,人将会一无所知
上集说到勇者坠入
黑暗之渊
,凭借对世界的认知构建出了世界系
到此为止,OpenGL的世界观已经映入脑海,新手十二副本已经经过
接下来等待他们的将是什么,普通副本开启....git
斗转星移
绘制矩形
这关简单,回头看看,是否是感受清晰了许多,下面列一下关键点,其余不说了
这个矩形可不是一开始摸黑瞎画的,而是轴系下可感的矩形,地址:matrix.MatrixRectangle
视角移回正方向0f, 0f, -6
,这时候画出的是后面github
//顶点坐标
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,//紫色
};
复制代码
视图的矩阵变换和投影矩阵感受在
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());
复制代码
想一下,若是我平移画一个立方,mOpMatrix会变化,
我再平移画一个立方时mOpMatrix会在上一个mOpMatrix的基础上进行变换
这种状况下咱们是但愿mOpMatrix在画完后回到以前状态的,这就涉及栈的概念
此处没用Java的Stack类,由于元素是操做矩阵,即float[],很差放bash
一个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());
复制代码
//默认栈深为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--;//栈顶下移
}
复制代码
一个x方向-1.5,另外一个x方向+1.5,这才是咱们想要的效果post
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();
复制代码
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;
复制代码
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个数字是怎么让图形变换的ui
Matrix.translateM分析
怎么分析呢?废话,固然是看源码了spa
/**
* 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]*z
i=1: m[13] += m[1]*x + m[5]*y + m[9]*z
i=2: m[14] += m[2]*x + m[6]*y + m[10]*z
i=3: m[15] += m[3]*x + m[7]*y + m[11]*z
Why ?
复制代码
坐标变换的源头
因此的根源在于这句话,它表明了什么意思?
注意:OpenGL 中的向量是列主元,也就是竖着排
而后算出gl_Position的结果会是一个四维向量
这也就是m12,m13,m14,m15为何特别
,m0,m1,m2,m3为何和x息息相关
再换个角度来看,gl_Position中的四个维度分别表明:x,y,z,w
gl_Position.x = m0*x + m4*y + m8*z + m12
gl_Position.y = m1*x + m5*y + m9*z + m13
gl_Position.z = m2*x + m6*y + m10*z + m14
这也说明 m12 m13 m14 三个数分别控制x,y,z的位移
复制代码
如今再看translateM,能够看出它的做用是改变第四行
咱们传入的是行向量,传入渲染器中变成列向量,至关于转置,
也就是处理时uMVPMatrix的第四列,这样一来路就走通了,
translateM经过改变第四行的向量来操做顶点的位置,yes!
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]*z
i=1: m[1] = m[1]*x m[5] = m[5]*y m[9] = m[9]*z
i=2: m[2] = m[2]*x m[6] = m[4]*y m[10] = m[10]*z
i=3: m[3] = m[3]*x m[7] = m[7]*y m[11] = m[11]*z
>这样一看是否是豁然开朗
复制代码
旋转仍是算了吧,没几张草稿纸还真算不清,源码看着也挺复杂
等之后哪天闲到怀疑人生的时候再来慢慢算算吧。
形之累变
在一个圆环上等分点,能够造成若干三角形,由此能够拼合出图形
这个副本是为了练习一下规律型的运算,发现规律,加以使用
/**
* 初始化顶点坐标与颜色
* @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);
复制代码
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);
复制代码
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);
复制代码
世界之幕
下面是
视点:(2,2,-6) far 9 near 3
的单位立方,结合上面的图来看看:
near 和 far两个面决定了视野,即两面间的内容可见
near面的上下左右的长度也决定这物体的高矮胖瘦,好比左右减半后,
你应该能想了视野被"压扁"了,物体也会随之变扁
MatrixStack.frustum(
-ratio/2, ratio/2, -1, 1,
3f, 9);
复制代码
near面越近,成像越小,你能够根据那个图,本身想一想
当观察到的事物在near和far面组成的棱台以外,便不可见
因此说眼睛限制了你对这个宇宙的认知,但没有眼睛你会一无所知
这个比较简单,也就是没有透视,立方的对线都是平行的,参数和透视投影一致
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);
}
复制代码
好了,本集结束,下一集:宇宙之光
项目源码 | 日期 | 备注 |
---|---|---|
V0.1-github | 2018-1-15 | Android多媒体之GL-ES战记第四集--移形换影 |
笔名 | 微信 | 爱好 | |
---|---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 | 语言 |
个人github | 个人简书 | 个人掘金 | 我的网站 |
1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----我的能力有限,若有不正之处欢迎你们批评指证,一定虚心改正
4----看到这里,我在此感谢你的喜欢与支持