OpenGL 学习教程
Android OpenGL ES 学习(一) – 基本概念
Android OpenGL ES 学习(二) – 图形渲染管线和GLSL
Android OpenGL ES 学习(三) – 绘制平面图形
Android OpenGL ES 学习(四) – 正交投影
Android OpenGL ES 学习(五) – 渐变色
Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序
Android OpenGL ES 学习(七) – 纹理
Android OpenGL ES 学习(八) –矩阵变换
Android OpenGL ES 学习(九) – 坐标系统和。实现3D效果
代码工程地址: https://github.com/LillteZheng/OpenGLDemo.git
今天要完成的效果:
一. 矩阵变换
说到矩阵,就不得不说大学的线性代数,可能大部分看到这,就不想往下看了。
别急,google 也知道我们懒,所以提供了 Matrix 这个类,来帮助我们,用简单易懂的方式,实现矩阵变换。
当然,一些基础知识,还是学习的,你也可以参考官网,学习更全面的知识。
https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/
1.1 矩阵基础知识
我们都知道 OpenGL 是一个向量,向量是什么东西,就是又有方向,又有大小,就像高中物理的力。
而矩阵又是什么呢?简单来说矩阵就是一个矩形的数字、符号或表达式数组。矩阵中每一项叫做矩阵的元素(Element)。下面是一个2×3矩阵的例子:
那向量跟矩阵又有啥关系?
我们知道,向量可以表示位置,颜色或者坐标,甚至是纹理,其实深入去看向量,它其实是一个 NX1的矩阵 ,N表示向量分量的个数(也叫N维(N-dimensional)向量:
这样,我们就可以让矩阵和向量相乘,而向量只有一列,故所得乘积的结果,依赖于矩阵的效果。正巧,很多有趣的2D/3D变换都可以放在一个矩阵中,用这个矩阵乘以我们的向量将变换(Transform)这个向量。
1.2 单位矩阵
单位矩阵是比较特殊的矩阵,在OpenGL 中,由于只有4个分量,所以使用 4x4 的矩阵,单位矩阵是一个除了对角线以外都是0的N×N矩阵,任何矩阵乘以它,都等于矩阵本身。
你可能会奇怪,要一个单位矩阵干嘛?别着急,看看下面的效果。
1.3 缩放
对一个向量进行缩放(Scaling)就是对向量的长度进行缩放,而保持它的方向不变。由于我们操作的是 2维或者3维,所以只需要改变x,y 或者 z 的大小就可以了。
比如向量v¯=(3,2)。我们可以把向量沿着x轴缩放0.5,使它的宽度缩小为原来的二分之一;我们将沿着y轴把向量的高度缩放为原来的两倍:
而用矩阵怎么表示呢?
如果把缩放值改成 s1,s2,s3 ,则缩放的矩阵为:
如果使用代码,则用:
Matrix.scaleM(UnitMatrix,0,s1,s2,s3)
1.4 平移
位移(Translation)是在原始向量的基础上加上另一个向量从而获得一个在不同位置的新向量的过程。
这个比较简单,套上矩阵的加法公式,可以得出矩阵:
使用代码:
Matrix.translateM(UnitMatrix,0,s1,s2,s3)
1.5 旋转
旋转稍微会比较复杂一点,如下图:
我们需要定义一条边,并在此基础上,旋转一个角度。那角度和方向怎么去关联呢,聪明的你,已经猜想到,使用 cosθ 或者 sinθ 了。旋转的矩阵,也是这样,如沿x轴旋转
代码:
Matrix.rotateM(UnitMatrix, 0, angle, s1, s2, s3);
二. 实践
之前的版本中,我们使用了正交投影 Android OpenGL ES 学习(四) – 正交投影,设置了一个矩阵,这样也保持不变,由于向量的读法是从右到左的,所以顶点着色器如下:
private const val VERTEX_SHADER = """#version 300 es
uniform mat4 u_Matrix;
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec2 aTexture;
out vec4 vTextColor;
out vec2 vTexture;
void main()
{
// 矩阵与向量相乘得到最终的位置
gl_Position = u_Matrix * a_Position;
vTexture = aTexture;
}
"""
其他不变,然后获取 matrix 的值:
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
GLES30.glClearColor(1f, 1f, 1f, 1f)
makeProgram(VERTEX_SHADER, FRAGMENT_SHADER)
uMatrix = getUniform(U_MATRIX)
...
}
为了每次都能修改都,将赋值操作,放到onDrawFrame 那里
override fun onDrawFrame(gl: GL10?) {
//步骤1:使用glClearColor设置的颜色,刷新Surface
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
GLES30.glUniformMatrix4fv(uMatrix, 1, false, UnitMatrix, 0)
//useVaoVboAndEbo
texture?.apply {
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,id)
}
GLES30.glBindVertexArray(vao[0])
GLES30.glDrawElements(GLES30.GL_TRIANGLE_STRIP, 6, GLES30.GL_UNSIGNED_INT, 0)
}
GlSurfaceView 的刷新模式,改成点击才刷新,这样方便点击测试
//等待点击才会刷帧
renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
其中,平移,旋转和缩放的代码如下:
linear.addBtn("平移"){
Matrix.translateM(UnitMatrix,0,0.5f,0.0f,0f)
glView.requestRender()
}
linear.addBtn("旋转"){
Matrix.rotateM(UnitMatrix, 0, 180f, 1f, 0f, 0f);
glView.requestRender()
}
linear.addBtn("缩放"){
Matrix.scaleM(UnitMatrix,0,0.8f,0.8f,0f)
glView.requestRender()
}
看看效果: