Metal 透视投影

引言

Hi,大家好,我是一牛。在上一篇博客中,我们学习了如何在Metal 中使用MVP变换实现正交投影。正交投影常用于工程制图,它可以保持物体的比例不变,但是如果我们想要模拟人眼中的现实世界(近大远小),我们需要使用透视投影。今天,让我们一起学习下透视投影是如何实现的。

视椎体

透视投影矩阵

和正交投影使用长方体不同的是,在透视投影中我们使用了视椎体,经过模型变换、视图变换后,为了得到透视投影矩阵,我们只需要将是椎体压缩成长方体,之后再进行一次正交投影,我们就能够得到透视矩阵。

那么我们是如何通过挤压视椎体得到正交投影需要要的长方体?

假设我们从+x 方向观察,看向-x

根据相似三角形我们可以得到

<math xmlns="http://www.w3.org/1998/Math/MathML"> y ′ = n y z y' = \frac{ny}{z} </math>y′=zny

同理,我们可以得到

<math xmlns="http://www.w3.org/1998/Math/MathML"> x ′ = n x z x' = \frac{nx}{z} </math>x′=znx

对于空间中的任何一点 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x y z 1 ) \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} </math> xyz1

经过挤压得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( n x z n y z u n k n o w n 1 ) \begin{pmatrix} \frac{nx}{z} \\ \frac{ny}{z} \\ unknown \\ 1 \end{pmatrix} </math> znxznyunknown1

由于是齐次坐标,我们可以将乘z 得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( n x n y u n k n o w n z ) \begin{pmatrix} nx \\ ny \\ unknown \\ z \end{pmatrix} </math> nxnyunknownz

我们可以初步得到这个矩阵

<math xmlns="http://www.w3.org/1998/Math/MathML"> ( n 0 0 0 0 n 0 0 A B C D 0 0 1 0 ) \begin{pmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ A & B & C & D \\ 0 & 0 & 1 & 0\end{pmatrix} </math> n0A00nB000C100D0

接下来我们只需要求出待定系数 A,B,C,D

我们知道,近平面上的点经过挤压保持不变。也就是说对于近平面任意一点 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x y n 1 ) \begin{pmatrix} x \\ y \\ n \\ 1 \end{pmatrix} </math> xyn1

由于它是齐次坐标,我们可以乘上n,得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( n x n y n 2 n ) \begin{pmatrix} nx \\ ny \\ n^2 \\ n \end{pmatrix} </math> nxnyn2n

我们观察待求矩阵的第三方,可得 A=0,B=0,即

<math xmlns="http://www.w3.org/1998/Math/MathML"> [ 0 0 A B ] \begin{bmatrix} 0 & 0 & A & B \end{bmatrix} </math>[00AB] <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x y n 1 ) \begin{pmatrix} x \\ y \\ n \\ 1 \end{pmatrix} </math> xyn1 = <math xmlns="http://www.w3.org/1998/Math/MathML"> n 2 n^2 </math>n2

-> <math xmlns="http://www.w3.org/1998/Math/MathML"> A ∗ n + B = n 2 A * n + B = n^2 </math>A∗n+B=n2 (1)

又因为远平面的中心经过挤压保持不变,所以对应远平面中心点 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 0 0 f 1 ) \begin{pmatrix} 0 \\ 0 \\ f \\ 1 \end{pmatrix} </math> 00f1

由于它是齐次坐标,我们可以乘上f,得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 0 0 f 2 f ) \begin{pmatrix} 0 \\ 0 \\ f^2 \\ f \end{pmatrix} </math> 00f2f

-> <math xmlns="http://www.w3.org/1998/Math/MathML"> [ 0 0 A B ] \begin{bmatrix} 0 & 0 & A & B \end{bmatrix} </math>[00AB] <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 0 0 f 1 ) \begin{pmatrix} 0 \\ 0 \\ f \\ 1 \end{pmatrix} </math> 00f1 = <math xmlns="http://www.w3.org/1998/Math/MathML"> f 2 f^2 </math>f2

-> <math xmlns="http://www.w3.org/1998/Math/MathML"> A ∗ f + B = f 2 A * f + B = f^2 </math>A∗f+B=f2 (2)

联立(1)(2)解一元二次方程组

得 <math xmlns="http://www.w3.org/1998/Math/MathML"> A = n + f A = n + f </math>A=n+f 、 <math xmlns="http://www.w3.org/1998/Math/MathML"> B = − n ∗ f B = -n*f </math>B=−n∗f

带人A,B,C,D 系数,得到挤压矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( n 0 0 0 0 n 0 0 0 0 n + f − n ∗ f 0 0 1 0 ) \begin{pmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n + f & -n*f \\ 0 & 0 & 1 & 0\end{pmatrix} </math> n0000n0000n+f100−n∗f0

将挤压矩阵左乘正交投影矩阵 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 2 r − l 0 0 − r + l r − l 0 2 t − b 0 − t + b t − b 0 0 1 f − n − n f − n 0 0 1 0 ) \begin{pmatrix} \frac{2}{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\ 0 & \frac{2}{t-b} & 0 & -\frac{t+b}{t-b} \\ 0 & 0 & \frac{1}{f-n} & -\frac{n}{f-n} \\ 0 & 0 & 1 & 0\end{pmatrix} </math> r−l20000t−b20000f−n11−r−lr+l−t−bt+b−f−nn0

最终透视投影矩阵是

<math xmlns="http://www.w3.org/1998/Math/MathML"> ( 2 n r − l 0 − r + l r − l 0 0 2 n t − b − t + b t − b 0 0 0 f f − n − n ∗ f f − n 0 0 1 0 ) \begin{pmatrix} \frac{2n}{r-l} & 0 & -\frac{r+l}{r-l} & 0 \\ 0 & \frac{2n}{t-b} & -\frac{t+b}{t-b} & 0 \\ 0 & 0 & \frac{f}{f-n} & -\frac{n*f}{f-n} \\ 0 & 0 & 1 & 0\end{pmatrix} </math> r−l2n0000t−b2n00−r−lr+l−t−bt+bf−nf100−f−nn∗f0

在图形学中我们一般用视场角fovY,和近平面宽高比来定义透视投影矩阵。假定视椎体是对称物体, <math xmlns="http://www.w3.org/1998/Math/MathML"> b = − t b=-t </math>b=−t 且 <math xmlns="http://www.w3.org/1998/Math/MathML"> l = − r l=-r </math>l=−r

<math xmlns="http://www.w3.org/1998/Math/MathML"> t a n f o v Y / 2 = t / n tan fovY / 2 = t / n </math>tanfovY/2=t/n 且 <math xmlns="http://www.w3.org/1998/Math/MathML"> a s p e c t R a t i o = r / t aspectRatio = r / t </math>aspectRatio=r/t

我们可以得到透视投影矩阵

swift 复制代码
    func createPerspectiveMatrix(fov: Float, aspectRatio: Float, nearPlane: Float, farPlane: Float) -> simd_float4x4 {
        let tanHalfFov = tan(fov / 2.0);
        var matrix = simd_float4x4(0.0);
        matrix[0][0] = 1.0 / (aspectRatio * tanHalfFov);
        matrix[1][1] = 1.0 / (tanHalfFov);
        matrix[2][2] = farPlane / (farPlane - nearPlane);
        matrix[2][3] = 1.0;
        matrix[3][2] = -(farPlane * nearPlane) / (farPlane - nearPlane);
        return matrix;
    }

效果

仔细观察,与正交投影变换不同的是,图片的比例在转动的过程中会发生改变,这是因为转动过程中图片的深度值在发生改变,对应不同的深度值,透视投影会产生近大远小的效果!

结语

掌握好透视投影是我们打开3D世界的钥匙,而掌握好透视投影的关键是了解透视投影的矩阵推导过程。 谢谢大家,欢迎大家点赞、收藏!

本项目已开源

相关推荐
sakiko_11 小时前
UIKit学习笔记5-使用UITableView制作聊天页面
笔记·学习·swift·uikit
朗清风13 小时前
“\“在字符串表示正则语义中的作用
swift
生而为虫18 小时前
Claude Code 最新版安装教程(Windows/Mac/Linux 全平台) 面向普通用户的 Claude Code 安装与模型接入指南
linux·windows·macos
懋学的前端攻城狮19 小时前
iOS 列表性能优化实战:从 45fps 到 60fps 的蜕变
ios·性能优化·ui kit
斯班奇的好朋友阿法法20 小时前
鸿蒙 vs iOS vs 微信小程序:开发平台全面对比
ios·微信小程序·harmonyos
李老师的Java笔记20 小时前
如何解决Mac升级完nodejs没有生效的问题?
macos
开开心心_Every21 小时前
轻量级PDF阅读器,仅几M大小打开秒开
linux·运维·服务器·安全·macos·pdf·phpstorm
Chengbei1121 小时前
轻量化 Web 安全日志分析神器 星川智盾日志威胁检测、地理溯源、MITRE ATT&CK 映射,支持 Windows/macOS/Linux
前端·人工智能·安全·web安全·macos·系统安全·安全架构
生而为虫21 小时前
在VScode中使用Claude Code agent并配置模型(仅mac电脑实际操作,windows电脑未实际操作如有问题可留言)
windows·vscode·macos
大飞记Python1 天前
刚从 Win 转 Mac?鼠标滚轮反向、触控板乱跑、第三方鼠标卡顿——这一篇就够了
macos·计算机外设·mac鼠标