前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >进入三维

进入三维

作者头像
故乡的樱花开了
发布2024-02-04 08:16:03
900
发布2024-02-04 08:16:03
举报
文章被收录于专栏:Android技术专栏Android技术专栏

一.前言

  想象一下,你在游戏厅和朋友玩空气曲棍球游戏,从你的视角看,空气曲棍球桌是什么样的?你的那一端桌子会显得较大,因为你是从一个角度向下看桌子的,而不是俯视桌子,我们在上一篇文章中所写的程序就是俯视视角下的,在这片文章中,我们将走进三维,让绘制的桌子更符合实际的视角。

二.透视除法

  在一个顶点坐标成为归一化设备坐标之前,其实还进行了一个额外的步骤,它被称为透视除法。还记得我们在之前的文章中提过一下顶点坐标的w分量吧,它就是用于作透视除法用的。为了在屏幕上创建三维的幻象,OpenGL会把每个gl_Position的x,y,z分量除以w分量,当w分量表示距离的时候,就使得较远处的物体被移动到离渲染区域中心更近的地方,这个中心的作用就相当于一个消失点。

  举个例子,有两个坐标(1,1,1,1),(1,1,1,2),在OpenGL将他们转化为归一化坐标之前,会先进行透视除法,每个分量都除以w分量,这两个坐标整除后变为(1/1,1/1,1/1)和(1/2,1/2,1/2),这个除法之后,归一化设备坐标就是(1,1,1)和(0.5,0.5,0.5)。有较大的w值的坐标被移动到离(0,0,0)更近的位置,(0,0,0)就是归一化设备坐标里渲染区域的中心。

三.添加w分量创建三维图

  如果我们实际添加一下w分量,可以更直观的发现它所产生的影响。因为我们现在要指定一个位置的x,y,z和w分量,所以我们先要更新一下代码中的POSITION_COMPONENT_COUNT变量,将值改为4。下一步,我们需要更新顶点数组,修改后的值如下:

代码语言:javascript
复制
val tableVertices=floatArrayOf(
            //Triangle fan
            0f,0f,0f,1.5f,1f,1f,1f,
            -0.5f,-0.8f,0f,1f,0.7f,0.7f,0.7f,
            0.5f,-0.8f,0f,1f,0.7f,0.7f,0.7f,
            0.5f,0.8f,0f,2f,0.7f,0.7f,0.7f,
            -0.5f,0.8f,0f,2f,0.7f,0.7f,0.7f,
            -0.5f,-0.8f,0f,1f,0.7f,0.7f,0.7f,
            //Mid Line
            -0.5f,0f,0f,1.5f,1f,0f,0f,
            0.5f,0f,0f,1.5f,1f,0f,0f,
            //Mallets
            0f,-0.4f,0f,1.25f,0f,0f,1f,
            0f,0.4f,0f,1.75f,1f,0f,0f
        )

  我们给每个顶点都加入了w分量,靠近屏幕底部的w的值为1,而靠近屏幕顶部的w的值为2,其他的顶点的w的值也从底部到顶部逐渐增加。而对于z值,我们全部设为零即可,产生的效果应该是桌子的底部看起来更宽些,桌子的顶部看起来更窄些,就像我们从远处观看一样。可以运行程序看看效果,是否和我们所预想的那样。

四.使用透视投影

  我们加入w分量后,桌子看上去更像三维了。然而,如果我们希望这些物体更加动态,比如改变桌子的角度,放大或缩小,该怎么办呢?那么我们就不能指定w的值,我们要用矩阵来生成这些值。把我们刚刚做的改动都去掉,接下来我们将用矩阵来生成同样的效果。

  现在我们可以将onSurfaceChanged函数中glViewport()后的代码全部删除,然后使用Matrix类当中的perspectiveM()函数生成一个透视投影矩阵,这个函数的定义如下:

代码语言:javascript
复制
public static void perspectiveM(
float[] m,//存储透视投影矩阵
int offset,//数组开始存储投影矩阵的偏移值
float fovy, //视场垂直角度
float aspect, //宽高比
float zNear,//近裁剪面距离
float zFar//远裁剪面距离
)

  我们需要加入的代码如下:Matrix.perspectiveM(projectionMatrix,0,45f,width.toFloat()/height.toFloat(),1f,10f)

  此时运行程序时,我们什么也看不到,因为我们没有指定z值,那么z值就默认为0,而我们刚刚通过perspectiveM函数将z值的范围指定为了[-10,-1],所以什么也看不到。此时,我们可以利用模型矩阵将桌子移动到这个范围内,首先我们得在MyRenderer内中定义一个变量用于存储模型矩阵:private val modelMatrix:FloatArray=FloatArray(16)//存储模型矩阵

  然后,加入如下代码:

代码语言:javascript
复制
//生成模型矩阵
        Matrix.setIdentityM(modelMatrix,0)//设置为单位矩阵
        Matrix.translateM(modelMatrix,0,0f,0f,-2f)//将z值平移到可见范围内
        val temp:FloatArray=FloatArray(16)//存储矩阵相乘的结果
        Matrix.multiplyMM(temp,0,projectionMatrix,0,modelMatrix,0)
        System.arraycopy(temp,0,projectionMatrix,0,temp.size)//将temp复制到projectionMatrix

  现在,我们运行这个应用程序,可以看到桌子了,但是是以俯视的视角看的,我们需要旋转一下桌子,从而以正常的视角看向桌子,并且因为旋转之后,桌子的底部会离我们更近,我们可以让桌子离我们稍微远一些,这样的效果更好,修改之后的代码如下:

代码语言:javascript
复制
//生成模型矩阵
        Matrix.setIdentityM(modelMatrix,0)//设置为单位矩阵
        Matrix.translateM(modelMatrix,0,0f,0f,-3.5f)//将z值偏移-3.5
        Matrix.rotateM(modelMatrix,0,-60f,1f,0f,0f)//绕x轴旋转-60度
        val temp:FloatArray=FloatArray(16)//存储矩阵相乘的结果
        Matrix.multiplyMM(temp,0,projectionMatrix,0,modelMatrix,0)
        System.arraycopy(temp,0,projectionMatrix,0,temp.size)//将temp复制到projectionMatrix

  现在,运行程序,就可以看到三维场景下的空气曲棍球桌子了。

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

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

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

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

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