Android OpenGL ES(二)-正交投影

2018-08-30 10:55:36 浏览数 (1)

上文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的代码

代码语言:javascript复制
  @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相乘。

代码语言:javascript复制
//定义一个matrix。相当于4x4的矩阵
uniform mat4 u_Matrix;
attribute vec4 a_Position;
void main() {
    //与position相乘
    gl_Position =u_Matrix* a_Position;
}
计算矩阵

onSurfaceChanged生命周期方法中,计算我们的投影矩阵。

代码语言:javascript复制
   //投影矩阵
   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中。

代码语言:javascript复制
 @Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);

        //传递给着色器
        GLES20.glUniformMatrix4fv(uMatrix,1,false,mProjectionMatrix,0);

        //...
    }

结果

结果.png

和我们预想的结果一样。撒花~~

总结一下,我们从这这章节的内容了解到了下面这些使用的知识点:

  1. 矩阵的知识回顾
  2. 正交投影变换

但是我们现在还依然是平面的图。因为正交投影其实相当于正视图。

后面我们会先根据这章的内容画出其他图形。

然后再将其变成三维的样子。

整体的代码位置:https://github.com/deepsadness/OpenGLDemo5

0 人点赞