• 调整屏幕的宽高比


    一.前言

      我们将上一篇文章中写的应用程序再次运行起来,然后将屏幕横过来,我们会发现空气曲棍球的桌子被压扁了。这之所以会发生,是因为我们没有考虑屏幕的宽高比,直接将坐标传递给了OpenGL。在这片文章中,我们会弄清楚为什么桌子被压扁了,以及如何使用投影解决这个问题。

     二.宽高比的问题

      我们现在都知道一个事实:在OpenGL中,我们要渲染的一切物体,都要映射到x,y和z轴的[-1,1]范围内,这个范围内的坐标被称为归一化设备坐标,其独立于屏幕实际的形状和尺寸。不幸的是,由于它独立于实际的屏幕尺寸和形状,我们直接使用就会出现问题,例如横屏模式下桌子被压扁了。

      我们现在假设设备的分辨率是1280x720,并且OpenGL占据整个屏幕,那么[-1,1]的范围对应1280像素的高,却只有720像素的宽,图像在x轴上就会显得扁平,同样的问题在y轴上也会发生。想要解决这个问题,我们需要调整坐标空间,以使它把屏幕形状考虑在内。我们可以把较小的范围固定在[-1,1]内,而按屏幕尺寸的比例调整较大的范围。举例来说,在竖屏模式下,可以把宽度限制在[-1,1]内,把高度限制在[-1280/720,1280/720]内。同理,在横屏模式下,可以将高度限制在[-1,1]中,而把高度限制在[-1280/720,1280/720]中。通过这个方法,无论是在竖屏还是横屏下,物体的形状都是一样的,我们所进行的操作就是正交投影。

    三.定义正交投影

      要定义正交投影,我们要借助Android的Matrix类,这个类有一个称为orthoM()的方法,它可以为我们生成一个正交投影,这个函数的定义如下:

    复制代码
    public static void orthoM(
      float[] m, //目标数组,这个目标数组的长度至少16个元素,这样才能存储正交投影矩阵
      int mOffset,//结果矩阵起始的偏移值
      float left, //x轴的最小范围
      float right, //x轴的最大范围
      float bottom, //y轴的最小范围
      float top,//y轴的最大范围
      float near, //z轴的最小范围
      float far//z轴的最大范围
    )
    复制代码

      当我们调用这个函数的时候,它会给我们生成一个4x4的矩阵,这个正交投影矩阵会把所有在左右之间,上下之间和远近之间的事物映射到归一化设备坐标中[-1,1]的范围中,在这个范围内的东西在屏幕上都是可见的。

    四.加入正交投影

      让我们加入正交投影,并修复那个被压扁的桌子。首先我们需要修改顶点着色器,在里面接收这个投影矩阵,代码修改如下:

    复制代码
    #version 300 es
    layout(location=0) in vec4 a_Position;
    layout(location=0) uniform mat4 u_Matrix;
    layout(location=1) in vec4 a_Color;
    out vec4 v_Color;
    void main() {
        gl_Position=u_Matrix*a_Position;
        v_Color=a_Color;
        gl_PointSize=10.0;
    }
    复制代码

      然后在MyRenderer类的顶部加入如下代码:private val projectionMatrix:FloatArray=FloatArray(16)//存储投影矩阵

      接着再更新onSurfaceChanged()函数,在末尾加入如下代码:

    复制代码
         //根据屏幕方向生成投影矩阵
            val aspectRatio=if(width>height) width.toFloat()/height.toFloat() else height.toFloat()/width.toFloat()
            if(width>height){
                Matrix.orthoM(projectionMatrix,0,-aspectRatio,aspectRatio,-1f,1f,-1f,1f)
            }
            else{
                Matrix.orthoM(projectionMatrix,0,-1f,1f,-aspectRatio,aspectRatio,-1f,1f)
            }
    复制代码

      最后,将生成的投影矩阵传入顶点着色器,在onDrawFrame()函数中的glClear()函数后加入如下代码即可:

    //传入投影矩阵
    glUniformMatrix4fv(0,1,false,projectionMatrix,0)

      第一个参数指uniform变量的位置,第二个参数指矩阵的个数。

      最后,我们可以修改下桌子的结构,让桌子更高点,这样看起来效果更好,只需要修改y值即可。更新后的结构如下:

    val tableVertices=floatArrayOf(
                //Triangle fan
                0f,0f,1f,1f,1f,
                -0.5f,-0.8f,0.7f,0.7f,0.7f,
                0.5f,-0.8f,0.7f,0.7f,0.7f,
                0.5f,0.8f,0.7f,0.7f,0.7f,
                -0.5f,0.8f,0.7f,0.7f,0.7f,
                -0.5f,-0.8f,0.7f,0.7f,0.7f,
                //Mid Line
                -0.5f,0f,1f,0f,0f,
                0.5f,0f,1f,0f,0f,
                //Mallets
                0f,-0.4f,0f,0f,1f,
                0f,0.4f,1f,0f,0f
            )

      最后,可以运行程序了,看看效果是不是和我们期待的那样。

     

  • 相关阅读:
    Error: GlobalConfigUtils setMetaData Fail Cause:java.lang.NullPointerException
    通过阿里云server配置属于自己的云笔记leanote
    五、03【Java IO模型】之字节流
    QCC51XX---QACT用户指南
    最新最全大数据毕业设计选题推荐
    c# --- 列表
    【数据结构】带头双向循环链表的实现(C语言)
    基于Java模具制造企业订单跟踪管理系统设计实现(源码+lw+部署文档+讲解等)
    Java中特殊字符处理(转义字符)
    【C++】C++智能指针
  • 原文地址:https://www.cnblogs.com/luqman/p/18003747