深入探索透视投影变换(续)

2023-11-16

-潘宏

-2009.4.14

-本人水平有限,疏忽错误在所难免,还请各位数学高手、编程高手不吝赐教

-email: popyy@netease.com

-B站专栏: https://b23.tv/oWsl6PD

 

 

在上一篇文章中我们讨论了透视投影变换的原理,分析了OpenGL所使用的透视投影矩阵的生成方法。正如我们所说,不同的图形API因为左右手坐标系、行向量列向量矩阵以及变换范围等等的不同导致了矩阵的差异,可以有几十个不同的透视投影矩阵,但它们的原理大同小异。这次我们准备讨论一下Direct3D(以下简称D3D)以及J2ME平台上的JSR184(M3G)(以下简称M3G)的透视投影矩阵,主要出于以下几个目的:

 

(1)       我们在写图形引擎的时候需要采用不同的图形API实现,当前主要是OpenGL和D3D。虽然二者的推导极为相似,但D3D的自身特点导致了一些地方仍然需要澄清。

(2)       DirectX SDK的手册中有关于透视投影矩阵的一些说明,但并不详细,甚至有一些错误,从而使初学者理解起来变得困难,而这正是本文写作的目的。

(3)       M3G是J2ME平台上的3D开发包,采用了OpenGL作为底层标准进行封装。它的透视投影矩阵使用OpenGL的环境但又进行了简化,值得一提。

 

本文努力让读者清楚地了解D3D与M3G透视投影矩阵的原理,从而能够知道它与OpenGL的一些差别,为构建跨API的图形引擎打好基础。需要指出的一点是为了完全理解本文的内容,请读者先理解上一篇文章《深入探索透视投影变换》的内容,因为OpenGL和它们的透视投影矩阵的原理非常相似,因此这里不会像上一篇文章从基础知识讲起,而是对比它们的差异来推导变换矩阵。我们开始!

 

OpenGL与D3D的基本差异

前面提到,不同API的基本差异导致了最终变换矩阵的不同,而导致OpenGL和D3D的透视投影矩阵不同的原因有以下几个:

 

(1)       OpenGL默认使用右手坐标系,而D3D 默认使用左手坐标系。

 fig1_coordsys.JPG

 

(2)       OpenGL使用列向量矩阵乘法而D3D使用行向量矩阵乘法。

 

 fig2_mat_vec_mul.JPG

 

(3)       OpenGL的CVV的Z范围是[-1, 1],D3D的CVV的Z范围是[0, 1]。

 

 

    以上这些差异导致了最终OpenGL和D3D的透视投影矩阵的不同。

 

D3D的透视投影矩阵推导

我们先来看最最基本的透视关系图(上一篇文章开始的时候使用的图):

 

fig3_proj_theory.JPG

这里我们考察的是xz平面上的关系,yz平面上的关系同理。这里o是相机位置。np是近裁剪平面,也是投影平面,N是它到相机的距离。fp是远裁剪平面,F是它到相机的位置。p是需要投影的点,p’是投影之后的点。根据相似三角形定理,我们有

 

fig4_similar_triangles.JPG

 

则有

 

fig5_components.JPG

 

注意到OpenGL使用右手坐标系,因此应该使用-N(请参考上一篇文章的这一步),而D3D使用左手坐标系,因此使用N,这是者的不同点之一。这样,我们得到投影之后的点

fig6_trans_point1.JPG

 

第三个信息点是变换之后的z在投影平面上的位置,也就是N,它已经没用了,我们把p’写成

fig7_trans_point2.JPG

 

从而用第三个没用信息点它来存储z(如果读者对这一点不太了解,请参考上一篇文章)。接下来我们求出a和b,从而在z方向上构建CVV。请注意这里是OpenGL和D3D的另一个不同点,OpenGL的CVV的z范围是[-1, 1],而D3D的CVV的z范围是[0, 1]。也就是说,D3D 中在近裁剪平面上的点投影之后的点会处于CVV的z=0平面上,而在远裁剪平面上的点投影之后的点会在CVV的z=1平面上。这样我们的计算方程就是

 

 fig8_ab.JPG

 

从而我们得到了透视投影矩阵的第一个版本

 

fig9_persp_proj_ver1.JPG

 

fig10_persp_proj_formula.JPG

 

这个时候第三个分量变换到CVV情形了,CVV的z范围是[0,1]。接下来根据上一篇文章所讲到的,我们要把前两个分量变成CVV情形,CVV的x和y范围是[-1, 1],如下图所示:

fig11_lerp1.JPG

 

使用线性插值,我们有:

 

fig12_lerp2.JPG

 

这里left和right是投影平面的左右范围,top和bottom是投影平面的上下范围。xcvv和ycvv是我们需要算出的在CVV情形中的x和y,也就是我们要计算出的结果。但在算出它们之前,我们先把上面的式子写成:

 

fig13_lerp3.JPG

 

这里有一个需要注意的地方,如果投影平面在x方向上居中,则

 

 fig14_half_width.JPG

 

那么第一个式子就可以销掉等号两边的1/2,写成

 

fig15_remove_half_width.JPG

 

同理,如果投影平面在y方向上居中,则第二个式子可以写成

 

fig16_remove_half_width2.JPG

 

则我们现在分两种情况讨论:

(1)       投影平面的中心和x-y平面的中心重合(在x和y方向上都居中)

(2)       一般情况

我们分别讨论:

 

(1)特殊情况方程

 

fig17_special_case.JPG

 

这组是特殊情况,方程比较简单,但也是使用频率最高的方式(这是D3DXMatrixPerspectiveLH、D3DXMatrixPerspectiveRH、D3DXMatrixPerspectiveFovLH、D3DXMatrixPerspectiveFovRH四个方法所使用的情况)。我们导出它:

 

fig18_cvv_xy.JPG

 

则我们反推出透视投影矩阵:

 

fig19_persp_proj_mat1.JPG

 

其中

 

fig20_ab.JPG

 

而r-l和t-b可以分别看作是投影平面的宽w和高h。最后那个矩阵就是D3D的透视投影矩阵之一。另外呢,如果我们不知道right、left、top以及bottom这几个参量,也可以根据视野(FOV – Field Of View)参量来求得。下面是两个平面的视野关系图:

 

fig21_fov.JPG

 

fig22_fov_deduction.JPG

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

其中,两个fov分别是在x-z以及y-z平面上的视野。如果只给了一个视野,也可以通过投影平面的宽高比计算出来:

fig23_aspect_ratio.JPG

 

用一个视野算出w或者h,然后用宽高比算出h或者w。

 

(2)一般情况的方程

 

fig24_general_case.JPG

 

 

这组方程比较繁琐,但更具一般性(和OpenGL一般矩阵的推导一致,这也是D3DXMatrixPerspectiveOffCenterLH和D3DXMatrixPerspectiveOffCenterRH两个方法所使用的情况)。我们导出它:

 

 fig25_general_case_xy.JPG

 

我们继续反推出透视投影矩阵:

 

fig26_general_case_mat.JPG

其中

fig27_ab.JPG

 

最后那个矩阵就是D3D的一般透视投影矩阵。

 

好了,目前为止,我们已经导出了D3D的两个透视投影矩阵。下面我把上一篇导出的OpenGL的透视投影矩阵写出来,大家可以拿它和刚刚导出的D3D的一般性透视投影矩阵做一个对比。

fig28_opengl_mat.JPG

 

如果仔细观察,可以发现二者在元素的布局上是一个转置的关系,这个就是由它们使用的左右手坐标系以及使用的行列矩阵的差异造成的。而另外在一些元素的细节上也存在着差异,这是由于D3D的CVV的z范围不同造成的。可见在原理相同的情况下,细微的环境差异可以造成非常大的变化,而这就是透视投影矩阵存在诸多不同版本的原因。一般情况的透视投影矩阵也可以使用视野方式来定义,方法和特殊情况相同。

M3G的透视投影矩阵

M3G是对OpenGL进行的一个封装,它的透视投影变换矩阵被放到了类Camera里面。因为它封装了OpenGL,因此环境和OpenGL相同:右手坐标系、列向量乘法、CVV范围[-1, 1]。它唯一和OpenGL有些差异的地方就在于它只使用投影平面的中心和x-y平面的中心重合(在x和y方向上都居中)的情况(就是我们上面D3D的第一种特殊情况)。我们用OpenGL透视投影矩阵最终版本来说明(再次提醒,如果读者对此感到迷惑,请参考第一篇文章):

 

fig28_opengl_mat.JPG

 

上面是OpenGL透视投影矩阵的最终版本,也是一般性版本,我们要把它变成特殊性,版本,非常简单,和上面D3D的特殊情况一样,我们从对x和y进行插值的那一步来看:

 

fig29_m3g_lerp.JPG

 

和D3D的第一种情况一样,销掉两边的1/2,得到:

 

fig30_m3g_remove_half.JPG

 

则我们反推出透视投影矩阵:

 

fig31_m3g_mat.JPG

 

最右边那个矩阵就是M3G的透视投影矩阵。仍然可以通过视野参数来设置透视投影矩阵,这里请读者自行推导,方法与上面D3D的完全相同。

 

结束语

    我们已经完成了对D3D和M3G透视投影矩阵的说明。如果读者理解了上面的内容,可能会觉得有些厌恶——为什么没有一个统一的标准,在同一个原理下为什么要弄出这么多种差别?原因有很多,历史遗留问题、API厂商之间的问题等等。但对于我们来说,抓住了原理以及方法,不论如何变化都应该不会迷失。下次见!

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入探索透视投影变换(续) 的相关文章

  • 使用 SAS EG 通过代理从 API 下载 JSON 文件

    我正在尝试使用瑞士当局提供的 API 对公司网络内的地址进行地理编码 我的公司使用带有用户名和密码的代理服务器 我是 SAS EG 的新手 这是我迄今为止拥有的代码 我必须匿名一些内容才能被允许在此处发布 filename response
  • 学院/大学数据 API [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试构建一个应用程序 允许用户查找特定大学并查看有关该大学的数据 录取率 SAT 分数 规模等 但
  • useState 由于某种原因没有更新?

    当我尝试使用 axios 从后端 API 获取一些数据 并在由于某种原因获得结果后设置状态时 状态不会更新 当我尝试使用状态时 它只会向我显示一个空数组 但有趣的是当我console log res data 它会毫无问题地向我显示我的列表
  • 超出 CreateConstantBufferView 处虚拟地址的末尾

    我正在遵循 使用 DirectX12 进行游戏编程 ch 6 代码 但在 ID3DDevice CreateConstantBufferView 中 我发现 D3D12 错误 D3D12 错误 ID3D12Device CreateCons
  • 如何使用 Twitter Api 在单个请求中获取 20 多个列表成员?

    我想让超过 20 个用户在单个请求中使用 twitter api 有什么参数可以指定吗 我正在使用这个APIhttp api twitter com 1 Barelyme Politics members xml cursor 1 http
  • Swift:转义闭包捕获非转义参数“onCompletion”

    我的 swift 有问题 我正在尝试发送 API 请求 然后检索数据 但收到以下错误消息 Swift 转义闭包捕获非转义参数 onCompletion 有谁知道我该如何解决这个问题 提前致谢 Code class RestApiManage
  • 我可以查看当前登录的 Twitter 用户吗?

    他们没有通过我的网站进行 OAuth 验证 但在该浏览器上登录到 Twitter 有什么办法让我知道他们的 Twitter 用户名吗 饼干 什么 除非某些东西非常糟糕或者 Twitter 明确提供了用于此目的的 API 否则不会 至少不是通
  • 使用 C#.net 中的私有存储库的身份验证读取 BitBucket API

    我已经尝试了几天让 BitBucket API 为我工作 但是当涉及到让它为具有身份验证的私有存储库工作时 将问题设置为私有 当它们设置为公开 无需身份验证 一切正常 代码示例如下 static void Main string args
  • Codeception API 测试响应返回“N/A”

    我正在使用 Laravel 和 Codeception 作为测试框架编写 API 我无法让 Codeception 返回响应代码 我可以从 Codeception 获取响应代码 以及 JSON 响应 我编写了一个简单的测试来从 GET 请求
  • XML-RPC 与 REST

    我打算在这里构建一个小型服务器 并想为其创建一个 API 我正在决定什么是更好的并且已经排除了SOAP因为在我看来那件事是一团糟 我只剩下REST and XML RPC 我真的很喜欢XML RPC 它实现起来非常简单 并且足够常规 所有客
  • Yammer API 限制

    Yammer Rest api 文档表示 每个应用程序每个用户在 10 秒内最多可以发出 10 个请求 问题 什么是user这里 是为当前经过身份验证的 yammer 用户发出请求的 IP 地址还是承载令牌 如果我们所有的用户都使用相同的外
  • Streamlabs API 405 响应代码

    我正在尝试使用Streamlabs API https dev streamlabs com Streamlabs API 使用 Oauth2 来创建应用程序 因此 首先我将使用我的应用程序的用户发送到一个授权链接 其中包含我的应用程序的客
  • 在Wordpress中通过API创建新用户时如何发送电子邮件密码?

    可以使用以下行通过 API 创建新用户 user id wp insert user user data 我想知道如何向新创建的用户发送包含其密码的电子邮件 Wordpress API 中是否有任何函数可以处理这项工作 还是我应该自己创建并
  • Android GCM 服务器的 API 密钥

    我有点困惑我应该为 GCM 服务器使用哪个 API 密钥 在文档中它说使用 android api 密钥 这对我不起作用并且总是给出未经授权的 http developer android com google gcm gs html ht
  • 如何为 ApplicationController 中 after_action 过滤器中的所有操作渲染 json?

    是否可以在 Rails ApplicationController 中创建一个 after filter 方法 该方法在每个操作上运行并呈现为 JSON 我正在构建一个 API 并且希望将控制器中的每个操作的输出呈现为 JSON 客户控制器
  • 端点按资源 swagger 注释分组?

    我正在使用 Spring 进行 REST API 开发 我有一些 API 其中有很多端点 当我打开 swagger ui 时 它看起来很拥挤 我刚刚读过this https swagger io docs specification gro
  • 带有 OAuth2 的 YouTube API v3:更新和删除失败并出现“权限不足”错误

    我在尝试着update and delete视频使用YouTube API v3 https developers google com youtube v3 docs videos with OAuth2 用于身份验证 https dev
  • 使用 PHP 和 OAuth 访问 SkyDrive

    我想使用 PHP 访问 skyDrive 我想检索文件和文件夹列表 下载 上传和删除文件 我有一个 microsoft dev clientID 和 clientSecret 有人可以帮助我开始使用 OAuth 连接到 skyDrive 并
  • 是否有 .NET 库或 API 可以与 IIS 配置数据库交互/编辑它?

    或者我是否坚持使用自己的 XML 切割 功能 我想创建一个小型任务托盘应用程序 以便我可以快速将虚拟目录重新指向硬盘上的几个文件夹之一 一点背景 我的开发机器上的代码库有 3 个不同的 svn 分支 Current Production B
  • 简单、安全的API认证系统

    我有一个简单的 REST JSON API 供其他网站 应用程序访问我网站的一些数据库 通过 PHP 网关 基本上该服务的工作原理如下 调用 example com fruit orange 服务器返回有关橙子的 JSON 信息 问题是 我

随机推荐

  • 某游戏大厂测开笔试题分享

    测开笔试题 某厂笔试题 执行时限1000ms 一个典型的电话拨号盘如下 1 2 3 4 5 6 7 8 9 0 手指在两个按键之间的移动距离被定义为这两个键的x y坐标差的绝对值之和 比如 6到自身的距离是0 到3 5 9键的距离是1 到2
  • 证件照如何换底色,分享三种证件照换底色的方法!

    在我们的日常生活中 不同场景需要使用不同颜色的证件照 如果我们需要更换证件照的背景颜色 通常情况下人们会选择去照相馆重新拍摄一组照片 但这样费时费力 而且在遇到紧急情况时可能来不及 本文将介绍三种非常实用的方法 希望能对您有所帮助 方法一
  • Langchain使用介绍之-文档加载

    Lanchain提供了加载多种文档的能力 Lanchain初了能加载txt csv等格式文档外 还支持加载网页 音频 pdf等 本篇博客将介绍如何通过Langchain完成PDF文档 音频文档 网页文档的加载 加载PDF文档 通过使用Lan
  • ChatGPT 中文调教指南。各种场景使用指南。学习怎么让它听你的话

    ChatGPT是由OpenAI训练的一款大型语言模型 能够生成类人文本 您只需要给出提示或提出问题 它就可以生成你想要的东西 在此页面中 您将找到可与 ChatGPT 一起使用的各种提示 正经指南 写小说 写一本拥有出人意料结局的推理小说
  • 1.1关于数据挖掘

    一 数据挖掘是什么 从技术层面讲 数据挖掘指从大量数据中提取潜在有用的信息和知识的过程 从商业层面讲 数据挖掘是一种对大量业务数据进行抽取 转换 分析和建模处理 并从中提取辅助商业决策的关键数据的商业信息处理技术 二 数据挖掘与传统传统数据
  • 2021-5-13 爬虫之Xpath的下载与安装,简单教学!

    5 13学习日记之Xpath Xpath的安装 怎么安装Xpath 问题一 Xpath的安装 XPath 是一门在 XML 文档中查找信息的语言 XPath 可用来在 XML 文档中对元素和属性进行遍历 简单来说 在进行网页信息爬取时 Xp
  • 抖音新版本抓包(绕过sslpinning证书校验)

    目录 前言 方案 frida 替换so Xposed 前言 当我们想要分析较新版本的接口时 会发现一个有趣的现象 无论是用Charles还是Fiddler 都会出现抓不到包的情况 如下图 这是因为使用SSL Pinning证书锁定技术 是一
  • mysql性能优化

    1 表字段要选择合适的属性 邮政编码设置char 6 就可以了 文本字段如省份或者性别用enum enum被当做数值型数据来处理 比文本类型快 2 建立索引 3 优化查询语句 查询条件里最好用in替代on 条件列表值如果连续 用betwee
  • 注意力机制——CAM、SAM、CBAM、SE

    CAM SAM CBAM详见 CBAM 即插即用的注意力模块 附代码 目录 1 什么是注意力机制 2 通道注意力机制 SE 1 Squeeze 2 Excitation 3 SE Block 3 CAM 4 SAM 5 CBAM 6 代码
  • 一篇文章入门C/C++自动构建利器之Makefile

    更多博文 请看音视频系统学习的浪漫马车之总目录 C C 编译 浅析C C 编译本质 一篇文章入门C C 自动构建利器之Makefile 升级构建工具 从Makefile到CMake Makefile简介 上一篇浅析C C 编译本质已经比较详
  • UE4修改splitScreen layout方式

    UE4自身提供了分屏显示的方法 可以分成两个屏幕 也可以分成三个屏幕 但提供的分屏布局方式有限 包括两个玩家分屏布局 Vertical Horizontal 三个玩家分屏布局 Favor Top Favor Bottom 这里提供给大家一种
  • 华为OD机试真题 Java 实现【光伏场地建设规划】【2023Q1 100分】

    一 题目描述 祖国西北部有一片大片荒地 其中零星的分布着一些湖泊 保护区 矿区 整体上常年光照良好 但是也有一些地区光照不太好 某电力公司希望在这里建设多个光伏电站 生产清洁能源 对每平方公里的土地进行了发电评估 其中不能建设的区域发电量为
  • 【TS】联合类型

    文章目录 TS 联合类型 1 普通变量声明 2 数组声明使用 3 函数中使用 TS 联合类型 联合类型即通过管道 将变量设置多种类型 赋值时可以根据设置的类型来赋值 1 普通变量声明 let res number string boolea
  • MySQL中的事务、索引、锁机制

    目录 一 MySQL基础架构 二 MySQL中的索引 1 什么是索引 索引的作用 2 索引的类型 1 主键索引 Primary Key 2 二级索引 辅助索引 3 索引的底层数据结构 1 B 树 B 树 2 为什么选择B B 树 为什么要用
  • 图块引用、属性块引用、创建新层表、ObjectArx开发中静态变量和全局变量要点

    一 图块设计技术 在AutoCAD数据库中 以各图块实际上市存储在块表记录里的实体集合 每个图块从一格AcDbBlockBegin对象开始 紧接着是一个或多个AcDbEntity对象 最后以AcDbBolckEnd对象结束 按其所属关系得层
  • Googleplay从服务器检索信息时出错。[DF-DFERH-01]

    googleapis cn services googleapis cn 两个地址强制走代理就OK了 转载于 https www cnblogs com Devopser p 10269312 html
  • Linux下利用TCP的服务端和客户端实现简单收发消息

    服务端采用了线程 可以同时提供给多台客户端连接 TCP的服务端 tcp server c include
  • 每秒上万并发下的Spring Cloud性能优化之道

    本文为转载文章 作者 中华石杉 十余年BAT架构经验 倾囊相授 作者微信公众号 石杉的架构笔记 ID shishan100 一 写在前面 相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构 毕竟现在这是非常火的一门技术
  • MySQL中的any_value()函数

    转载 MySQL中的any value 函数 小李小李彬彬有礼的博客 CSDN博客
  • 深入探索透视投影变换(续)

    潘宏 2009 4 14 本人水平有限 疏忽错误在所难免 还请各位数学高手 编程高手不吝赐教 email popyy netease com B站专栏 https b23 tv oWsl6PD 在上一篇文章中我们讨论了透视投影变换的原理 分