-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Open
Labels
Description
有不少读者问我法线转换的问题,例如P128页的代码:
// Transform the normal from object space to world space
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
很多人觉得这句话是个bug,但其实这段代码下面的文字已经详细解释过了:

由于是入门书籍,因此当时在写这一段的时候自认为写的比较详细,加之在第4章单独开辟了一节(4.7节)作为解释,奈何有若干读者跳过了这一段,作为作者表示很伤心T_T……因此开了一个issue特此说明!
希望大家在看书的时候从前往后看,遇到有一些难以理解的不要急于跳过,尤其是第4章,这一章非常关键,法线这里会有问题的读者我相信肯定没有仔细读过第4章^_^。我在写书的时候会在重要的地方用文字强调很多遍,请大家能够仔细阅读下文字,不要急于看代码和敲代码,这样这本书才能给您带来最大的作用~
最后,感谢大家对本书的支持。
vipshenji, hungry0, ShrekJiang, restary, bottomer and 5 more
Metadata
Metadata
Assignees
Labels
Projects
Milestone
Relationships
Development
Select code repository
Activity
[-]【常见问题】关于法线转换的问题[/-][+]【常见问题】关于法线转换的问题(以及在切线空间下计算法线纹理的问题)[/+]candycat1992 commentedon Nov 22, 2016
法线纹理:在切线空间下计算
由于前几天有个读者问了我一个问题,我突然发现P148在切线空间下计算法线光照一节是有问题的。
问题
目前书里面是使用内置宏TANGENT_SPACE_ROTATION来构建一个“从模型空间到切线空间的变换矩阵”:
上面代码的问题在于,如果模型存在非统一缩放,即缩放尺度各分量值不相等,那么上述方法就会造成错误的法线变换问题。
改正
由于光源方向、视角方向大多都是在世界空间下定义的,所以问题就是如何把它们从世界空间变换到切线空间下。我们可以先得到世界空间中切线空间的三个坐标轴的方向表示,然后把它们按列摆放,就可以得到从切线空间到世界空间的变换矩阵,那么再对这个矩阵求逆就可以得到从世界空间到切线空间的变换:
由于Unity不支持Cg的inverse函数,所以还需要自己定义一个inverse函数,这可以参考更新后的Chapter7-NormalMapTangentSpace.shader。
这种做法明显比较麻烦。实际上,在Unity 4.x版本及其之前的版本中,内置的shader一直是原来书上那种不严谨的转换方法,这是因为Unity 5之前,如果我们对一个模型A进行了非统一缩放,Unity内部会重新在内存中创建一个新的模型B,模型B的大小和缩放后的A是一样的,但是它的缩放系数是统一缩放。换句话说,在Unity 5以前,实际上我们在Shader中根本不需要考虑模型的非统一缩放问题,因为在Shader阶段非统一缩放根本就不存在了。但从Unity 5以后,我们就需要考虑非统一缩放的问题了。
结论
所以结论就是,大家最好还是按Chapter7-NormalMapWorldSpace.shader转换到世界空间算更加方便,这也是Unity 5之后所有内置Shader的做法。当然,如果你可以确保没有非统一缩放那么哪种都可以。
hungry0 commentedon Feb 10, 2017
乐乐女神的书不仅内容好,售后服务还做的这么好,这么前卫,O(∩_∩)O哈哈~ 我也是遇到了法线变换过程的迷惑,看了你这篇还有http://www.cnblogs.com/mengdd/archive/2011/08/30/2598025.html 这篇以后,就懂了!
candycat1992 commentedon Feb 11, 2017
@hungry0 感谢支持🙂
nothingcat commentedon Apr 30, 2017
唔,我照着实现148页代码时发现到tangent space的转换有问题,过来发现确实已经有反馈过的了,这本书对于入门很有帮助,第四章写的很赞,非常感谢欸嘿嘿。; )
candycat1992 commentedon May 2, 2017
@IronicGoose 有帮助就好~~~感谢支持 :)
xwc2021 commentedon May 18, 2017
回報1個問題
上面的shader在偶數次鏡射時光照是正確的,但奇數次鏡射時binormal就會差1個方向
偶數次鏡射: scale(-1,-1,1) scale(-1,1,-1) scale(1,-1,-1)
奇數次鏡射: scale(-1,1,1) scale(1,-1,1) scale(1,1,-1) scale(-1,-1,-1)
測試後發現Unity的 surface shader有處理這個情況
Unity的Normal mapping sample在下面的連結裡
https://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html
按[show generate code]出來的結果如下
上面的unity_WorldTransformParams.w應該就是[鏡射因子]
在奇數次鏡射會是(-1),而偶數次鏡射時還是(1)
/////////////////////////////////////////////////////////////////分隔線
另外在Unity裡作了一下實驗
發現即使作了scale.x=-1 Unity[在畫面上顯示的物件x軸]和[transform.right存儲的內容],編輯器裡的3軸顯示都不會反應出鏡射後方向上的變化
但像下面這麼作
Vector3 real_right =transform.lossyScale.x * transform.right;
Vector3 real_up =transform.lossyScale.y * transform.up;
Vector3 real_forward =transform.lossyScale.z * transform.forward;
還是可以得到object真實的軸向
然後檢查
cross(real_right ,real_up)和real_forward 是不是同向,就可以決定[鏡射因子]
但也有可能Unity是直接檢查sign(lossyScale.x * lossyScale.y * lossyScale.z)
GITHUB243884919 commentedon May 28, 2017
hi, candy,遮罩文理Chapter7-MaskTexture也使用了切线空间,用的TANGENT_SPACE_ROTATION。是不是也存在上面说的问题。
candycat1992 commentedon May 31, 2017
@GITHUB243884919 对的,之后如果遇到法线纹理,还是转换到世界空间算更加方便。如果出第二版的话,会把所有的法线纹理转换到世界空间,不会再这么算了。
6 remaining items
xwc2021 commentedon Aug 31, 2017
@laomoi
還有一個可能是你的uv有翻轉過,你是使用自己的模型嗎?
或是你有在Unity裡對模型作負值的縮放嗎?
laomoi commentedon Aug 31, 2017
@xiangwei71 用的是unity自带的Capsule。 我仔细看了书上配图7.15, bumpScale确实需要为负数才能有正确的凹凸感 = =
xwc2021 commentedon Aug 31, 2017
@laomoi
你是不是用新版的unity,我發現我用unity2017使用內建的stander shader時,normal的方向也不正確也
(內建的stander shader 法向量剛好也反過來了阿阿)
mingingzi commentedon Sep 27, 2017
您好,我不太明白tangent和binormal跟uv之间的关系。您在书中说“切线往往与纹理空间对齐”。我自己搜了一下,看到这句话“tangent代表的是顶点u增大的方向,binormal代表v增大的方向,二者未必是互相垂直关系”,一直想不明白uv增大的方向为什么会不垂直。不垂直的话uv坐标轴是不是也不垂直?uv贴图难道不是方形的了吗?
helpking commentedon Jul 20, 2019
还是这个解释移动。书中的公式推到反而让人迷惑更多。
vanxining commentedon Mar 18, 2020
那一篇文章啥推导都没有,啥引用都没有,就干巴巴地说什么是什么的逆矩阵之类的话,我觉得更加难以令人明白。
JunC74 commentedon Jul 23, 2020
切线的tangent.w的值1或-1的意义
Highmiao commentedon Oct 20, 2020
您好,我对更改前的方法有点疑惑,想请问一下,更改前的方法之所以不能处理非统一缩放,是因为计算的模型空间到切线空间的矩阵有问题吗,我不明白这个矩阵哪里有问题。倒是ObjSpaceLightDir(v.vertex)这个方法,如果是非统一缩放那么变换到模型空间的光照方向好像发生了变化?
xwc2021 commentedon Oct 25, 2021
如果妳是想知道「tangent和binormal跟uv之间的关系」:這張圖給妳參考
https://photos.app.goo.gl/1Nj8ojgitocw5iSu7
三角形的頂點是A、C、B、對映到的uv座標是a、b、c
uv座標的2個軸是u-axis、v-axis向量
a= (a.u,a.v) 代表
從uv座標的原點 o=(0,0)出發
沿著u-axis軸移動 a.u 長度、再沿著v-axis軸移動a.v長度,會到達點a
o + u-axis✖️a.u + v-axis✖️a.v = a
b和c也是類似的
o + u-axis✖️b.u + v-axis✖️b.v = b
o + u-axis✖️c.u + v-axis✖️c.v = c
這有什麼用呢?
現在換看頂點ACB
想像ACB會在同1個平面上P,然後再想像P上會有1個原點D
一開始我們不知道D的實際位置
但我們可以模仿uv空間
o + u-axis✖️a.u + v-axis✖️a.v = a
寫出
D + T✖️a.u + BN✖️a.v = A
D + T✖️b.u + BN✖️b.v = B
D + T✖️c.u + BN✖️c.v = C
再來只要解上面3個聯立方程式,就能找出tangent了
https://photos.app.goo.gl/1UVE8M3LQBSwupPm8
上面提到的T.w是什麼
那是個跟mirror有關的東西、除了是uv的mirror(因為artist在建模時、對稱的物件可以只用1半的uv)
也能用在物件的縮放上(比如說物件沿著自己的local x軸縮放-1)
如果你想知道更細節可以參考這裡
https://gpnnotes.blogspot.com/search/label/uv%20%26%20tangent
wyryyds commentedon Jan 28, 2023
这正好解决了我的问题,非常有用的帮助
spicy-ice commentedon Jan 28, 2023
luluxiu666 commentedon Jun 11, 2023
首先,第一个问题,随着物体的缩放(MVP矩阵M矩阵的变化),模型空间内的法向量也会进行同样的缩放,进而导致法向量不与平面垂直,我们如果想让他重新垂直的话可以使用书中变化到世界空间的方法来解决这个问题。原理的话可以参考这个链接:https://zhuanlan.zhihu.com/p/261667233
第二个问题,我感觉是的,原方法相当于直接构建了从模型空间到切线空间的变化,但是因为原模型空间的法线已经是错误的了,所以变化矩阵也是错误的。
spicy-ice commentedon Jun 11, 2023
TheZoomFlash commentedon Jun 11, 2023