从NDC(归一化的设备坐标)坐标转换到世界坐标要点

参考资料

How to go from device coordinates back to worldspace http://feepingcreature.github.io/math.html

《Unity Shader入门精要》

前情提要,从运动模糊说起

产生运动模糊效果,一般来说有两种做法,第一种是将当前帧和下一帧或上一帧等等图像混合起来作为当前的屏幕图像,这样做法可以导致物体运动时会出现多个残影(因为多个帧混合起来了),可以产生运动模糊效果,但效率不高。

第二种做法是,在Shader里面利用View-Projection矩阵及其逆矩阵获得当前帧和上一帧的世界坐标,通过这两个坐标得到当前像素的运动速度及其方向,再根据这个速度,向这个像素速度方向的N个纹素进行取样并混合,从而产生模糊效果。

而这第二种做法,就是我遇到问题的地方。

总所周知,在Shader里面,可以根据深度图和当前uv坐标得到当前像素的NDC坐标,那么只要采用View-Projection(视图-裁剪)的逆矩阵就可以将NDC坐标变换到世界坐标下(正是我们所需要的值),按理说,这样应该就可以了,但是我在《Unity Shader入门精要》中看到的计算方法却是下面这样。

1   fixed4 frag(v2f i) : SV_Target{
2 // 从深度图中获得深度
3 float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);
4 // 根据深度和当前uv坐标反推当前NDC坐标(注意这个坐标已经经过了齐次除法了)
5 float4 NDCPos = float4(i.uv.x*2-1, i.uv.y*2-1, d*2-1,1);
6 // 根据NDC坐标及View-Projection的逆矩阵,将NDC坐标变换到世界坐标下
7 float4 worldPos = mul(_CurrentViewProjectionInverseMatrix,NDCPos);
8
9 worldPos /= worldPos.w;
....
....
....
}

其中第9行是我最疑惑的内容,按说,你进行屏幕映射要除个w分量进行齐次除法我可以理解,但是你从NDC坐标变换到世界坐标还要除于w是个什么理喔?

百思不得其解之下,翻了下作者的GitHub,嗯,终于找到了答案(感谢作者及评论区的小伙伴!!!)。

http://feepingcreature.github.io/math.html 中有详细的数学证明,下面我给翻译一下~~

数学推导过程

已知条件

首先考虑从世界坐标是如何变换到NDC坐标下的。

很显然,首先世界坐标是通过VP矩阵(不用MVP,因为这里已经是世界坐标了,不用在从模型空间变换到世界空剑侠了)变换到了裁剪空间下,数学公式如下:

在裁剪空间下,通过进行齐次除法将坐标变换到NDC坐标中,公式如下:

最后,第三个已知条件是,世界坐标下的w分量在Unity中定义通常都是1,那么就是下面这样:

分析问题

那么,我们的问题就变成了下面这样:

已知:

三个公式,给定一个NDC坐标和一个View-Projection矩阵,求得该NDC坐标所对应的世界坐标.

推导

第一步,反转公式③,如下所示:

第二步,根据公式2可得:

同时,又因为clip=(clipxyz,clipw),所以可以根据公式⑤,可得下面的公式:

根据公式④和⑥,可得下面的公式:

下面有个解释不是很懂~,原文如下:

And note that since matrices are linear transforms, we can pull that clip_w in front of the matrix multiply:

大致意思是说,因为矩阵是线性变化的,所以可以把clipw放到矩阵的前面.(老实说clipw不是标量么,放哪里应该都可以把?)

总之,经过上面原文的解释,公式⑦变成下面这样.

clip的w分量怎么得?

那么,一个比较严重的问题就出现了,clip坐标的w分量我们并不知道欸~~~我们已知的只有NDC坐标下的(X,Y,Z)分量(分别可以通过当前像素的uv值和深度算出来).

别急,已知条件不是还有一个没用么,就下面这个已知条件.

已知世界坐标的w分量恒为1,根据公式①、⑦,在只关注运算过程和结果的w分量的情况下,有下面这个公式:

那么,clip的w分量就经过公式⑧算出来了。

最终结果

那么,已知clipw,根据公式⑧和公式⑦,得到下面的公式⑨。

总结

根据公式⑨,就可以知道为什么最后我们还要除于w分量了~~~~~因为下面的分母就是w分量啊。。

【Unity Shader】从NDC(归一化的设备坐标)坐标转换到世界坐标的数学原理的更多相关文章

  1. Unity Shader学习笔记-1

    本篇文章是对Unity Shader入门精要的学习笔记,插图大部分来自冯乐乐女神的github 如果有什么说的不正确的请批评指正 目录 渲染流水线 流程图 Shader作用 屏幕映射 三角形遍历 两大 ...

  2. 【Unity Shader】(六) ------ 复杂的光照(上)

    笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题.              [Unity Sha ...

  3. 【Unity Shader】(七) ------ 复杂的光照(下)

    笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题.              [Unity Sha ...

  4. 《Unity shader入门精要》复习<第13章 关于NDC坐标和深度/法线纹理>

    分为三个地方讲解. NDC(Normalize Device Coordinates)归一化的设备坐标 NDC坐标是世界空间坐标通过MVP变换之后再进行归一化得到的坐标.只需要再一步变换就能得到屏幕空 ...

  5. Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础

    摘录自 冯乐乐的<Unity Shader入门精要> 笛卡尔坐标系 1)二维笛卡尔坐标系 在游戏制作中,我们使用的数学绝大部分都是计算位置.距离.角度等变量.而这些计算大部分都是在笛卡尔坐 ...

  6. Unity Shader入门精要学习笔记 - 第2章 渲染流水线

    来源作者:candycat   http://blog.csdn.net/candycat1992/article/ 2.1 综述 渲染流水线的最终目的在于生成或者说是渲染一张二维纹理,即我们在电脑屏 ...

  7. Unity Shader入门精要读书笔记(一)序章

    本系列的博文是笔者读<Unity Shader入门精要>的读书笔记,这本书的章节框架是: 第一章:着手准备. 第二章:GPU流水线. 第三章:Shader基本语法. 第四章:Shader数 ...

  8. Unity Shader入门精要学习笔记 - 第13章 使用深度和法线纹理

    线纹理的代码非常简单,但是我们有必要在这之前首先了解它们背后的实现原理. 深度纹理实际上就是一张渲染纹理,只不过它里面存储的像素值不是颜色值而是一个高精度的深度值.由于被存储在一张纹理中,深度纹理里的 ...

  9. 《Unity Shader入门精要》读书笔记(1)

    主要是对第二章的整理 渲染流水线:由一个三维场景出发,生成(渲染)一张二维图像. 渲染流程:应用阶段.几何阶段.光栅化阶段. 应用阶段: 1. 把数据加载到显存中 渲染所需数据从硬盘,到内存,再到显存 ...

随机推荐

  1. 使用Doxygen生成C#帮助文档

    一. 什么是Doxygen? Doxygen 是一个程序的文件产生工具,可将程序中的特定批注转换成为说明文件.通常我们在写程序时,或多或少都会写上批注,但是对于其它人而言,要直接探索程序里的批注,与打 ...

  2. X-Pack权限控制之给Kibana加上登录控制以及index_not_found_exception问题解决

    无法查看索引下的日志问题解决 好事多磨,我们还是无法在Kibana下看到数据,究竟是怎么一回事呢? 笔者再次查看了logstash的控制台,又发现了如下错误: logstash outputs ela ...

  3. 为什么ConcurrentHashMap的读操作不需要加锁?

    我们知道,ConcurrentHashmap(1.8)这个并发集合框架是线程安全的,当你看到源码的get操作时,会发现get操作全程是没有加任何锁的,这也是这篇博文讨论的问题--为什么它不需要加锁呢? ...

  4. Ajax的用法

    1 Ajax是什么 1.1 Asynchronous JavaScript and XML(异步的javascript和xml) 实质为:使用浏览器内置的一个对象(XmlHttpRequest)向服务 ...

  5. POJ 1094 Sorting It All Out(拓扑排序+判环+拓扑路径唯一性确定)

    Sorting It All Out Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 39602   Accepted: 13 ...

  6. day05今日学习总结:字符串类型

    昨日学习复习: 数据类型: 有序.无序 有序:可以根据索引查找的数据 可变不可变 可变:在值变的情况下,id不变,证明原值是在改变的 不可变:在值变的情况下,id也跟着变,证明不是在改原值. 今日学习 ...

  7. “System.Reflection.AmbiguousMatchException”类型的异常在 mscorlib.dll 中发生

    错误提示: “System.Reflection.AmbiguousMatchException”类型的异常在 mscorlib.dll 中发生,但未在用户代码中进行处理. 发现不明确的匹配. 问题原 ...

  8. vi 替换

    在vi编辑器中,能够利用 :s命令能够实现字符串的替换.详细的使用方法例如以下: 1.:s/str1/str2/ 用字符串 str2 替换行中首次出现的字符串str1: 2.:s/str1/str2/ ...

  9. python_分布式进程中遇到的问题

    看文档学习分布式进程中遇到了一下问题,文档里面例题是python2.X,我用的python3.x,就出现了一下莫名奇妙的问题,最终版代码先呈上: taskManager.py # coding:utf ...

  10. 2019年北航OO第2单元(电梯模拟)总结

    1 三次作业的设计策略 经过了上一单元的训练,我也积累了一些设计策略上的经验.在这一单元的一开始,我便尽可能地把问题中的各个功能实体区分开来,分别封装成类,以便于随后作业中新需求的加入.与此同时,我也 ...