2

调整屏幕的宽高比 - 故乡的樱花开了

 7 months ago
source link: https://www.cnblogs.com/luqman/p/18003747
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

调整屏幕的宽高比

  我们将上一篇文章中写的应用程序再次运行起来,然后将屏幕横过来,我们会发现空气曲棍球的桌子被压扁了。这之所以会发生,是因为我们没有考虑屏幕的宽高比,直接将坐标传递给了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
)

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK