织梦网站如何做软件下载,餐饮平台app有哪些,wordpress大学主题2.7,wordpress 4.0 谷歌渲染中深度信息很重要#xff0c;但是也很让人迷惑#xff0c;透视投影是什么#xff0c;为什么要做透视除法#xff0c;view空间#xff0c;clip空间#xff0c;ndc空间对应的z值又代表什么#xff0c;这里简单总结下。
一.顶点变换的完整过程 二.View空间下的顶点和Z…渲染中深度信息很重要但是也很让人迷惑透视投影是什么为什么要做透视除法view空间clip空间ndc空间对应的z值又代表什么这里简单总结下。
一.顶点变换的完整过程 二.View空间下的顶点和Z值 输入顶点在经过MV矩阵变换后变化到View空间也就是相机视锥空间上图中的Eye Space在这个空间内的z值代表着顶点到摄像机Z方向的距离也就是相机到幕布的垂直距离下图中的Depth而不是Distance在View空间内是线性的 在该空间内假设有一点(, ) 转成齐次坐标后为(, 1) 三.Clip空间下的顶点和Z值 在这一步我们暂且先放下只假设该空间内的顶点为,,,下看看接下来我们目标NDC空间所需的顶点和Z值长什么样。 四.NDC空间下的顶点和Z值 NDC标准空间下的顶点值我们记为, 1.在渲染管线NDC空间中
范围为-1到1 在在openGL环境下是范围是-1到1DirectX中是0到1。
和和之间是存在联系的 具体推导过程详见这篇文章透视投影矩阵的推导 - bluebean - 博客园
这里单说结论其中即x即y方向同理下图中的z为 已知了他们的关系假设存在一个矩阵变换使得View空间中的顶点 (, 1)可以直接转换到, 1那么该矩阵续满足变量脑补替换下xyz对应 我们发现求解 很难找出合适的m00、m02,因为左边x和z是以加法的形式相邻,右边z确成为了x的分母。
解决方法将右边的以四维列向量表示的坐标每一项乘以z,所以有 所以可以求得矩阵为 再根据其他两个特殊条件在近平面时为-1. 在远平面时为1. 最后求得投影矩阵为 将这样的矩阵乘以视锥体内的一个顶点坐标(, 1)得到一个新的向量,,,再将这个向量的每个分量除以第四个分量()即透视除法,这样就可以得到顶点映射到NDC的坐标, 1这时与不再是线性关系 与的倒数是线性关系 五.再梳理一遍
那么以为的个人理解开始回推一下整个流程。
1.为什么要有透视除法
因为为了求出消除Z分量影响的投影矩阵 2.为什么要用消除Z分量影响的投影矩阵
因为这样整个渲染中一个相机只需要一个投影矩阵否则每个顶点都要传入其带有Z分量影响的投影矩阵。 3.流程再梳理 (, 1)是线性的经过投影矩阵变换 变为,,,
注意矩阵中的第四行三列值为1也即 。大家在很多shader中经常会看到用Clip中w分量去计算一些东西原理其实就是这样其实它存的就是View空间中的值大小。
,,, ,,, 在Clip空间中与还是线性关系。
并且的范围均为-到Opengl,在Clip空间会进行裁剪之后
,,,经过透视除法除以后就得到了NDC空间中的坐标 , 1。这时与不再是线性关系 与的倒数是线性关系
六.Unity中相关的一些问题
1.VertexShader中的顶点输出是啥 Vertex Shader的输出在Clip Space即 ,,, 2.Fragment Shader的输入是在什么空间 不是NDC而是屏幕空间Screen Space。
我们前面说到Vertex Shader的输出在Clip Space接着GPU会自己做透视除法变到NDC。这之后GPU还有一步应用视口变换转换到Screen Space输入给Fragment Shader
(Vertex Shader) Clip Space (透视除法) NDC (视口变换) Screen Space (Fragment Shader) 3.LinearEyeDepthLinear01Depth
NDC空间中的深度值深度贴图中存储的值范围为0到1Opengl需要从-1到1映射到0到1如何能反推得到View空间中的深度值呢具体推倒见该文章Unity Shader-深度相关知识总结与效果实现LinearDepthReverse Z世界坐标重建软粒子高度雾运动模糊扫描线效果_puppet_master的专栏-CSDN博客公式为
视空间 1 / param1 * param2
其中param1 N - F/ NFparam2 1 / N。 Unity自带Shader中关于深度值LinearEyeDepth的处理
// Z buffer to linear depth
inline float LinearEyeDepth( float z )
{return 1.0 / (_ZBufferParams.z * z _ZBufferParams.w);
}// Values used to linearize the Z buffer (http://www.humus.name/temp/Linearize%20depth.txt)
// x 1-far/near
// y far/near
// z x/far
// w y/far
float4 _ZBufferParams;
_ZBufferParams.z _ZBufferParams.x / far 1 - far / near/ far near - far / near * far
_ZBufferParams.w _ZBufferParams.y / far far / near / far 1 / near
我们推导的param1 _ZBufferParams.zparam2 _ZBufferParams.w实际上Unity中LinearEyeDepth就是将透视投影变换的公式反过来用zbuffer图中的屏幕空间depth反推回当前像素点的相机空间深度值。
下面再来看一下Linear01Depth函数所谓01其实也比较好理解我们上面得到的深度值实际上是真正的视空间Z值但是这个值没有一个统一的比较标准所以这个时候依然秉承着映射大法好的理念把这个值转化到01区间即可。由于相机实际上可以看到的最远区间就是F远裁剪面所以这个Z值直接除以F即可得到映射到0,1区间的Z值了
Z视空间01 Z视空间 / F 1 / (N - F/ N * depth F / N
Z视空间01 1 / param1 * depth param2param1 N - F/ N 1 - F/Nparam2 F / N。
再来看一下Unity中关于Linear01Depth的处理
// Z buffer to linear 0..1 depth
inline float Linear01Depth( float z )
{return 1.0 / (_ZBufferParams.x * z _ZBufferParams.y);
}// Values used to linearize the Z buffer (http://www.humus.name/temp/Linearize%20depth.txt)
// x 1-far/near
// y far/near
// z x/far
// w y/far
float4 _ZBufferParams; 可以看出我们推导的param1 _ZBufferParams.xparam2 _ZBufferParams.y。也就是说Unity中Linear01Depth的操作值将屏幕空间的深度值还原为视空间的深度值后再除以远裁剪面的大小将视空间深度映射到0,1区间。
Unity应该是OpenGL风格矩阵NDC等上面的推导上是基于DX风格的DNC进行的不过如果是深度图的话不管怎么样都会映射到01区间的相当于OpenGL风格的深度再进行一步映射就与DX风格的一致了。 4.unity_CameraProjection和UNITY_MATRIX_P (float4x4)
Unity shader 里面要获取投影矩阵有两个变量unity_CameraProjection (float4x4) 和 UNITY_MATRIX_P (float4x4)。需要注意的是这两个矩阵的内容实际上不一样。unity_CameraProjection UNITY_MATRIX_P 他们两个的区别是unity_CameraProjection一直是MainCamera的投影矩阵并且是OpenGL规范的。
UNITY_MATRIX_P是当前渲染投影矩阵不一定是OpenGL并且也不一定是MainCamera的 Whats difference between UNITY_MATRIX_P and unity_CameraProjection? - Unity Forum 5.向量从 camera space 映射到 clip space / screen space
如果想要把一个camera space的向量从 camera space 映射到 clip space / screen space需要采取的操作是用投影矩阵(unity_CameraProjection) 去乘那个向量(向量的齐次坐标 w 分量为0)例如 6.ComputeScreenPos
inline float4 ComputeScreenPos (float4 pos) { float4 o pos * 0.5f; o.xy float2(o.x, o.y*_ProjectionParams.x) o.w; o.zw pos.zw; return o; }
首先该函数传入的参数pos为顶点变换到齐次坐标系下的坐标ClipSpace也就是说在shader中你需要这么使用
o.pos UnityObjectToClipPos(v.vertex);
o.screenPos ComputeScreenPos(o.pos);
ComputeScreenPos返回的值是齐次坐标系下的屏幕坐标值其范围为[0, w]。那么为什么Unity要这么做呢Unity的本意是希望你把该坐标值用作tex2Dproj指令的参数值tex2Dproj会在对纹理采样前除以w分量。当然你也可以像下面代码那样自己除以w分量后进行采样但是效率不如内置指令tex2Dproj
pos UnityObjectToClipPos(v.vertex);
screenPos ComputeScreenPos(o.pos);
tex2D(_ScreenTex, float2(screenPos.xy / screenPos.w))
七.其它相关知识
1.ZBuffer的精度问题
2.Reverse-Z
3.Z1/Z
这些相关知识就查看这篇博客吧Unity Shader-深度相关知识总结与效果实现LinearDepthReverse Z世界坐标重建软粒子高度雾运动模糊扫描线效果_puppet_master的专栏-CSDN博客大佬已经写的非常详细了在此感谢大佬们的无私分享 参考资料
透视投影矩阵的推导 - bluebean - 博客园
Unity Shader-深度相关知识总结与效果实现LinearDepthReverse Z世界坐标重建软粒子高度雾运动模糊扫描线效果_puppet_master的专栏-CSDN博客 写给大家看的“透视除法” —— 齐次坐标和投影 - 简书
实时渲染中的坐标系变换4投影变换-2 - 知乎 掘金
Unity Shader中的ComputeScreenPos函数_linuxheik的专栏-CSDN博客 Whats difference between UNITY_MATRIX_P and unity_CameraProjection? - Unity Forum
八.留个疑问待研究
为什么剪裁是在Clip空间而不是NDC空间
有知道的大佬可以指教下知乎上的一个回答是