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. SpringMVC - 加载静态资源

    静态资源过滤 spring-config.xml <!-- 3,(1)让Spring MVC不处理静态资源 .(2)加载静态资源,也称为资源过滤 --> <mvc:default-s ...

  2. ECharts——快速入门

    ECharts快速入门 引入 ECharts <!DOCTYPE html> <html> <head> <meta charset="utf-8& ...

  3. 程序&命名-执行环境

    开发程序执行环境 系统级别 -- 编译器或解释器 程序级别 -- 命令行参数.配置文件 执行级别 -- 进程.线程.协程运行时上下文(树(命名空间 -- 函数-局部变量.包或模块-全局变量)) 命令行 ...

  4. [转帖]ssd/san/sas/磁盘/光纤/RAID性能比较

    https://plantegg.github.io/2022/01/25/ssd_san%E5%92%8Csas%E7%A3%81%E7%9B%98%E6%80%A7%E8%83%BD%E6%AF% ...

  5. [转帖]一行Python代码实现同一局域网内的文件共享

    在不同的设备之间传输文件除了数据线,网盘传输外是否还有其他优雅的方法?我们可以使用一行Python代码使局域网内的所有设备都可以访问并下载文件夹内的文件. 要求: 电脑中安装配置好python 访问的 ...

  6. [转帖]Elasticsearch部署配置建议

    1: 选择合理的硬件配置:尽可能使用 SSD Elasticsearch 最大的瓶颈往往是磁盘读写性能,尤其是随机读取性能.使用SSD(PCI-E接口SSD卡/SATA接口SSD盘)通常比机械硬盘(S ...

  7. 麒麟信安V3.4 安装PG15的过程

    麒麟信安V3.4 安装PG15的过程 背景 发现基于OpenEuler的几个系统使用CentOS的rpm包 安装PG数据库时有问题. 会提示缺少依赖的so文件. 今天想着解决一下, 就百度了一下并且进 ...

  8. 你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队

    1.前言 不要犹豫了,GC最大停顿时间小于1ms,支持16TB内存,这么高的性能提升,也不需要复杂的调优,节省了这个时间,你去陪对象不香嘛. 上篇文章给大家带来了JDK11升级JDK17的最全实践,相 ...

  9. devDependencies和dependencies有何区别

    01==>devDependencies用于本地环境开发时候所需要的依赖包. 上线后就不在需要了: npm i webpack-cli -D --save-dev等价为-D 意思是安装到开发环境 ...

  10. .netcore项目发布到IIS全流程

    一.环境准备 保证电脑上有.net core runtime下载地址:https://dotnet.microsoft.com/download/dotnet-core/current/runtime ...