上文Android OpenGL ES(一)-开始描绘一个平面三角形中我们已经成功描绘了一个三角形。但是奇怪的是,按照我们的坐标。期望得到的应该是一个等腰三角形。但是最后的结果,确实一个扁平的三角形。
OpenGL ES世界的基本元素
着色器
坐标系。矩阵
纹理
...
本文主要涉及的部分是矩阵。
直接开始
我们先快速过一下矩阵的基础知识
矩阵基础知识
单位矩阵
单位矩阵.png
任何一个矩阵乘以单位矩阵,都依旧是器本身。
平移矩阵
平移矩阵.png
平移矩阵和单位矩阵和类似。但是向量x,y,z,1前乘这个平移矩阵后的结构就是平移矩阵中定义的偏移量。
这里需要注意的。第四个变量其实是w。而在OpenGL中,如果我们不去定义这个w。默认就是1.
OpenGL的坐标系
归一化设备坐标
我们之前定义的坐标系。是OpenGL中的坐标系。x,y,z都映射到了-1,1的范围内。这个范围内的坐标我们称之为归一化设备坐标。他是独立于屏幕实际的尺寸和形状的。
代码语言:javascript复制 private static float TRIANGLE_COORDS[] = {
//Order of coordinates: X, Y, Z
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
归一化设备坐标.png
但是显而易见的,我们的手机的宽高比,明显不可能是1:1。而我们设置视口(ViewPort
)时,又使用了初始化传入的宽和高。
手机的宽高.png
设置viewport
的代码
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//在窗口改变的时候调用
GLES20.glViewport(0,0,width,height);
}
虚拟空间坐标系
我们应该停止直接在归一化坐标上工作,转而在虚拟空间坐标系中工作。 调整坐标空间。我们需要将虚拟空间坐标转换成归一化设备坐标,让OpenGL可以正确的渲染它们。 这种操作就是使用正交投影
正交立方体内的场景.png
一个正交投影.png
正交投影
正交投影其实可以当作是一个正视图。
我们可以调用这个方法来得到正交矩阵。
代码语言:javascript复制/ * float[] 目标数组。只要的有16个元素,才能存储正交投影矩阵
* mOffset 结果矩阵起始的偏移量
* left x轴的最小范围
* right x轴的最大范围
* bottom y轴的最小范围
* top y轴的最大范围
* near z轴的最小范围
* far z轴的最大范围
**/
Matrix.orthoM(mProjectionMatrix, 0, -aspectRatio, aspectRatio, -1, 1f, -1.f, 1f);
得到的矩阵如下图
正交投影矩阵.png
- 和平移矩阵相似 回到开头我们复习的平移矩阵。是不是两者是否相似。 我们可以理解为,其实就是将坐标缩放和平移到我们的归一化坐标中。
- 和平移矩阵不同 当时有一个地方不同的是z轴的正负。 原因是归一化的设备坐标系使用的是左手。而OpenGL使用的是右手。所以要转化成归一化坐标,就需要反过来。
代码实现
基于上一节的代码做下面的修改。
其实还是我们的上一节总结的套路。
代码语言:javascript复制着色器定义属性=>代码传递更新
我们需要着色器的代码中定义一个矩阵的常量。再将计算好的矩阵。传入其中
更新着色器的代码
在着色器中定义一个matrix
,并与position
相乘。
//定义一个matrix。相当于4x4的矩阵
uniform mat4 u_Matrix;
attribute vec4 a_Position;
void main() {
//与position相乘
gl_Position =u_Matrix* a_Position;
}
计算矩阵
在onSurfaceChanged
生命周期方法中,计算我们的投影矩阵。
//投影矩阵
private float[] mProjectionMatrix = new float[16];
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
super.onSurfaceChanged(gl, width, height);
//主要还是长宽进行比例缩放
float aspectRatio = width > height ?
(float) width / (float) height :
(float) height / (float) width;
if (width > height) {
//横屏。需要设置的就是左右。
Matrix.orthoM(mProjectionMatrix, 0, -aspectRatio, aspectRatio, -1, 1f, -1.f, 1f);
} else {
//竖屏。需要设置的就是上下
Matrix.orthoM(mProjectionMatrix, 0, -1, 1f, -aspectRatio, aspectRatio, -1.f, 1f);
}
}
激活属性。并传递值
在生命周期方法onDrawFrame
中。
@Override
public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
//传递给着色器
GLES20.glUniformMatrix4fv(uMatrix,1,false,mProjectionMatrix,0);
//...
}
结果
结果.png
和我们预想的结果一样。撒花~~
总结一下,我们从这这章节的内容了解到了下面这些使用的知识点:
- 矩阵的知识回顾
- 正交投影变换
但是我们现在还依然是平面的图。因为正交投影其实相当于正视图。
后面我们会先根据这章的内容画出其他图形。
然后再将其变成三维的样子。
整体的代码位置:https://github.com/deepsadness/OpenGLDemo5