6-9.添加HLSL镜面高光
6-9.添加HLSL鏡面高光
問題
你想要用你的自定義HLSL效果添加鏡面高光到你的3D場景。鏡面高光是光源反射產生的高亮區域,如6-11.
方案
??????? 下面的討論將幫助你決定那個像素將有鏡面光部分。
??? 6-11左邊顯示一個光的傳播,L是一條從光源命中三角形像素的向量。同樣,眼的向量是從相機朝像素方向。如果L的反射線幾乎和E一樣,像素應該有鏡面光部分。
你可以用鏡像方法通過該像素的法線來找到L的反射線。如果它們間的夾角很小這個鏡像了的方向幾乎和眼的方向一樣。你可以通過求它們的點積來檢查這兩個向量間的夾角。
??? 如果夾角是0,意味著兩個方向一樣且你應該添加鏡面部分到光照,點積就是1。如果兩個方向不同,點積會比1小。
注意 兩個向量A和B間的點積只不過是(A長度)×(B長度)×(cos(它們間的夾角))。如果A和B都單位化,點積就只是(cos(它們間的夾角))。如果AB間夾角是0,點積就是1.如果兩個向量互相垂直,夾角是90度,點積就是0。就像6-11右上方顯示的。如果兩個向量相反,夾角180度,點積是-1.
當所有光向量和眼向量的夾角少于90度時這個點積將被轉成正的,如6-11的右上。你不能馬上把這個值用作鏡面高光,因為這將添加一個鏡面光部分到所有不超過眼向量90度的反射向量。你想要縮小反射向量在10度或以下。
你可以通過給點積的結果一個高次方來得出。求這個點積的12次方,例如,將只為偏移10度之內的向量產生一個大于0的值,如6-11右下部顯示。
這將使每個像素結果在單精度值,表明在該像素的鏡面部分的強度。
運作
一直以來,你將想要能夠設置世界,視圖和投影矩陣來變換你的3D位置到2D屏幕坐標。因為這節是為點光源而寫,你需要能夠修改它的位置。要計算眼向量,你需要知道相機的位置。你應該能設置鏡面次方數來控制鏡面高光的大小。因為光照的總量可能大于1,你應該能夠縮小光強度來避免過度飽和。
注意 在許多情況,你將想要縮小光源的強度。要擁有多個光時要這么做,否則引起大多數像素融進光里,浪費了照明效果。
float4x4 xWorld;
float4x4 xView;
float4x4 xProjection;
float3 xLightPosition;
float3 xCameraPos;
float xAmbient;
float xSpecularPower;
float xLightStrength;
?
struct SLVertexToPixel
{
???? float4 Position????????? : POSITION;
???? float3 Normal?????????? : TEXCOORD0;
???? float3 LightDirection : TEXCOORD1;
???? float3 EyeDirection?????? : TEXCOORD2;
};
struct SLPixelToFrame
{
???? float4 Color ??????????? : COLOR0;
};
這個頂點著色器也將計算EyeDirection并使它對所有像素用插值替換。像素著色器仍然只需要輸出每個像素的顏色。
頂點著色器
頂點著色器和上一節沒啥區別。唯一新增的是計算了眼向量。通過從目標減去原點得出從一個點到另一個點的向量。
SLVertexToPixel SLVertexShader(float4 inPos: POSITION0, float3 inNormal: NORMAL0)
{
??? SLVertexToPixel Output = (SLVertexToPixel)0;
??? float4x4 preViewProjection = mul(xView, xProjection);
??? float4x4 preWorldViewProjection = mul(xWorld, preViewProjection);
??? Output.Position = mul(inPos, preWorldViewProjection);?
??? float3 final3DPos = mul(inPos, xWorld);
??? Output.LightDirection = final3DPos - xLightPosition;
??? Output.EyeDirection = final3DPos - xCameraPos;
??? float3x3 rotMatrix = (float3x3)xWorld;
??? float3 rotNormal = mul(inNormal, rotMatrix);
??? Output.Normal = rotNormal;
??? return Output;
}
像素著色器
像素著色器更有趣。基色固定是藍色,因此你不必浪費心思在這。一個很好的做法是,你單位化每個你在像素著色器接收到的方向,因為它的長度可能不是1.
和以前一樣,你計算通用光照。把它和xLightStrength相乘來縮小它一點。
SLPixelToFrame SLPixelShader(SLVertexToPixel PSIn) : COLOR0
{
??? SLPixelToFrame Output = (SLPixelToFrame)0;
??? float4 baseColor = float4(0,0,1,1);?
??? float3 normal = normalize(PSIn.Normal);?
??? float3 lightDirection = normalize(PSIn.LightDirection);???
??? float shading = dot(normal, -lightDirection);
??? shading *= xLightStrength;
??? float3 reflection = -reflect(lightDirection, normal);
??? float3 eyeDirection = normalize(PSIn.EyeDirection);
??? float specular = dot(reflection, eyeDirection);
??? specular = pow(specular, xSpecularPower);?
??? specular *= xLightStrength;
??? Output.Color = baseColor*(shading+xAmbient)+specular;???????????
??? return Output;
}
接著,你用反射原理通過法線鏡像化光方向。因為光方向照向像素,它的反射線將向著眼。這和眼向量相反,所以你反轉它。
鏡面值通過眼向量和反轉的反射光方向間的點積得出。使這個值有高的次方確保只為當那些兩個向量差異小于10度左右的像素該值才顯著的大于0。再有,這個值是和xLightStrength值相乘。
最后,環境光,著色值,鏡面高光部分組合得出最終像素顏色。
注意 鏡面部分添加白色到最終色。如果你的光有不同顏色,你應該用你的光的顏色乘以鏡面值。
轉載于:https://www.cnblogs.com/XNAconglele/archive/2009/09/26/1574319.html
總結
以上是生活随笔為你收集整理的6-9.添加HLSL镜面高光的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【HelloKitty团队项目】Alph
- 下一篇: 运行项目遇到:该网页无法正常运作,loc