深圳云购网站制作,同性性做视频网站,嘉陵区建设局网站,免费一键生成详情页法线和法线贴图 1、法线无处不在#xff0c;这是图形学基础中的基础。
2、法线贴图#xff0c;凹凸图#xff0c;位移图等等#xff0c;在图形学历史上有着比较重要的位置#xff0c;在很多图形学的架构中都有应用#xff0c;典型的例如延迟渲染架构。
法线
法线…法线和法线贴图 1、法线无处不在这是图形学基础中的基础。
2、法线贴图凹凸图位移图等等在图形学历史上有着比较重要的位置在很多图形学的架构中都有应用典型的例如延迟渲染架构。
法线
法线英文名normal。首先要理解点法线和面法线。 第一个问题假设一个顶点被N个三角形公用这个顶点的法线怎么算 如图点A的法线应该怎么算才是合理的
如果是取的面法线那么多个面公用一个点取哪个面的才是合理的
第一个方法取任意一个面的法线。但是这种做法效果奇差。
第二个方法计算所有相邻的面的法线然后取平均值。
这种方案一般情况下效果还凑合。但是会有其他一些问题。
1、当一个很大的面和一个很小的面邻面两个面加权求平均合适吗
2、并非所有的应用都是需要这种平滑的光照渲染例如有一些GIS相关的应用说不定不需要平滑的光照而是希望得到面与面的清晰边界的光照。
第三个方法每一个面的法线都要每个顶点的法线就是面法线。那么这种情况下共面的顶点怎么办当然是多顶点不适用共享顶点操作模式。例如一个顶点有N个共面那么这个顶点就不是一个顶点而是N个顶点但是坐标是一样的法线不一样。这种模式会导致共享顶点减少显存占用的做法失效了。但是对现代显卡来说其实顶点数大多数情况下不是很大的开销了尤其是对于PC来说。
第四个方法根据相邻面的权重来计算。权重用面积来算。假设有三个面共顶点三个面的面积分别为A、B、C法线分别为N1、N2、N3.那么先算A的面积占比p1 A / (A B C)法线的占比跟面积的占比一样的。N1 p1 * (N1 N2 N3)。这种做法效果不怎么样.
我估计现在图形学上采用的方案主要是第二跟第三种。我在网上找了两张图看看第二跟第三种的区别一眼可以看到。 图片来源于网络侵删。
这么看来是不是顶点法线才是最合理的面法线不靠谱其实不是的这种完全是看你的需要。看图 图片来源于网络侵删。
如果是这种box你用了顶点法线出来的就是这种不伦不类的效果了。这种情况下你大概率需要的是各个面的面法线。所以总结开来当你需要平滑的时候用求平均这类顶点法线当你需要区分的时候不同面用不同的法线。
法线贴图
为什么需要法线贴图 图片来源于网络侵删。
看看这图。如果需要在3D里面渲染这样一个画面首先是很麻烦其次是顶点太多。做这样一个模型估计几万面都是可能的。一些大场景里满屏的悬崖峭壁都是这样的效果面数太多导致了渲染效率的低下。
那么能不能做一个面然后直接上图当然可以。但是效果较差并且实时渲染光照的时候就更加不理想了。这个时候就需要用法线贴图了。
法线贴图的意思是这里还是渲染一个QUAD两个三角形但是通过贴图来描述像素的法线。渲染每一个像素的时候都用的不同的法线这样实时光照的时候能完美模拟出来光照的效果而且大大降低了计算量。下面我随手用CPU写一点伪代码来模拟这个过程能轻易看出来这个效率的不同。
1、使用模型的渲染方式假设10000个三角形。
Float4 RenderTriangle(TriangleData)
{Vector3 Pos CalcPos(); // 一般是线性插值Vector3 Normal CalcNormal(); // 一般是线性插值Float4 Col RenderPixel();Return Col;
}Void RenderMesh()
{For(int i 0; i 10000; i){RenderTriangle();}
} 这是直接渲染模型的线性插值得到法线值。需要迭代N个三角形效率低。
Float4 RenderTriangle(TriangleData)
{Vector3 Pos CalcPos(); // 一般是线性插值Vector3 Normal SampleNormal(); // 采样法线贴图得到法线值Float4 Col RenderPixel();Return Col;
}Void RenderMesh()
{For(int i 0; i 2; i){RenderTriangle();}
} 这是用法线贴图的。(注意以上仅仅是简单的CPU模拟GPU不是这样的。GPU有大量的渲染线程并且我没记错的话还是SIMD指令复杂得多。但是不管怎么样法线贴图渲染效率的提升都是实打实的。)
那么以上可以看出法线贴图技术仅仅是让三角形渲染的时候多了一个真实的法线值用于做光照计算而不能增加顶点值。因为一般时候顶点值在计算光照的时候都用不到。
那么是不是所有的复杂模型都可以用法线贴图来解决呢当然是不可能的。说穿了法线贴图仅仅是简单的视觉欺骗一旦凹凸太明显的模型使用了法线贴图太靠近的时候就穿帮了。所以适用于法线贴图的场合主要就是凹凸不太明显细节很多需要表现实时光照效果不会太靠近观察的物体。
法线贴图为什么绝大部分都是偏蓝色的这是一个好问题。彻底理解了这个问题那么法线的理解基本上可以说登堂入室脱离了菜鸟行列。
重点1纹理的像素值都是0-1之间没有负数不能大于1
这么干有什么好处呢很简单一般的浮点数就是32位精度有限还有大量的精度用于描述整数部分必然导致了小数部分精度的缺失。全部用于描述小数精度更好。我没有仔细查看过GPU这块用的是哪个浮点数标准我只隐约记得Nvidia的文档里提过一般浮点数是IEEE754标准而纹理的就不知道了但是我相信不会跟一般浮点数一样的毕竟不需要使用大量的资源来描述整数位了。
所以法线值储存在贴图里首先就要normalize转化为-1到1之间。然后再因为不能有负数需要再转换到0-1之间一般有大概这样的函数
Float3 DecodeNormal(float3 n)
{Return(n * 2 - 1.0f);
}Float3 EncodeNormal(float3 n)
{Return(n 1.0f) * 0.5;
} 据说这个函数有人玩出花来的例如什么压缩到16位贴图减少显存占用这个其实比较简单因为normalize之后的法线值其实是x ^2 y^2 z^2 1;那么你保存了x跟y岂不是可以反过来算出z了嘛。但是这种做法虽然降低了显存占用同时也降低了效率啊需要开方一次。其他据说还有一大堆乱七八糟的优化我只是耳闻反正我没有干过。有兴趣的也可以自己试试看。
重点 2模型有本地坐标系世界坐标系。渲染的时候必须变换到世界坐标系才能正确渲染。这个变换一般都很简单就是一行代码
Float4 WorldPos WorldMatrix * LocalPos;
那么问题来了法线怎么弄呢当你没有用到法线贴图的时候其实也是一样的
Float4 WorldNormal WorldMatrix * LocalNormal;
那么你使用了法线贴图呢
我们需要这么干
Float4 Normal tex2D(NormalTexture, UV);
Float4 RealNormal DecodeNormal(Normal); // 0,1转换到-11
Float4 WorldNormal WorldMatrix * RealNormal;
Float4 Col CalcLighting(WorldNormal, Light);// 法线和光照计算颜色。
上面代码有什么问题吗其实如果就一般的程序来说一点问题都没有。甚至更糟糕的垃圾代码都没有问题。我见过无数比这糟糕得到的代码照样跑得666.
图形学为什么相对比较难因为图形学对性能有极致的需求。以上代码对性能上有一定的损耗。主要表现在哪里
首先这里的UV是需要三角形插值得到的这就导致了这部分代码必须只能运行在PS像素着色器上。也就是说每个像素都需要执行一遍。
其实这也不是什么大问题。但是有更好的优化方式啊。我可以把Light的坐标转换到法线贴图的本地坐标系然后进行光照结果是一样的啊只要在同一个坐标系即可。而Light的坐标转换只需要在VS里面算一次即可不需要在PS里面反复算。
所以你们看到法线贴图相关的shader大概率都是这样的
Void VS()
{
float3 binormal cross(tangent.xyz, normal); // tangent是切线需要外部传入
float3x3 rotation float3x3(tangent.xyz, binormal, normal);
oTSLightDir mul(rotation, lightDir); // lightDir是光照方向
}
Float4 PS()
{
float3 lightVec normalize(TSlightDir).xyz;
float3 Normal DecodeNormal(tex2D(normalMap, uv).xyz);
Return CalcLighting(Normal, lightVec );
}
看到了吗这个算法比上面的算法效率上是要更高的。高多少天知道跟很多因素有关。法线贴图少的时候这个提升其实可以忽略不计但是肯定是提升。
以上这个坐标系叫做切线坐标系。首先任意一个三角形先计算一个Normal然后再计算一个切线。根据法线跟切线的两两垂直关系叉乘crossProduct得到副法线构建坐标系。三角形的法线好计算已知三个顶点根据面的方向两两叉乘可以得到法线这点代码到处都能找到。那么切线是怎么算的呢我没记错的话我记得是用偏微分方程以U坐标方向为切线方向来算的那么V方向就是副法线方向这部分不保证绝对正确懒得去查资料了大概理解一下原理即可想知道的自己去查一下。
除了效率原因还有另外一个原因据说是形变。假设是模型使用了形变如果法线贴图储存的是本地坐标系这个世界变换并不能体现这个形变而且法线贴图的计算一般都是再MAX玛雅之类的软件里引擎一般不提供修改法线贴图就很麻烦了。而使用了切线坐标系是可以实现形变的。形变之后重新计算Normal跟Tangent即可。但是这其实也是挺麻烦的事一般来说使用到法线贴图的模型都是一些大平面的细节模型形变这个因素我没碰到过。
回到主题为什么法线贴图是偏蓝色很明显了在切线坐标系里定义顺序是Tangent、Binormal、Normal也就是说Normal处于z这个方向。而对于一个三角形而言绝大多数时候法线值都是垂直于这个面的。显而易见法线贴图的法线值大多数时候是接近于001的当然是接近于蓝色了。
当然了这不是绝对的跟引擎有关也跟自己的处理有关。例如压缩到16位的法线贴图例如只保存xy的不就是偏黑色了嘛没搞过按道理是这样的