6-9.添加HLSL镜面高光

6-9.添加HLSL镜面高光

问题

你想要用你的自定义HLSL效果添加镜面高光到你的3D场景。镜面高光是光源反射产生的高亮区域,如6-11.

6-9.添加HLSL镜面高光_第1张图片

方案

        下面的讨论将帮助你决定那个像素将有镜面光部分。

    6-11左边显示一个光的传播,L是一条从光源命中三角形像素的向量。同样,眼的向量是从相机朝像素方向。如果L的反射线几乎和E一样,像素应该有镜面光部分。

你可以用镜像方法通过该像素的法线来找到L的反射线。如果它们间的夹角很小这个镜像了的方向几乎和眼的方向一样。你可以通过求它们的点积来检查这两个向量间的夹角。

    如果夹角是0,意味着两个方向一样且你应该添加镜面部分到光照,点积就是1。如果两个方向不同,点积会比1小。

注意 两个向量AB间的点积只不过是(A长度)×(B长度)×(cos(它们间的夹角))。如果AB都单位化,点积就只是(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镜面高光)