unity-shader-切线空间
法线贴图 与 切线空间
前篇
- 为什么要有切线空间(Tangent Space),它的作用是什么 - https://www.zhihu.com/question/23706933
- 切线空间(Tangent Space) 的计算与应用
- http://docs.cryengine.com/display/SDKDOC4/Tangent+Space+Normal+Mapping
- Tutorial 13 : Normal Mapping - http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping
- 凹凸映射 Bump mapping(unityshader入门精要学习笔记) - https://blog.csdn.net/u013354943/article/details/52779991
- Unity切线空间问题和推理思考 - https://www.jianshu.com/p/af800402f5db
切线空间
使用 法线贴图 是因为在 低模 下想获得 高模 凹凸表面光照效果。(也就是面数不够,法线来凑,基于面的法线建立一个 虚拟坐标系 ,通过 法线贴图 在面上 加多点法线 )
由于需要将同一份法线纹理贴到不同模型的表面,或者同一个模型中不同角度的表面,那么当初生成这份法线纹理时所使用的面元角度(所谓模型local坐标系),就无法适用在其他角度的面元上。所以,人们不记录当时用模型坐标系生成的法线,而使用z轴与该点所在面元的法线平行的一个虚拟的相对坐标系来记录该点法线在该坐标系中的x/y/z的值,相当于记录了该点法线与所在平面法线之间的相对关系,而不是记录绝对坐标。由于点的法向量基本不会偏移面法向量太多,也就是凹凸程度一般不会太夸张,所以最终的法向量值中,z的值总是比x和y的大一些(也就是说 点法向量 与平面的 的夹角一般会大于45度),用(法向量值+1)/2转换为RGB后,就成了蓝色为主的色调了。
经过这样记录下来的法向量,贴到哪个模型表面,就按照当前表面的法向量,计算出该点法向量在世界坐标空间中的值,然后计算光照就可以了。
但是有个问题,看下图,竖轴同样是面法向量,但是有多种不同的x和y轴组合,每种组合生成的点法向量是不一致的,所以需要规定一套固定的x和y轴,大家遵守同样的规则。怎么规定呢?就用纹理的uv坐标来定。具体做法是,取该点所在的三角形的三个顶点P1.P2.P3的纹理U和V坐标,然后x轴的方向就是P3指向P1,又称T轴;y轴方向是P3指向P2,又称B轴。 (完)
参考资料
- https://www.zhihu.com/question/23706933
- 切线空间(Tangent Space) 的计算与应用
- http://docs.cryengine.com/display/SDKDOC4/Tangent+Space+Normal+Mapping
- http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/
- 凹凸映射 Bump mapping : https://blog.csdn.net/u013354943/article/details/52779991
- 法线贴图(凹凸贴图): https://nuysoft.gitbooks.io/unity-manual/content/Manual/StandardShaderMaterialParameterNormalMap.html
顶点 法线,切线,副切线 图解
测试代码1
1 | // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' |
切线空间 下的计算
1 | Shader "LT/TangentNormal" |
世界空间 下的计算
1 | Shader "LT/WorldNormal" |
副切线计算
1 | fixed3 worldBinormal = cross(worldNormal,worldTangent) * v.tangent.w; |
要乘以 v.tangent.w 是为了确定副切线的正确方向, 因为 叉乘 cross 的几何意义是得到 垂直于 两个向量所在平面的 向量, 这个切线的方向与uv展开的方向相关, 这个可能与建模软件相关.
相关讨论可以参考: https://github.com/candycat1992/Unity_Shaders_Book/issues/32
这里也有关于 乘以 v.tangent.w 的讨论:Unity切线空间问题和推理思考 - https://www.jianshu.com/p/af800402f5db
经过一段时间的搜索找到如下解释,有勘误还请指正
首先我们切换回到纹理的定义
对于纹理UV来说,
OpenGL 从左到右(0,1),从下到上(0,1)
DirectX 从左到右(0,1),从下到上(1,0)
可见对于切线 ,即UV的U,对于OpenGL和DirectX都是左到右(左侧为0.0,右侧为1.0)。
而对于binormal,即UV的V,这在OpenGL和DirectX中是不同的。
OpenGL是自下而上的,DirectX是自上而下的。
这也是许多引擎和3D工具对“+ Y / -Y”法线贴图的偏好来自不同的地方。
Unity是+ Y,这是OpenGL标准,
Unreal是-Y,这是DirectX标准。
显然,Unity在Windows上使用DirectX,所以大部分时间W组件都是负值的。
然而,如果纹理UV被反转,不是因为网格反转,而是因为网格的纹理被镜像。
所以,如果纹理被镜像,则需要w来存储这个值