cg語言漫反射光照模型中的worldMatrix_IT是什麼意思?是世界變換矩陣的轉置的逆?

struct VertexIn
{
float4 position : POSITION;
float4 normal : NORMAL;
};
struct VertexScreen
{
float4 oPosition : POSITION;
float4 color : COLOR;
};
void main_v(VertexIn posIn,
out VertexScreen posOut,
uniform float4x4 modelViewProj,
uniform float4x4 worldMatrix,
uniform float4x4 worldMatrix_IT,
uniform float3 globalAmbient,
uniform float3 lightPosition,
uniform float3 lightColor,
uniform float3 Kd)
{
posOut.oPosition = mul(modelViewProj, posIn.position);
float3 worldPos = mul(worldMatrix, posIn.position).xyz;
float3 N = mul(worldMatrix_IT, posIn.normal).xyz;
N = normalize(N);
//計算入射光方向
float3 L = lightPosition - worldPos;
L = normalize(L);
//計算方向光漫反射光強
float3 diffuseColor = Kd*lightColor*max(dot(N, L), 0);
//計算環境光漫反射光強
float3 ambientColor = Kd*globalAmbient;
posOut.color.xyz = diffuseColor+ambientColor;
posOut.color.w = 1;
}

這是一個漫反射光照模型的頂點著色器代碼,其中的這一句不是很理解

float3 N = mul(worldMatrix_IT, normal).xyz;

這句代碼應該是要將法線從模型空間變換到世界空間吧,為什麼不直接左乘一個worldMatrix,而是要左乘worldMatrix_IT?

worldMatrix_IT是什麼意思?是世界變換矩陣的轉置的逆?


並不一定需要inverse transpose,僅當變換包含非等比縮放時才需要。後者不這樣做的話,法線便不會垂直於平面。

手機輸入不了公式,後補推導。

--

這問題有很多推導方式,以下是其中一種。

先證切線矢量(tangent vector)可以直接使用變換矩陣來做變換。

mathbf{P}_1, mathbf{P}_2為兩個表面上接近的點,則mathbf{T}=mathbf{P_2} - mathbf{P_1}是一個切線矢量,那麼對於任何變換矩陣mathbf{M},變換後的切線矢量mathbf{T}可以由這兩個點變換後的坐標相減得出:

egin{align}
mathbf{T} = mathbf{M}mathbf{P}_2 - mathbf{M}mathbf{P}_1\
= mathbf{M}(mathbf{P}_2 - mathbf{P}_1)\
= mathbf{M}mathbf{T}
end{align}

由於法線的定義就是垂直於平面,即法矢量mathbf{N}與切線矢量mathbf{T}垂直,那麼:

egin{align}
mathbf{N}cdotmathbf{T} = 0\
mathbf{N}^mathrm{T}mathbf{T} = 0\
mathbf{N}^mathrm{T} mathbf{M}^{-1} mathbf{M}  mathbf{T} = 0\
mathbf{N}^mathrm{T} mathbf{M}^{-1} mathbf{T} = 0
end{align}

因為變換後的法矢量mathbf{N}也需要垂直於變換後的切線矢量mathbf{T},從上式得出:

egin{align}
(mathbf{N}^mathrm{T} mathbf{M}^{-1}) mathbf{T} = 0\
(mathbf{N}^{mathrm{T}}) mathbf{T} = 0\
Leftrightarrow\
mathbf{N}^{mathrm{T}} = mathbf{N}^mathrm{T} mathbf{M}^{-1}\
mathbf{N} = (mathbf{N}^mathrm{T} mathbf{M}^{-1})^{mathrm{T}}\
= (mathbf{M}^{-1})^{mathrm{T}} mathbf{N}
end{align}

證明法矢量需要用變換矩陣的逆的轉置來做變換,才能保證變換後的法矢量垂直於變換後的表面。

另外,對於正交矩陣(Orthogonal matrix)mathbf{Q},其逆矩陣等同於其轉置矩陣,所以:

(mathbf{Q}^{-1})^{mathrm{T}} = (mathbf{Q}^{mathrm{T}})^{mathrm{T}}=mathbf{Q}

由於只含等比縮放、旋轉的變換矩陣是正交矩陣,其逆的轉置即等於自身,所以這情況下不需要使用逆的轉置來變換法線。


這個屬於圖形學書中會提到但是容易被忽略的問題。對於法線是一定要用Inverse Transpose(所以不僅是「逆」)來轉換的。這裡有篇中文博客,有示意圖(文章我沒有細看,自己斟酌):http://blog.csdn.net/christina123y/article/details/5963679


——上圖引用自 "Real Time Rendering,3rd edtion". Page 63.

通常在計算光照時,法向量一般都會被變換到世界坐標系或者眼(坐標系)中。而這個變換過程的採用的矩陣一般是平移、旋轉和縮放矩陣的按某種順序的連乘。平移對向量的方向不產生影響;旋轉正是我們希望對法向量進行的操作;如果當變換中存在縮放,且在各軸的縮放比例相同,也不會對法向量方向產生影響,只是改變了它的長度。但是當各軸上的縮放比例不同時,就會出現如上圖中間所示的情況,方向發生了與「預期」相背離的變化。

為什麼要使用原矩陣的逆的轉置呢?

我們希望避免上述那種與「預期」相背離的變化。這種變化的根源在於縮放的非一致性。那我們的目的就是在對法向量進行縮放時取消這種非一致性。

由於平移對方向無影響,通常我們只對原變換矩陣的左上3X3矩陣進行分析。

設原3X3矩陣為

M = R_{1} * S * R_{2}

其中R_{1}, R_{2} 為純旋轉矩陣(此處增加這兩個純旋轉矩陣只是為了表明變換矩陣的一般性),S為純縮放矩陣。我們希望抵消縮放矩陣對法向量帶來的影響。即我們希望對法向量進行變換的矩陣為

M^{} = R_{1} * S^{-1} * R_{2}

又因為純旋轉矩陣的轉置等於其逆矩陣,即R^{T} = R^{-1} 。純縮放矩陣的轉置等於它本身,即 S^{T} =  S。因此有:

M^{} = ((R_{1})^{-1})^{T} * ((S)^{-1})^{T} * ((R_{2})^{-1})^{T}

= ((R_{2})^{-1} * (S)^{-1} * (R_{1})^{-1})^T

= ((R_{1} * S * R_{2})^{-1})^{T}

= (M^{-1})^{T}

所以我們需要原矩陣的逆的轉置


如@milo yip大神所說,逆轉置是因為法線的變換矩陣不同於切線和頂點,因此需要逆轉置矩陣來對其進行模型矩陣變換,但是對於正交矩陣(不引入非等比縮放的模型變換矩陣),其轉置等於其逆矩陣,於是再求逆就得到了原始的模型變換矩陣,對於世界空間法線到視空間的變換來說,這步是沒必要的,因為這個空間變換沒有引入縮放


已經有高人把原理說得很清楚了。

不過想想去年的這個時候剛剛看完龍書第一版,算是一隻腳邁進了DirectX的大門,直到前2個月決定嘗試下OpenGL。。。DirectX龍書的11版本有解釋(DirectX10看的是中文版,英文原版沒接觸,翻譯版中沒發現相關解釋)。懶得翻書了,記憶里是在光照那一章,龍書里的shader代碼沒有乘以逆矩陣的這個步驟,一直都是直接使用world project view三個matrix,我也想不起來我是怎麼發現這個問題的,貌似是看另一本書,其中有一段簡化版的光線追蹤代碼,我發現裡面的shader代碼在處理是都會讓world乘以題主所說的worldMatrix_IT這個值。而龍書沒乘以這個值,是因為龍書里的例子進行的都是剛性變換,所以無需加工,我算個入門水平吧,如果說錯了還請指出。

修改下答案,畢竟第一次回答還是很緊張的。


上面# @Milo Yip已經推導過了,按照這個推導就OK了。

鑒於自己也模糊過這個東西。提供2點讓自己糾結的地方,方便更容易看懂整個公式

(好想把圖片給刪除掉呀,經樓下兄次奧提醒),補一張圖

上圖1.的解釋是錯誤的,做一個補充,錯誤的經驗也是經驗把


推薦閱讀:

TAG:遊戲開發 | 遊戲引擎 | 影視製作 | 計算機圖形學 |