2017.10.16更新,分割线下面是以前的文字,有表达的意思,却言不达意,实属羞耻,看官只需看前面文字即可。

Twinsen大神的《深入探索透视投影变换》有几个点说得不够清晰,我这里提一下:

1.p'代表的是近平面的投影点,P'代表的是p'映射到单位立方体后的投影点;

2.原文:事实上是透视投影变换由两步组成:

  1)  用透视变换矩阵把顶点从视锥体中变换到裁剪空间的CVV中。

  2)  CVV裁剪完成后进行透视除法(一会进行解释)。

故,该文求的是第1)步的那个变换矩阵,只不过用到了第2步的信息来约束,因为这些约束很有用(单位立方体),所以该文先求得透视除法后的坐标点P',然后返回来*w求得变换矩阵。这也是该文大量篇幅所做的:在2)的约束下求P'。

3.那P'*w和p'什么关系?即p'和1)中那个变换矩阵什么关系?看下图:

    视空间=》p'=》单位立方体空间

    视空间=》裁剪空间=》单位立方体空间

  P'*w就是裁剪空间的坐标,而p'不是,但因为他们都处于中间流程,所以容易混淆!事实上,p'不过是求单位立方空间坐标的过渡。

  注:顶点shader输入是模型空间坐标,输出是裁剪空间坐标!

4.以上3点很重要,也是原文没注意到却影响阅读效果的细节,那问题来了,为什么要分1)2)步,不直接拿P'给后续流水线?其他的俺不知,但显然P'如下格式是不易构造变换矩阵的:

=======================羞耻分割线=======================

首先感谢Twinsen大神在此块的知识分享,我在好几篇文章中解惑不少,这篇文章是针对《深入探索透视投影变换》的个人看法,本来是打算直接评论中交流,但为了方便自己复习就写了篇随笔记一下,本文是基本copy原文中间那部分,只是进行了一点小改动,红色字体标出。

原文地址:http://blog.csdn.net/popy007/article/details/1797121

透视投影变换

好,有了上面两个理论知识,我们开始分析这次的主角——透视投影变换。这里我们选择OpenGL的透视投影变换进行分析,其他的APIs会存在一些差异,但主体思想是相似的,可以类似地推导。经过相机矩阵的变换,顶点被变换到了相机空间。这个时候的多边形也许会被视锥体裁剪,但在这个不规则的体中进行裁剪并非那么容易的事情,所以经过图形学前辈们的精心分析,裁剪被安排到规则观察体(Canonical View Volume, CVV)中进行,CVV是一个正方体,x, y, z的范围都是[-1,1],多边形裁剪就是用这个规则体完成的。所以,事实上是透视投影变换由两步组成:

1)  用透视变换矩阵把顶点从视锥体中变换到裁剪空间的CVV中。

2)  CVV裁剪完成后进行透视除法(一会进行解释)。

我们一步一步来,我们先从一个方向考察投影关系。

上图是右手坐标系中顶点在相机空间中的情形。设P(x,z)是经过相机变换之后的点,视锥体由eye——眼睛位置,np——近裁剪平面,fp——远裁剪平面组成。N是眼睛到近裁剪平面的距离,F是眼睛到远裁剪平面的距离。投影面可以选择任何平行于近裁剪平面的平面,这里我们选择近裁剪平面作为投影平面。设P’(x’,z’)是投影之后的点,则有z’ = -N。通过相似三角形性质,我们有关系:

同理,有

这样,我们便得到了P投影后的点P’

从上面可以看出,投影的结果z’始终等于-N,在投影面上。实际上,z’对于投影后的P’已经没有意义了,这个信息点已经没用了。但对于3D图形管线来说,为了便于进行后面的片元操作,例如z缓冲消隐算法,有必要把投影之前的z保存下来,方便后面使用。

假设p'完成透视除法后变成p'',由于各种原因(原文有说明),需要把p''的z'变量变成 -(az+b)/z,即

p' = (x', y', z') = (x', y', -(az+b)/z)

你一定会问为什么要把z'写成那样子

有三个原因:

0)后面投影之后的光栅化阶段,要通过x'和y'对z进行线性插值,以求出三角形内部片元的z,进行z缓冲深度测试。在数学上,投影后的x'和y',与z不是线性关系,与1/z才是线性关系。而正是1/z的线性关系,即-a+b/z。用这个1/z的线性组合值和x'、y'进行插值才是正确的。(2013年11月补充条目。对此感到迷惑的读者可以参考《深入探索透视纹理映射》,里面从细节上说明了这个问题。)

1)  P’的3个代数分量统一地除以分母-z,易于使用齐次坐标变为普通坐标来完成,使得处理更加一致、高效。

2)  后面的CVV是一个x',y',z'的范围都为[-1,1]的规则体,便于进行多边形裁剪。而我们可以适当的选择系数a和b,使得这个式子在z = -N的时候值为-1,而在z = -F的时候值为1,从而在z方向上构建CVV。

接下来我们就求出a和b:

但这个时候我们只完成了z'统一到[-1,1],x'和y'还没完成,这个也同理根据线性差值,由P'和P''得到:

对于P',我们知道-Nx / z的有效范围是投影平面的左边界值(记为left)和右边界值(记为right),即[left, right],-Ny / z则为[bottom, top]。而现在我们想把把P''的x'束缚在[-1,1]中,即-Nx / z属于[left, right]映射到[-1, 1],-Ny / z属于[bottom, top]映射到y属于[-1, 1]中。你想到了什么?哈,就是我们简单的线性插值,你都已经掌握了!我们解决掉它:

【注,上面我用的是原文的图片,实际上,Nx的x是x,其他单独的x应该改成x',y同理】

这样就可以得到P''=(x', y' ,z')了,我们前面说过的透视变换分2步,第一步是用透视矩阵变换,第二步是透视除法,我们得到的P''是透视除法的结果,而我们需要得到的是透视矩阵,所以必须“回退”一下,即P'''是透视矩阵变换的结果:

P''' = P'' * -z(-z才是正的),然后从P'''中拆解出透视矩阵即可P''' = M * (x, y, z,1)T:

M就是最终的透视变换矩阵。相机空间中的顶点,如果在视锥体中,则变换后就在CVV中。如果在视锥体外,变换后就在CVV外。而CVV本身的规则性对于多边形的裁剪很有利。OpenGL在构建透视投影矩阵的时候就使用了M的形式。注意到M的最后一行不是(0 0 0 1)而是(0 0 -1 0),因此可以看出透视变换不是一种仿射变换,它是非线性的。另外一点你可能已经想到,对于投影面来说,它的宽和高大多数情况下不同,即宽高比不为1,比如640/480。而CVV的宽高是相同的,即宽高比永远是1。这就造成了多边形的失真现象,比如一个投影面上的正方形在CVV的面上可能变成了一个长方形。解决这个问题的方法就是在对多变形进行透视变换、裁剪、透视除法之后,在归一化的设备坐标(Normalized Device Coordinates)上进行的视口(viewport)变换中进行校正,它会把归一化的顶点之间按照和投影面上相同的比例变换到视口中,从而解除透视投影变换带来的失真现象。进行校正前提就是要使投影平面的宽高比和视口的宽高比相同。

此外,原文中说CVV是一个正方体,x, y, z的范围都是[-1,1],但推导过程中一直以透视除法后的坐标为[-1,1]为基准计算的,是不是透视除法后才得到CVV?

总结:本文认为原文这样的写法是错误的:,也因此导致了一些疑惑,记此文方便和Twinsen交流,如有误,麻烦指正。

读Twinsen的深入探索透视投影变换的更多相关文章

  1. 齐次坐标概念&&透视投影变换推导

    http://daehgib.blog.163.com/blog/static/1861071422011579551134/ 透视投影是3D固定流水线的重要组成部分,是将相机空间中的点从视锥体(fr ...

  2. 初探Stage3D(三) 深入研究透视投影矩阵

    关于本文 本文主要讲解从数学的角度如何推导出Stage3D中用到的两个投影矩阵 perspectiveLH public function perspectiveLH(width:Number,hei ...

  3. CSS 3 学习——transform 3D转换渲染

    以下内容根据官方规范翻译,没有翻译关于SVG变换的内容和关于矩阵计算的内容. 一般情况下,元素在一个无景深无立体感的平面(flat plane)上渲染,这个平面就是其包含块所处的平面.同时,页面上的其 ...

  4. [收藏夹整理]OpenCV部分

    OpenCV中文论坛 OpenCV论坛 opencv视频教程目录(初级) OpenCV 教程 Opencv感想和一些分享 tornadomeet 超牛的大神 [数字图像处理]C++读取.旋转和保存bm ...

  5. 【转】d3d的投影矩阵推导

    原帖地址:http://blog.csdn.net/popy007/article/details/4091967 上一篇文章中我们讨论了透视投影变换的原理,分析了OpenGL所使用的透视投影矩阵的生 ...

  6. Scaleform 中的 3D视角相关研究

    参考文献: 1.D3D中的第一人称视角 2.透视投影的原理和实现 http://blog.csdn.net/ww51xh/article/details/2910 3.深入探索透视投影变换 http: ...

  7. [收藏转载链接]Opencv部分

    转载自-柳如风-http://www.cnblogs.com/rongfangliu/p/opencvlink.html [收藏夹整理]OpenCV部分   OpenCV中文论坛 OpenCV论坛 o ...

  8. 透视投影(Perspective Projection)变换推导

    透视投影是3D固定流水线的重要组成部分,是将相机空间中的点从视锥体(frustum)变换到规则观察体(Canonical View Volume)中,待裁剪完毕后进行透视除法的行为.在算法中它是通过透 ...

  9. opengl视图变换 投影变换推导

    视图变换在opengl中,视图变换的输入是:(1)眼睛位置(或者说相机位置)eys:(2)眼睛朝向的中心center,(就是眼睛朝哪里看);(3)头的方向up.任何一点经过视图变换后都会转化到眼睛坐标 ...

随机推荐

  1. 精讲RestTemplate第2篇-多种底层HTTP客户端类库的切换

    本文是精讲RestTemplate第2篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 RestTemplate只是对其他的HTTP客 ...

  2. resultMap的用法以及关联结果集映射

    resultType resultType可以把查询结果封装到pojo类型中,但必须pojo类的属性名和查询到的数据库表的字段名一致. 如果sql查询到的字段与pojo的属性名不一致,则需要使用res ...

  3. 基于IDEA 代码提交Git

    基于IDEA 代码提交Git 步骤 1 创建一个项目 2 点击 VCS --> Import init Version Control --> Create Git Repository ...

  4. apicloud版人人商城app打包教程

    一.APP环境搭建和配置编译1.登录APICLOUD后台新建应用 步骤一 .注册账号注册apicloud 账号并登录APICLOUD控制台 注册apicloud 账号:https://www.apic ...

  5. 基于深度学习的人脸识别系统Win10 环境安装与配置(python+opencv+tensorflow)

    一.需要下载的软件.环境及文件 (由于之前见识短浅,对Anaconda这个工具不了解,所以需要对安装过程做出改变:就是Python3.7.2的下载安装是可选的,因为Anaconda已经为我们解决Pyt ...

  6. Maven骨架生成项目速度慢问题解决办法

    在创建maven project时(使用了archetype),速度慢的令人不敢相信,从Idea的控制台可以看到信息停留在: [INFO] <<< maven-archetype-p ...

  7. LinuX操作系统基础------> 用户管理,grep搜索

    用户管理 用户组管理 字符管理和grep搜索 1.用户管理: LinuX是一个多用户,多任务的操作系统,可以让多个用户使用,这个账号可以帮助对用户进行追踪,必须在初始化的时候设置好自己的密码 1-1配 ...

  8. three.js 着色器材质之变量(二)

    上一篇郭先生在例子中用到了着色器变量中的uniform和varying.这篇继续结合例子将一下attribute变量,在使用过程中也发现由于three.js的版本迭代,之前的一些属性和参数已经发生了改 ...

  9. 22、Command 命令模式

    1.command 命令模式 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指 ...

  10. Python3 连接 Oracle 数据库

    Python3 连接 Oracle 数据库 需要导出一些稍微复杂的数据,用Python处理很方便 环境 Win10 Python 3.7.0 Oracle 11g 安装依赖 安装 cx_Oracle ...