0

进入三维 - 故乡的樱花开了

 7 months ago
source link: https://www.cnblogs.com/luqman/p/18005229
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

进入三维

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

二.透视除法

  在一个顶点坐标成为归一化设备坐标之前,其实还进行了一个额外的步骤,它被称为透视除法。还记得我们在之前的文章中提过一下顶点坐标的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。下一步,我们需要更新顶点数组,修改后的值如下:

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()函数生成一个透视投影矩阵,这个函数的定义如下:

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)//存储模型矩阵

  然后,加入如下代码:

//生成模型矩阵
        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

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

//生成模型矩阵
        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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK