技术标签: Unity Shader
UnityObjectToWorldDir用于把模型空间下的矢量转换到世界空间
UnityObjectToWorldNormal用于把模型空间下的法线向量转换到世界空间。因为必须保证法线垂直于模型的表面,所以缩放的时候与普通矢量不一样。
如果法线用UnityObjectToWorldDir,则会出现以下错误
,
而用UnityObjectToWorldNormal,则可得到正确的结果
世界坐标空间中的法线
除了被动态批次合并的对象以外,我们所有的法线都在物体空间之中。但是我们必须知道世界坐标空间中的表面的方向。因此,我们必须将法线从物体空间转换到世界坐标空间。为了做到这一点,我们需要物体的转换矩阵信息。
Unity将一个物体的整个变换层次结构折叠成一个单一的变换矩阵,就像我们在第一部分中所做的那样。我们可以将它写为O = T1T2T3 ...其中T是单独的变换矩阵,而O是组合变换矩阵。这个矩阵被称为物体空间到世界空间的变换矩阵。
Unity通过类型为float4x4的unity_ObjectToWorld变量使这个矩阵在着色器中可用,该变量在UnityShaderVariables中进行定义。将这个矩阵乘以顶点着色器中的法线数据,以便将数据转换到世界坐标空间。因为它是一个方向,重新定位应该被忽略。所以齐次坐标的第四个分量必须为零。
或者,我们可以只对矩阵的3×3的部分做乘法运算。编译出来的代码最终是一样的,因为编译器会去掉所有与常数零相乘的东西。
从物体空间变换到世界坐标空间。
法线现在处于世界坐标空间,但有些发现看起来比别的发现更亮。这是因为他们也进行了缩放。因此,我们必须在转换后对法线进行归一化。
归一化后的法线。
虽然我们再次对向量进行了归一化,但对于没有均匀大小的对象来说,它们看起来很奇怪。这是因为当表面在一个维度上进行拉伸的时候,这个表面的法线不会以相同的方式进行拉伸。
在X轴进行缩放,顶点和法线都变为½。
当大小不均匀的时候时,应该对法线进行取逆操作。这样,当它们被再次被归一化后,法线将匹配变形的曲面的形状。而这对于均匀尺度来说没有影响。
在X轴进行缩放,顶点变为½,而法线加倍。
所以我们必须对大小进行取逆操作,但旋转应该保持不变。那么我们应该怎么做?
我们将对象的变换矩阵描述为O = T1T2T3 ...但我们可以更加具体一些。我们知道层次结构中的每个步骤都结合了缩放、旋转和位移。因此每个T可以分解为SRP。
这意味着 O=S1R1P1S2R2P2S3R3P3…,但是为了方便起见,让我们假设说 O=S1R1P1S2R2P2 。
因为法线是方向向量,所以我们不关心重新定位的问题。所以我们可以进一步简化到O=S1R1S2R2,而且我们只需要考虑3×3的矩阵。
我们想要对缩放取逆,但同时保持旋转不变。所以我们想要一个新的矩阵N = S-11R1S-12R2。
如何对矩阵取逆?
矩阵M的逆写作。 它也是一个矩阵,当它们相乘的时候,将抵消另外一个矩阵带来的操作。互相是对方矩阵的逆。所以
。
要抵消一系列步骤带来的影响,必须以相反的顺序执行相反的步骤。这方面的助记符涉及一些规则。这意味着。
对于单个数x的情况,它的逆更加简单的 ,这是因为
。这也表明零没有逆元。也不是每个矩阵都具有相应的逆矩阵。
我们正在使用缩放、旋转和重新定位矩阵。只要我们不把矩阵缩放为零,所有这些矩阵可以取逆。
位移矩阵的逆矩阵是通过简单地对其第四列中的XYZ分量取负来得到的。
缩放矩阵的逆矩阵是通过对它的对角线上的分量取倒数得到的,我们只需要考虑3×3的矩阵。
旋转矩阵可以每次针对一个轴进行考虑,例如考虑围绕Z轴的情况。 旋转z弧度的操作可以通过简单旋转-z弧度的操作来抵消。当你研究正弦和余弦波的时候,你会注意到sin(-z)= - sinz和cos(-z)= cosz。 这使得旋转矩阵的逆矩阵非常简单。
需要注意的是,旋转矩阵的的逆矩阵在其主对角线上的分量与原始矩阵相同。只有正弦分量的正负发生了变化。
除了物体空间到世界空间的变换矩阵意外,Unity还提供了一个世界空间到物体空间的变换矩阵。这些矩阵实际上是彼此的逆矩阵。所以我们得到这么一个公式。
这给出了我们需要的缩放矩阵的逆矩阵,但也给了我们旋转矩阵和位移矩阵的逆矩阵。幸运的是,我们可以通过转置矩阵来移除那些我们不需要的效果。 然后我们得到。
什么是矩阵的转置?
矩阵M的转置被写为。通过翻转矩阵的主对角线上的变量来对矩阵进行转置。因此它的行会成为转置矩阵的列,它的列会成为转置矩阵的行。需要注意的是,这意味着对角线上的变量本身保持不变。
像逆矩阵一样,对矩阵乘法进行转置会反转其顺序。。 当对不是方阵的矩阵使用的时候,这是有意义的,否则可能会导致无效的乘法。 但是一般来说,这个等式是成立的,你可以查找下它的证明。
当然转置两次会让你得到最初的结果。所以 。
所以,让我们转置世界空间到物体空间的矩阵,并乘以顶点的法线数据。
正确的世界坐标空间的法线。
实际上,UnityCG包含一个方便的UnityObjectToWorldNormal函数,正是做这个工作。所以我们可以使用那个函数。它也使用显式的矩阵乘法,而不是使用矩阵转置。这应该会生成更好的编译代码。
UnityObjectToWorldNormal看起来是什么样子?
这里就是UnityObjectToWorldNormal的代码了。 inline关键字不起任何作用。
重新归一化
在顶点程序中产生正确的法线之后,正确的法线值会通过内插值器。不幸的是,在不同单位长度的向量之间进行线性内插不会生成另外一个单位长度的向量。它会比单位长度的向量要小一些。
所以我们必须在片段着色器中再次对法线进行归一化。
对法线重新进行归一化。
虽然对法线重新进行归一化可以产生更好的结果,但这两者之间的误差通常非常小。如果你更重视性能的话,你可以决定不在片段着色器里面再次进行归一化。这是移动设备中常见的优化。
这是比较夸张的错误。
HugePages是通过使用大页内存来取代传统的4kb内存页面,使得管理虚拟地址数变少,加快了从虚拟地址到物理地址的映射以及通过摒弃内存页面的换入换出以提高内存的整体性能。尤其是对于8GB以上的内存以及较大的Oracle SGA size,建议配值并使用HugePage特性。本文基于x86_64 Linux下来描述如何配值 HugePages。1、Hugepage的引入 操作系统对于...
1 使用BuildConfig Log.e("-------版本名",BuildConfig.VERSION_NAME); Log.e("-------版本号", String.valueOf(BuildConfig.VERSION_CODE));这样就获取到了2 使用 PackageInfo try { PackageIn..._android 读取app版本
#include #include // av_ts2str/av_ts2timestr 给定时间值返回字符串,后面可以指定timebasestatic void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const
简介处理的一维数据类型:.asc微震文件小波层数:5 小波基:sym8阈值公式: ,cD1为第一层分解的细节系数,N为数据长度阈值函数:软硬阈值折中的方法2.代码#模块调用import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport mathimport pywt#封装成函数def sgn(num):if(..._一维数据 阈值选取
有时候我有这样的需求:在一个程序的操作中我需要保存历史记录,或者存储一些信息;但我不希望这个数据在内存中是无限量增加的。简单的说,就是使用数组或者链表但是我不希望其无限量的增长,我只需要保存最近的20或者30条记录就ok,多了我不要。就上面的需求来说,可能我们可以直接通过一定的方法对数组或者链表进行一定的操作(满了就删除第一个)就能实现对应的需求,当然也可以二次封装。_java 实现个队列大致思路
前边写了url的分析我们爬取想要的数据,一般是打开网站链接,用浏览器的elements功能对网页标签进行数据分析,确定想要的数据的位置,再在代码中通过requests库的get和post方式发起请求,得到返回的内容,再用美味汤进行数据查找,最后保存。但这是静态加载的网站的数据分析方式,学习别人代码时会出现AttributeError: 'NoneType' object has no at...
摘自:http://hyb.im/4Mp5y在网上看好多程序员在找Java文件上传的demo,其实用Servlet3.0(JDK1.6)自身带的API就可以了,Servlet3.0新增了Part接口,HttpServletRequest的getPart()方法取得Part实现对象,以下是实现单文件上传的代码: 1、前端页面代码: <!DOCTYPE html PUBLIC "-//W3C...
_textview类结构层次
如果说是JQuery是手工作坊,那么Vue.js就像是一座工厂,虽然Vue.js做的任何事情JQuery都可以做,但无论是代码量还是流程规范性都是前者较优。Vue.js的官方中文教程其实也是一个不错的教程,不过相比于一次性把所有概念掌握,我更倾向于先会用,之后再在实际应用中把未涉及到的知识点逐步补全。就像开车,不是非要知道发动机的工作原理才能上路的,甚至你可能一辈子也不用知道。准备工作首先,..._vue.js教学
{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航,为用户的数据库应用系统进行性能和风险评估,参与配合进行数据压测演练,提供数据库优化方面专业建议,在业务高峰期与用户共同保障数据库系统平..._mysql突然连接不上了
文件占用磁盘空间,基本单位不是字节而是簇。一般情况下,软盘每簇是1个扇区,硬盘每簇的扇区数与硬盘的总容量大小有关,可能是4、8、16、32、64……同一个文件的数据并不一定完整地存放在磁盘的一个连续的区域内,而往往会分成若干段,像一条链子一样存放。这种存储方式称为文件的链式存储。由于硬盘上保存着段与段之间的连接信息(即FAT),操作系统在读取文件时,总是能够准确地找到各段的位置并正...
这个文章主要是保存一份inotifywait+rsync同步文件的例子。#!/bin/shhost=16src=/data/www/project_path/des=/data/www/project_path/user=ubuntu inotifywait -mrq --format '%Xe %w%f' -e modify,create,delete,move $src | wh...