1 前言

MVP矩阵变换 中主要介绍了模型变换(平移、旋转、对称、缩放)和观测变换基本原理,本文将介绍透视变换的基本原理。

​ 如下图,近平面远平面间棱台称为视锥体,表示可见区域范围,视锥体以外的空间将被裁剪丢弃,视锥体内的模型通过透视变换投影到近平面上,近平面上得到的平面图形就是屏幕上要显示的模型的图形。

​ 近平面的高度为 2(区间为 [-1, 1],为方便计算,已归一化),宽度也为 2。当相机位置和模型位置已固定时,由于近平面的宽高已固定,因此可以通过平移近平面的位置控制模型显示的缩放大小。

2 透视变换原理

​ 如下图,在视图坐标系下,已知近平面和远平面距离原点的距离分别为 N 和 F,近平面高度和宽度都为 2,假设视锥体内任意一点的坐标为 (x0, y0, z0),经透视投影后的坐标为 (x1, y1, z1)。

​ 由于近平面的宽高都是 2,即宽高比为 1: 1,但是 ViewPort 一般不是 1: 1,为了避免投影成像变形,通常将投影后的 x 坐标除以 ViewPort 的宽高比(假设为 r),因此有如下公式:

​ 由于 z0 是个变量,导致 (x1, y1) 与 (x0, y0) 之间不是线性关系。为了方便使用线性变换描述透视投影,将透视投影分为 2 个步骤:透视变换透视分割

​ 1)透视变换

​ 2)透视分割

​ 其中,x0, y0 是模型坐标,xp, yp 是透视变换后的坐标,x1, y1 是透视投影(或透视分割)后的坐标。

​ 透视投影划分 2 步后,透视变换可以使用如下方式表示:

​ 经透视投影后,x 轴和 y 轴的坐标都被归一化到 [-1, 1] 区间内,z 轴坐标同样也需要归一化到 [-1, 1] 区间内。本来z1 与 z0 之间应该是线性关系,但是考虑到 (xp, yp, zp) 与 (x0, y0, z0) 之间需要使用矩阵表示,即 zp 与 z0 之间存在线性关系,因此,有如下函数关系:

​ 进一步得到 z1 与 z0 的函数关系如下:

​ 使用待定系数法将 (-N, -1), (-F, 1) 代入求得 k 和 b 的值如下:

​ 因此,zp 与 z0 之间的函数关系如下:

​ 经透视变换后,接着需要进行透视分割,即将 (xp, yp, zp) 除以 (-z0),为保证透视变换后 z0 的信息不被丢失,将 -z0 值保存到第四维空间(即 w 维)中。因此,透视变换可以进一步使用如下方式表示:

​ frustumM 方法源码如下,m 是透视变换返回的矩阵;offset 为索引偏移,表示 m 中 offset 之前的数不参与变换,通常取 0;(left, right, bottom, top) 为投影平面的边框,通常取 (-ratio, ratio, -1, 1)(ratio为 ViewPort 宽高比);near 为近平面到相机的距离;far 为远平面到相机的距离。

public static void frustumM(float[] m, int offset,
float left, float right, float bottom, float top,
float near, float far) {
... //输入合法性校验
final float r_width = 1.0f / (right - left);
final float r_height = 1.0f / (top - bottom);
final float r_depth = 1.0f / (near - far);
final float x = 2.0f * (near * r_width);
final float y = 2.0f * (near * r_height);
final float A = (right + left) * r_width;
final float B = (top + bottom) * r_height;
final float C = (far + near) * r_depth;
final float D = 2.0f * (far * near * r_depth);
m[offset + 0] = x;
m[offset + 5] = y;
m[offset + 8] = A;
m[offset + 9] = B;
m[offset + 10] = C;
m[offset + 14] = D;
m[offset + 11] = -1.0f;
m[offset + 1] = 0.0f;
m[offset + 2] = 0.0f;
m[offset + 3] = 0.0f;
m[offset + 4] = 0.0f;
m[offset + 6] = 0.0f;
m[offset + 7] = 0.0f;
m[offset + 12] = 0.0f;
m[offset + 13] = 0.0f;
m[offset + 15] = 0.0f;
}

​ 透视变换除了 frustumM 方法,还可以使用 perspectiveM 方法,公式如下:

​ 其中,r 为 ViewPort 宽高比,θ 为视野角的一半。与 frustumM 方法相比,perspectiveM 方法仅对 z 进行了归一化,未对 x, y 进行归一化。

​ perspectiveM 方法源码如下,m 是透视变换返回的矩阵;offset 为索引偏移,表示 m 中 offset 之前的数不参与变换,通常取 0;fovy 为视野角度(角度制);aspect 为 ViewPort 宽高比;zNear 为近平面到相机的距离;zFar 为远平面到相机的距离。

public static void perspectiveM(float[] m, int offset,
float fovy, float aspect, float zNear, float zFar) {
float f = 1.0f / (float) Math.tan(fovy * (Math.PI / 360.0));
float rangeReciprocal = 1.0f / (zNear - zFar); m[offset + 0] = f / aspect;
m[offset + 1] = 0.0f;
m[offset + 2] = 0.0f;
m[offset + 3] = 0.0f; m[offset + 4] = 0.0f;
m[offset + 5] = f;
m[offset + 6] = 0.0f;
m[offset + 7] = 0.0f; m[offset + 8] = 0.0f;
m[offset + 9] = 0.0f;
m[offset + 10] = (zFar + zNear) * rangeReciprocal;
m[offset + 11] = -1.0f; m[offset + 12] = 0.0f;
m[offset + 13] = 0.0f;
m[offset + 14] = 2.0f * zFar * zNear * rangeReciprocal;
m[offset + 15] = 0.0f;
}

​ 声明:本文转自【OpenGL ES】透视变换原理

【OpenGL ES】透视变换原理的更多相关文章

  1. OpenGL ES

    前言 OpenGL ES是Khronos Group创建的一系列API中的一种(官方组织是:http://www.khronos.org/).在桌面计算机上有两套标准的 3DAPI:Direct3D和 ...

  2. Android OpenGL ES 离屏渲染(offscreen render)

    通常在Android上使用OpenGL ES,都是希望把渲染后的结果显示在屏幕上,例如图片处理.模型显示等.这种情况下,只需要使用Android API中提供的GLSurfaceView类和Rende ...

  3. OpenGL ES无法获取贴图数据原因

    最近在做一个项目,要从贴图中获取图像数据,查了很多资料,也琢磨很久,获取到的数据都是0.终于在一次偶然的机会,发现了端倪,成功了. 不得不说这"一分灵感"真的很重要 以下是在获取贴 ...

  4. 在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping)

    在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping) 视差贴图 最近一直在研究如何在我的 iPad 2(只支持 OpenGL ES 2.0, 不支持 3.0) 上实现 视 ...

  5. Android OpenGL ES(二)----平滑着色

    直线或者三角形上的每个片段混合后的颜色可以用一个varying生成.我们不仅能混合颜色,还可以给varying传递任何值,OpenGL会选择属于那条直线的两个值,或者属于那个三角形的三个值,并平滑地在 ...

  6. Android OpenGL ES(十四)gl10方法解析

    Android 支持 OpenGL 列表 1.GL 2.GL 10 3.GL 10 EXT 4.GL 11 5.GL 11 EXT 6.GL 11 ExtensionPack 我们将使用 GL10 这 ...

  7. Android OpenGL ES 开发(九): OpenGL ES 纹理贴图

    一.概念 一般说来,纹理是表示物体表面的一幅或几幅二维图形,也称纹理贴图(texture).当把纹理按照特定的方式映射到物体表面上的时候,能使物体看上去更加真实.当前流行的图形系统中,纹理绘制已经成为 ...

  8. OpenGL ES平移矩阵和旋转矩阵的左乘与右乘效果

    OpenGL ES平移矩阵和旋转矩阵的左乘与右乘 在OpenGL .OpenGL ES中矩阵起着举足轻重的作用,而矩阵之间的左乘与右乘在效果上是不同的. 一.先平移后旋转 场景效果:人绕树旋转. 原理 ...

  9. OpenGL ES学习资料总结

    从今年春节后开始学习OpenGL ES,发现网上资料很有限,而且良莠不齐,所以整理了一下我学习时用到的资料和一些心得. 1. OpenGL ES1.x参考资料 把NEHE的教程移植到了Android上 ...

  10. 基于Cocos2d-x学习OpenGL ES 2.0之多纹理

    没想到原文出了那么多错别字,实在对不起观众了.介绍opengl es 2.0的不多.相信介绍基于Cocos2d-x学习OpenGL ES 2.0之多纹理的,我是独此一家吧.~~ 子龙山人出了一个系列: ...

随机推荐

  1. 非标准库--conio.h库

    1.getch函数 主要内容 int getch(void): 所在头文件:conio.h 函数用途:从控制台读取一个字符,但不显示在屏幕上,即一个不需要通过ENTER确定的getchar. 函数原型 ...

  2. [转帖]strace分析sqlplus登录慢问题

    一. 问题分析 有时会遇到sqlplus / as sysdba登录非常慢的问题,由于还没登录,通过数据库等待事件一般看不出来啥,需要用到strace这个分析利器.strace有很多参数,后面会列出, ...

  3. [转帖]linux 部署jmeter&报错处理

    一.linux 安装jdk Java Downloads | Oracle 二. linux上传jmeter 2.1 上传jmeter jmeter 下载地址: Apache JMeter - Dow ...

  4. [转帖]煮饺子与 docker、kubernetes 之间的关系

      前言:云原生的概念最近非常火爆,企业落地云原生的愿望也越发强烈.看过很多关于云原生的文章,要么云山雾罩,要么曲高和寡. 所以笔者就有了写<大话云原生>系列文章的想法,期望用最通俗.简单 ...

  5. [转帖]strace 命令详解

    目录 1.strace是什么? 2.strace能做什么? 3.strace怎么用? 4.strace问题定位案例 4.1.定位进程异常退出 4.2.定位共享内存异常 4.3. 性能分析 5.总结 1 ...

  6. [转帖]linux 调优各项监控指标小记

    https://z.itpub.net/article/detail/8A4E4E96522BD59D45AB5A4CA442EDB3 自开始负责生产环境部署,中间遇到了若干线上环境内存以及CPU的问 ...

  7. Linux时间戳转换成易读格式的方法

    背景 最近一直在学习Redis相关的知识. 其中遇到了一个redis monitor的命令 但是这里有一个问题是: 原生命令查询出来的时间是Unix时间戳格式的. 不太好发现查看与进行对照. 所以今天 ...

  8. Windows设置一键安装Mysql数据库的方法

    Windows设置一键安装Mysql数据库的方法 前言 因为MySQL数据库的8126 65536 以及3072最大索引长度等问题 研发这边提交的补丁总是出现稀奇古怪的问题. mysql数据库又因为D ...

  9. js文件下载blob

    使用axios文件下载 if (tableDataSource.selectedRowKeys.length > 0) { //本次请求你携带token axios.defaults.heade ...

  10. Vue基础系统文章07---webpack安装和配置与打包

    1.当前web开发困境 a.文件依赖关系错综复杂 b.静态资源请求效率低 c.模块化支持不友好 d.浏览器对高级js兼容性低 例如:模块代码实现隔行换色 1)在新建空白文件夹中运行:npm init ...