在学习图形变换之前,可以先参考文档https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/学习基本概念。我们之前的绘制的都是静态的图像,如果我们使用改变顶点坐标的方式来让图像变换起来是非常麻烦的,而且会消耗更多的处理时间和性能。我们可以使用一个或多个矩阵(Matrix)对象可以更好的变换(Transform)一个物体。这些变换包括:移动,缩放,旋转。和桌面版不同的是,在Android中可以使用Matrix类来实现各种变换矩阵(注意是android.opengl.Matrix,不要和android.graphics包下的混淆了)。
multiplyMM(float[] result, int resultOffset, float[] lhs, int lhsOffset, float[] rhs, int rhsOffset):将两个4x4矩阵相乘,并将结果存储在第三个4x4矩阵中。以矩阵表示法表示:结果=lhs x rhs。
参数
result:保存结果的浮点数组
resultOffset:保存结果的浮点数组的偏移量
lhs:包含左侧矩阵的浮点数组
lhsOffset:左侧矩阵的浮点数组的偏移量
rhs:包含右侧矩阵的浮点数组
rhsOffset:右侧矩阵的浮点数组的偏移量
transposeM(float[] mTrans, int mTransOffset, float[] m, int mOffset):转置矩阵,将矩阵的主对角线翻转,交换矩阵的行索引与列索引。mTrans和m不能重叠。
参数
mTrans:保存结果的浮点数组
mTransOffset:保存结果的浮点数组的偏移量
m:输入的浮点数组
mOffset:输入的浮点数组的偏移量
invertM(float[] mInv, int mInvOffset, float[] m, int mOffset):逆矩阵,存在矩阵M以及矩阵N,假如M*N = 矩阵I(Identify Matrix单位矩阵),那么矩阵M和矩阵N互为逆矩阵。
参数
mInv:保存输出逆矩阵的数组
mInvOffset:输出逆矩阵的数组的偏移量
m:输入的浮点数组
mOffset:输入的浮点数组的偏移量
orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far):计算正射投影矩阵。关于正射投影可以参考文档https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/,
参数
m:保存结果的浮点数组
mOffset:保存结果的数组中的偏移量
left,right,bottom,top:近平面的 左, 右, 下, 上 的值
near:近平面与视点之间的距离
far:远平面与视点之间的距离
frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far):根据六个值计算透视投影矩阵。关于透视投影投影可以参考文档https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/,
参数
m:保存结果的浮点数组
mOffset:保存结果的数组中的偏移量
left,right,bottom,top:近平面的 左, 右, 下, 上 的值
near:近平面与视点之间的距离
far:远平面与视点之间的距离
perspectiveM(float[] m, int offset, float fovy, float aspect, float zNear, float zFar):根据计算透视投影矩阵。关于透视投影投影可以参考文档https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/。
参数
m:保存结果的浮点数组
offset:保存结果的数组中的偏移量
fovy:y方向上的角度范围
aspect:窗口的宽高比
zNear:z方向上近平面
zFar:z方向上远平面
length(float x, float y, float z):计算向量的长度。
参数
x,y,z:向量的xyz的值
setIdentityM(float[] sm, int smOffset):设置单位矩阵。
参数
sm:保存结果的浮点数组
smOffset:保存结果的数组中的偏移量
scaleM(float[] sm, int smOffset, float[] m, int mOffset, float x, float y, float z):使用xyz向量来缩放矩阵m,把结果放到矩阵sm中。
参数
sm:保存结果的浮点数组
smOffset:保存结果的数组中的偏移量
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值
scaleM(float[] m, int mOffset, float x, float y, float z):使用xyz向量来缩放矩阵m,把结果放到矩阵m中。
参数
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值
translateM(float[] tm, int tmOffset, float[] m, int mOffset, float x, float y, float z):使用xyz向量来位移矩阵m,把结果放到矩阵tm中。
参数
tm:保存结果的浮点数组
tmOffset:保存结果的数组中的偏移量
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值
translateM( float[] m, int mOffset, float x, float y, float z):使用xyz向量来位移矩阵m,把结果放到矩阵m中。
参数
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值
rotateM(float[] rm, int rmOffset, float[] m, int mOffset, float a, float x, float y, float z):围绕xyz组成的轴来旋转矩阵m,旋转的角度是a,把结果放到矩阵rm中。
参数
rm:保存结果的浮点数组
rmOffset:保存结果的数组中的偏移量
m:源矩阵
mOffset:源矩阵的偏移量
a:旋转的角度
x,y,z:向量的xyz值
rotateM(float[] m, int mOffset, float a, float x, float y, float z):围绕xyz组成的轴来旋转矩阵m,旋转的角度是a,把结果放到矩阵m中。
参数
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值
setRotateM(float[] rm, int rmOffset, float a, float x, float y, float z) :围绕xyz组成的轴旋转角度a创建一个矩阵m,把结果放到矩阵m中。
参数
rm:保存结果的浮点数组
rmOffset:结果矩阵的偏移量
a:旋转的角度
x,y,z:向量的xyz值
setRotateEulerM(float[] rm, int rmOffset, float x, float y, float z):根据欧拉角度生成矩阵rm。
参数
rm:结果矩阵
rmOffset:结果矩阵的偏移量
x,y,z:旋转的角度值
setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ):设置观察视角,参数包括观察点的作品,物体的中心点,视觉向量(朝向)。关于观察点可以参考文档https://learnopengl-cn.github.io/01%20Getting%20started/09%20Camera/
参数
rm:结果矩阵
rmOffset:结果矩阵的偏移量
eyeX, eyeY, eyeZ:观察点坐标
centerX, enterY, centerZ:物品中心点
upX, pY, pZ:视觉向量
我们先绘制一个三角形,顶点坐标如下
private final float[] TriangleCoords = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
我们知道在OpenGL的坐标范围都是1,而屏幕的宽高是不一样的,竖屏状态下三角形会比较高,而横屏下三角形会比较宽,如果我们想要使三角形变为正三角形就需要改变三角形宽高一致。有两种方式,一种是缩放,一种是正射投影。使用缩放的方式:在竖屏状态下,缩放y轴为宽高比;横屏时,缩放x轴为高宽比;使用正射投影:竖屏状态下,top和bottom分别为高宽比的正负值;横屏状态下,left和right分别为宽高比的正负值。原理上都是一样的,竖屏时缩放y轴,横屏时缩小x轴。
首先定义一个一个4x4的矩阵,然后在onSurfaceChanged时根据宽高来计算矩阵的值
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
super.onSurfaceChanged(gl, width, height);
Matrix.setIdentityM(projectionMatrix, 0);
float aspectRatio = width > height ?
(float) width / (float) height :
(float) height / (float) width;
if (width > height) {
// 横屏
Matrix.scaleM(projectionMatrix, 0, 1 / aspectRatio, 1, 1);
//Matrix.orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
} else {
// 竖屏or正方形
Matrix.scaleM(projectionMatrix, 0, 1, 1 / aspectRatio, 1);
//Matrix.orthoM(mProjectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
}
}
我们需要改变顶点着色器代码,定义顶点的坐标为变换矩阵和顶点的坐标相乘,代码如下
vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 aPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * aPosition;" +
"}";
然后再onDrawFrame传入我们计算后的矩阵
@Override
public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
……
// 得到形状的变换矩阵的句柄
int mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");
// 将投影和视图转换传递给着色器
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, projectionMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
GLES20.glDisableVertexAttribArray(positionHandle);
}
显示效果如下
同样的道理,可以使用位移,旋转改变当前的三角。设置一直绘制,根据时间对三角形进行旋转和位移,可以看到一个不停位移和旋转的三角形,绘制的代码如下
@Override
public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
Matrix.setIdentityM(rotationMatrix, 0);
Matrix.setIdentityM(translateMatrix, 0);
……
// 旋转从0到360循环旋转
long time = SystemClock.uptimeMillis() % 4000L;
float angle = 0.090f * ((int) time);
Matrix.setRotateM(rotationMatrix, 0, angle, 0, 0, -1.0f);
// 位移 在x轴上循环滑动
float translate = (SystemClock.uptimeMillis() % 4000f) / 2000f;
Matrix.translateM(translateMatrix, 0, translate <= 1 ? (translate - 0.5f) : (1 - (translate - 0.5f)), 0, 0);
// 计算
Matrix.multiplyMM(tempMatrix, 0, projectionMatrix, 0, translateMatrix, 0);
// 计算
Matrix.multiplyMM(vPMatrix, 0, tempMatrix, 0, rotationMatrix, 0);
// 将视图转换传递给着色器
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, vPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
GLES20.glDisableVertexAttribArray(positionHandle);
}
可以自行运行代码查看效果。
来源:CSDN
作者:jklwan
链接:https://blog.csdn.net/jklwan/article/details/103490031