前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >调整屏幕的宽高比

调整屏幕的宽高比

作者头像
故乡的樱花开了
发布2024-02-03 09:32:38
1600
发布2024-02-03 09:32:38
举报
文章被收录于专栏:Android技术专栏

一.前言

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

代码语言:javascript
复制
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]的范围中,在这个范围内的东西在屏幕上都是可见的。

四.加入正交投影

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

代码语言:javascript
复制
#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()函数,在末尾加入如下代码:

代码语言:javascript
复制
     //根据屏幕方向生成投影矩阵
        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()函数后加入如下代码即可:

代码语言:javascript
复制
//传入投影矩阵
glUniformMatrix4fv(0,1,false,projectionMatrix,0)

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

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

代码语言:javascript
复制
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
        )

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档