【Unity Shader】从NDC(归一化的设备坐标)坐标转换到世界坐标的数学原理
从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(归一化的设备坐标)坐标转换到世界坐标的数学原理的更多相关文章
- Unity Shader学习笔记-1
本篇文章是对Unity Shader入门精要的学习笔记,插图大部分来自冯乐乐女神的github 如果有什么说的不正确的请批评指正 目录 渲染流水线 流程图 Shader作用 屏幕映射 三角形遍历 两大 ...
- 【Unity Shader】(六) ------ 复杂的光照(上)
笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Sha ...
- 【Unity Shader】(七) ------ 复杂的光照(下)
笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Sha ...
- 《Unity shader入门精要》复习<第13章 关于NDC坐标和深度/法线纹理>
分为三个地方讲解. NDC(Normalize Device Coordinates)归一化的设备坐标 NDC坐标是世界空间坐标通过MVP变换之后再进行归一化得到的坐标.只需要再一步变换就能得到屏幕空 ...
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
摘录自 冯乐乐的<Unity Shader入门精要> 笛卡尔坐标系 1)二维笛卡尔坐标系 在游戏制作中,我们使用的数学绝大部分都是计算位置.距离.角度等变量.而这些计算大部分都是在笛卡尔坐 ...
- Unity Shader入门精要学习笔记 - 第2章 渲染流水线
来源作者:candycat http://blog.csdn.net/candycat1992/article/ 2.1 综述 渲染流水线的最终目的在于生成或者说是渲染一张二维纹理,即我们在电脑屏 ...
- Unity Shader入门精要读书笔记(一)序章
本系列的博文是笔者读<Unity Shader入门精要>的读书笔记,这本书的章节框架是: 第一章:着手准备. 第二章:GPU流水线. 第三章:Shader基本语法. 第四章:Shader数 ...
- Unity Shader入门精要学习笔记 - 第13章 使用深度和法线纹理
线纹理的代码非常简单,但是我们有必要在这之前首先了解它们背后的实现原理. 深度纹理实际上就是一张渲染纹理,只不过它里面存储的像素值不是颜色值而是一个高精度的深度值.由于被存储在一张纹理中,深度纹理里的 ...
- 《Unity Shader入门精要》读书笔记(1)
主要是对第二章的整理 渲染流水线:由一个三维场景出发,生成(渲染)一张二维图像. 渲染流程:应用阶段.几何阶段.光栅化阶段. 应用阶段: 1. 把数据加载到显存中 渲染所需数据从硬盘,到内存,再到显存 ...
随机推荐
- [Spark Core] 在 Spark 集群上运行程序
0. 说明 将 IDEA 下的项目导出为 Jar 包,部署到 Spark 集群上运行. 1. 打包程序 1.0 前提 搭建好 Spark 集群,完成代码的编写. 1.1 修改代码 [添加内容,判断参数 ...
- 【转】Java学习—什么是时间复杂度
[原文]https://www.toutiao.com/i6593144782992704007/ 转载:程序员小灰 时间复杂度的意义 究竟什么是时间复杂度呢?让我们来想象一个场景: 某一天,小灰和大 ...
- Nginx的配置使用
因为做了一个聚合支付的服务应用,对于交易系统来说,并发要求比较高,所以需要使用负载均衡来缓解并发的要求,自行开发又太费时费力,Nginx查了些资料基本满足需求,故对其做了下研究,记录下防止遗忘. 一. ...
- Alpha冲刺报告(3/12)(麻瓜制造者)
今日已完成情况: 肖小强: 解决了之前的部署demo问题,学习了基本需要的api 江郑: 进行发布需求数据库的增删改查 邓弘立: 完成了轮播图的设计 刘双玉: 编码发布商品的接口,数据库表的修改 汪志 ...
- wx.aui.AuiManager部分/布局翻译
wx.aui.AuiManager wx.aui.AuiManager 是AUI框架类中的主要类 wx.aui.AuiManager管理于指定的wx.Frame相关联的窗口,通过使用窗口的wx.aui ...
- python爬虫(二)
python爬虫之urllib 在python2和python3中的差异 在python2中,urllib和urllib2各有各个的功能,虽然urllib2是urllib的升级版,但是urllib2还 ...
- Effective MySQL之SQL语句最优化——读书笔记之二
第二章,基本的分析命令 本章简单介绍了如下几个基本的MySQL分析命令: EXPLAIN命令 生成QEP不是确定的 QEP不会绑定给一个SQL或者存储过程,而是在执行的时候根据实际情况生成 可以通过Q ...
- Kubernetes1.91(K8s)安装部署过程(五)--安装flannel网络插件
node节点需要安装flannel网络插件才能保证所有的pod在一个局域网内通信,直接使用yum安装即可,版本是0.7.1. 1.安装flannel插件: 注意是2个node节点都需要安装,都需要修改 ...
- Linux防火墙工具Firestarter
Firestarter是一个非常好用的防火墙图形化配置工具,作者和开发者是芬兰人. 首先肯定的说Firestarter防火墙是一款非常优秀的基于GUI图形用户界面下的,完全免费的自由软件,它为中小型L ...
- 向jupyter notebook加入Anaconda3中已添加的虚拟环境kernel
# jupyter notebook添加Anaconda虚拟环境的kernel # 开启虚拟环境 (base) C:\Users\jiangshan>activate tensorflow # ...