OpenGL ES投影和视图变换

躬行之

共 3324字,需浏览 7分钟

 · 2021-08-31

37248031f5f5886aa01152a7a2cbf944.webp

PS:无论多忙,还是要多留点时间给自己。

上篇文章介绍了 Android 中的 OpenGL 以及坐标映射等,在 OpenGL ES 环境中,通过投影和相机视图,显示的绘制对象更接近于眼睛看到的实物,这种呈现方式是通过对绘制对象坐标进行数学转换来完成,这里介绍一下投影和相机视图相关知识,文中代码案例变更可以参考前一篇文章:

主要内容如下:

  1. 投影类型

  2. 定义投影

  3. 定义相机视图

  4. 应用投影和相机视图

  5. 运行效果

投影类型

OpenGL 中主要有两种投影模式,分别是正交投影和透视投影,其特点分别如下:

  1. 透视投影:符合人眼习惯,呈现近大远小的效果。

  2. 正交投影:所有物体在投影平面上保持原来的大小。

透视投影的视景体是截锥体,正交投影的视景体是长方体,透视投影和正交投影示意图如下:

59530750cc49eb78c9b66d1bab04fb2f.webp

两者对应的矩阵计算函数如下:

1// 透视投影矩阵
2Matrix.frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far);
3// 正交投影矩阵
4Matrix.orthoM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far);

上述函数参数中 m 用来存储对应的投影矩阵数据,near 和 far 分别表示视景体的近平面、远屏幕距离,left、right、top、bottom 对应的是远平面的参数。

定义投影

根据上一小节内容,这里使用透视投影,填充投影矩阵使用 Matrix.frustumM() ,如下

1private val projectionMatrix = FloatArray(16)
2override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
3    GLES20.glViewport(00, width, height)
4    val ratio: Float = width.toFloat() / height.toFloat()
5    Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
6}

上述代码填充了一个投影矩阵projectionMatrix,其变化参考如下动图:

6656d32a005ecb1696eb24b169c5bc6e.webp

定义相机视图

相机视图顾名思义就相当于站在相机的角度观察某个物体,使用Matrix.setLookAtM方法来填充视图矩阵,关键参数主要是相机位置、目标位置和相机的正上方向量,然后将投影矩阵和视图矩阵合并为vPMatrix,如下:

 1override fun onDrawFrame(gl: GL10?) {
2    // 绘制当前frame,用于渲染处理具体的内容
3    Log.d(tag, "onDrawFrame")
4
5    // 设置相机位置(视图矩阵)
6    Matrix.setLookAtM(viewMatrix,0,
7                  0.0f,0.0f,5.0f, // 相机位置
8                  0.0f,0.0f,0.0f, // 目标位置
9                  0.0f,1.0f,0.0f) // 相机正上方向量
10    // 计算投影和视图变换
11    Matrix.multiplyMM(vPMatrix,0,projectionMatrix,0,viewMatrix,0)
12
13    // 具体绘制
14    triangle.draw(vPMatrix)
15}

如上案例中相机位置 z 坐标为只能在 near 和 far 之间,也就是必须在 3 和 7 之间,不能在其范围外观察,参考如下动图:

8023eb74dfd8408a05313b333f209f80.webp

应用投影和相机视图

为了适应投影和视图变换,修改上一篇中的着色器代码如下:

 1// default
2attribute vec4 vPosition;
3void main() {
4    gl_Position = vPosition;
5}
6// 使用投影和视图变换
7uniform mat4 uMVPMatrix;
8attribute vec4 vPosition;
9void main() {
10    gl_Position = uMVPMatrix * vPosition;
11}

将上一小节中计算得到的的vPMatrix矩阵传入着色器即可:

 1fun draw(mvpMatrix: FloatArray) {
2    // 获取attribute变量的地址索引
3    // get handle to vertex shader's vPosition member
4    positionHandle = GLES20.glGetAttribLocation(programHandle, "vPosition").also {
5        // enable vertex attribute,默认是disable
6        GLES20.glEnableVertexAttribArray(it)
7        GLES20.glVertexAttribPointer(
8            it,
9            COORDINATE_PER_VERTEX,
10            GLES20.GL_FLOAT,
11            false,
12            vertexStride,
13            vertexBuffer
14        )
15    }
16    // get handle to fragment shader's vColor member
17    colorHandler = GLES20.glGetUniformLocation(programHandle, "vColor").also {
18        GLES20.glUniform4fv(it, 1, color, 0)
19    }
20
21    // get handle to shape's transformation matrix
22    vPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "uMVPMatrix")
23    // Pass the projection and view transformation to the shader
24    GLES20.glUniformMatrix4fv(vPMatrixHandle, 1false, mvpMatrix, 0)
25
26    // draw triangle
27    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)
28    GLES20.glDisableVertexAttribArray(positionHandle)
29}

到此通过代码改造应用投影和相机视图,解决了横竖屏切换的变形问题,这种变形自然可以延伸到其他领域,比如OpenGL渲染视频时的视频比例等。

运行效果

可以与上一篇文章中的运行效果进行对比,运行效果如下:

435b50a73ff336090007ae7ed7d4f0b2.webp
可以回复关键字【OpenGL】获取源代码,上文中出现的动图程序回复关键字【OTUTORS】获取。

推荐阅读:

浏览 40
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报