美文网首页
Projection矩阵 Reverse-Z推导

Projection矩阵 Reverse-Z推导

作者: crossous | 来源:发表于2020-11-12 15:07 被阅读0次

一般DirectX透视矩阵推导

符号表:
\begin{align} &\alpha\quad\quad垂直FOV角 \\ &\beta\quad\quad水平FOV角 \\ &w、h\quad\quad宽、高 \\ &r\quad\quad宽高比\frac{w}{h} \\ &d\quad\quad当半高为1时,平面与相机的距离 \end{align}

为什么要半高为1?
因为我们希望最终计算结果在NDC空间中,范围在xy中都是[-1, 1],其中我们令y的半高为1,根据宽高比,x的半宽为r,后面我们回让x/r来达到xy都处于[-1, 1]范围内。

\tan{\frac{\alpha}{2}} = \frac{1}{d}\quad\quad\stackrel{}\Rightarrow\quad\quad d = \frac{1}{\tan{\frac{\alpha}{2}}}
\frac{w_1}{1}=\frac{w}{h}=r\quad\quad\stackrel{}\Rightarrow\quad\quad w_1=r
tan{\frac{\beta}{2}}=\frac{w_1}{d}=\frac{r}{d}=r\tan{\frac{ \alpha }{2}}
  给一点p=(x,y,z),求它的透视在半高为1平面上的点p'=(x', y', d),可以得到以下关系:
\frac{y}{z}=\frac{y'}{d} \quad\quad\stackrel{}\Rightarrow\quad\quad y'=\frac{y}{z\tan{\frac{\alpha}{2}}}
  同理x'=\frac{x}{z\tan{\frac{\alpha}{2}}}
  此平面上,y取值范围为[-1, 1],x取值范围为[-r, r],为保证1:1,将x除以r
  当前矩阵为:\left[ \begin{matrix} \frac{1}{r\tan{\frac{\alpha}{2}}} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan{\frac{\alpha}{2}}} & 0 & 0 \\ 0 & 0 & A & 1 \\ 0 & 0 & B & 0 \\ \end{matrix}\right]
  当view空间乘此矩阵时得到:
  当前矩阵为:\left[x, y, z, 1\right] \left[ \begin{matrix} \frac{1}{r\tan{\frac{\alpha}{2}}} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan{\frac{\alpha}{2}}} & 0 & 0 \\ 0 & 0 & A & (1) \\ 0 & 0 & B & 0 \\ \end{matrix}\right] =[\frac{x}{r\tan{\frac{\alpha}{2}}}, \frac{y}{\tan{\frac{\alpha}{2}}}, Az+B, z]
\quad\quad\stackrel{divide\quad w}\Rightarrow\quad\quad [\frac{x}{rz\tan{\frac{\alpha}{2}}}, \frac{y}{z\tan{\frac{\alpha}{2}}}, A+\frac{B}{z}, 1]
  由此得到view->ndc z的转换函数:g(z)=A+\frac{B}{z}
  希望转换到NDC时,近平面是0,远平面1,所以:g(f)=A+\frac{B}{f}=1\quad and\quad g(n)=A+\frac{B}{n}= 0
B=\frac{nf}{n-f}\quad A=\frac{-f}{n-f}
  最终得到矩阵:\left[ \begin{matrix} \frac{1}{r\tan{\frac{\alpha}{2}}} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan{\frac{\alpha}{2}}} & 0 & 0 \\ 0 & 0 & \frac{-f}{n-f} & 1 \\ 0 & 0 & \frac{nf}{n-f} & 0 \\ \end{matrix}\right]

Reverse-Z

  差异主要体现在A和B计算时的差异,我们希望NDC近平面是1,远平面为0,因此重写公式:g(f)=A+\frac{B}{f}=0\quad and\quad g(n)=A+\frac{B}{n}= 1
B=\frac{-nf}{n-f}\quad A=\frac{n}{n-f}
  最终得到Reverse-Z矩阵:\left[ \begin{matrix} \frac{1}{r\tan{\frac{\alpha}{2}}} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan{\frac{\alpha}{2}}} & 0 & 0 \\ 0 & 0 & \frac{n}{n-f} & 1 \\ 0 & 0 & \frac{-nf}{n-f} & 0 \\ \end{matrix}\right]

Unity中的DirectX矩阵

  Unity的PC平台API是DX,针对平台差异,和我们推导出的矩阵有一定差异。

  • 渲染到RT时传递的矩阵,要对y取反
  • 要将view矩阵取反的z重新取反,所以Unity渲染时传入的DX矩阵是这个公式:
    \left[ \begin{matrix} \frac{1}{r\tan{\frac{\alpha}{2}}} & 0 & 0 & 0 \\ 0 & -\frac{1}{\tan{\frac{\alpha}{2}}} & 0 & 0 \\ 0 & 0 & \frac{-n}{n-f} & -1 \\ 0 & 0 & \frac{-nf}{n-f} & 0 \\ \end{matrix}\right]
      我们可以得到剪裁空间关于z和w的函数:
    C(z)=\frac{-n(z+f)}{n-f}\quad and \quad C(w)=-z
      并且经过齐次除法后NDC(z)=\frac{C(z)}{C(w)}=\frac{-n(z+f)}{(n-f)(-z)}
      验证下,假如我们场景中,有个点在[0, 0, near]位置的点,因为Unity View矩阵取反特性,变为[0, 0, -near](view空间下),带入此点:C(-n)=n,同理C(-f)=0NDC(-n)=1NDC(-f)=0
      URP下有个函数:
//当Reverse-z(API为DX时)
// { (f-n)/n, 1, (f-n)/(n*f), 1/f }
float4 _ZBufferParams;
float LinearEyeDepth(float depth, float4 zBufferParam)
{
    return 1.0 / (zBufferParam.z * depth + zBufferParam.w);
}

  使用方法是在片元着色器传入SV_Position或深度图采样出来的值,注意片元着色器中的SV_Position是已经经历过透视除法,乃至视口变换的,z值相当于上边的NDC公式。
  我们根据公式写一遍:LinearEyeDepth(NDC(z))=\frac{1}{\frac{-n(z+f)}{(n-f)(-z)}*\frac{f-n}{nf}+\frac{1}{f} }=-z
  这个z是view空间的,因为view矩阵本身对z取反,这个-z操作正好让我们察觉不到view矩阵的取反操作。
  同样还有Linear01Depth方法:

float Linear01Depth(float depth, float4 zBufferParam)
{
    return 1.0 / (zBufferParam.x * depth + zBufferParam.y);
}

  推导公式:Linear01Depth(NDC(z))=\frac{1}{\frac{-n(z+f)}{(n-f)(-z)}*\frac{f-n}{n}+1}=-\frac{z}{f}

Unity移动平台、OpenGL透视矩阵推导

  与PC端DirectX相比,差异主要体现在三方面:

  • 二行二列无需对y轴取反
  • w项乘项为-1
  • ndc范围为[-1, 1],n映射到-1,f映射到1
    注意Opengl中,相机空间是看向z轴负半轴方向,所以严格说是-n映射到-1,-f映射到1
      同样写下基础矩阵:\left[ \begin{matrix} \frac{1}{r\tan{\frac{\alpha}{2}}} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan{\frac{\alpha}{2}}} & 0 & 0 \\ 0 & 0 & A & (-1) \\ 0 & 0 & B & 0 \\ \end{matrix}\right]
    NDC(z)=-A- \frac{B}{z}
    NDC(-n)=-A- \frac{B}{-n}=-1 and NDC(-f)=-A- \frac{B}{-f}=1
    B=\frac{2nf}{n-f}\quad A=\frac{n+f}{n-f}
      得到的最终矩阵为:\left[ \begin{matrix} \frac{1}{r\tan{\frac{\alpha}{2}}} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan{\frac{\alpha}{2}}} & 0 & 0 \\ 0 & 0 & \frac{n+f}{n-f} & (-1) \\ 0 & 0 & \frac{2nf}{n-f} & 0 \\ \end{matrix}\right]
    NDC(z)=\frac{(n+f)z+2nf}{(f-n)z}
    不过因为深度要存储在0-1范围内,因此在片元着色器中得到的SV_Position值,实际上被重映射过:
    FragSVPos(z)=NDC(Z)*0.5+0.5
//当API为OPENGL时
// { (n-f)/n, f/n, (n-f)/(n*f), 1/n }
float4 _ZBufferParams;

LinearEyeDepth(NDC(Z)*0.5+0.5)=\frac{1}{\left(\frac{(n+f)z+2nf}{-(n-f)z}·\frac{1}{2}+\frac{1}{2}\right)·\frac{n-f}{nf}+\frac{1}{ n }}= -z
Linear01Depth(NDC(Z)*0.5+0.5)=\frac{1}{\left(\frac{(n+f)z+2nf}{-(n-f)z}·\frac{1}{2}+\frac{1}{2}\right)·\frac{n-f}{n}+\frac{f}{ n }}= -\frac{z}{f}

Unity中投影矩阵的获取

  调用Camera的API即可获取矩阵:

Camera camera = Camera.main;
Matrix4x4 oglProj = camera.projectionMatrix

  这个矩阵其实是“死”的,是上面我们推导出来的OpenGL(移动平台)矩阵,Unity有一个API能根据当前平台,获取当前API的矩阵:

Matrix4x4 proj = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true);

  第一个参数是从Camera获取的矩阵,第二个参数是:是否渲染到RT。这个RT包括普通颜色缓冲,传入Shader的unity_MatrixVP就是这么算的:

Matrix4x4 unity_MatrixVP = proj * camera.worldToCameraMatrix;

用python构造Unity中的两个矩阵

  测试用:

import glm
import numpy as np

near = n = 0.3
far = f = 1000

fov = glm.radians(60)
aspect = 2

dxproj_reverse_z = np.matrix([[1 / (aspect * np.tan(fov/2)), 0, 0, 0], 
    [0, -1 / np.tan(fov/2), 0, 0],
    [0, 0, n / (f - n), -1],
    [0, 0, n * f / (f - n), 0]])
print(glm.transpose(dxproj_reverse_z))

#print(glm.transpose(glm.perspective(fov, aspect, near, far)))
oglproj = np.matrix([
    [1 / (aspect * np.tan(fov/2)), 0, 0, 0],
    [0, 1 / np.tan(fov/2), 0, 0],
    [0, 0, -(f + n) / (f - n), -1],
    [0, 0, -2*n*f/(f-n), 0],])

print(glm.transpose(oglproj))

相关文章

  • Projection矩阵 Reverse-Z推导

    一般DirectX透视矩阵推导 符号表: 为什么要半高为1?因为我们希望最终计算结果在NDC空间中,范围在xy中都...

  • 计算机图形学常用术语整理(1)

    Projection 【英文缩写】Projection 【中文翻译】投影矩阵 Stencil Test 【中文翻译...

  • 透视投影变换就是三维变换

    透视投影(Perspective Projection)变换推导[https://www.cnblogs.com/...

  • Jensen不等式

    (hessan矩阵是根据泰勒公式推导)

  • OpenGL 投影矩阵

    OpenGL Projection MatrixOpenGL投影矩阵 概述 透视投影 正交投影 概述 计算机显示器...

  • 投影矩阵推导

    1、正交投影 上图就是unity中的摄像机,把摄像机设置为正交投影,我们可以在inspector界面调整clipp...

  • 多元logistic回归矩阵推导

    网上的文章很少多元logistic回归的矩阵推导, 这里写一下我的推导, 欢迎交流.

  • 4.1/5.2:OpenGL MVP矩阵 初使用

    请打开原图食用。 重点看 Model、View、Projection 3个矩阵的 变化时机 和 使用时机。 关于这...

  • 20180316周五~技术收获

    python的字典,集合,列表,能用推导就用推导 list能用切片就用切片 线性代数,向量是对象,矩阵是变换,是坐...

  • BLOSUM矩阵的推导

    BLOSUM62是应用得最广的氨基酸替换矩阵,是BLAST中蛋白质序列比对的默认矩阵。 1. 如何下载替换计分矩阵...

网友评论

      本文标题:Projection矩阵 Reverse-Z推导

      本文链接:https://www.haomeiwen.com/subject/troebktx.html