问题
直到现在,你都是用单向光照亮场景,这对在3D世界中添加阳光是很有用的。但很多情况中,你还需要一个从点发出的光线,例如一个探照灯或爆炸。这种光源叫做点光源。
解决方案
将点光源的3D位置从XNA项目传送到XNA effect中。对每个顶点,计算光源指向顶点的方向,并将这个方向作为光线方向。知道了光线方向就可以像以前一样继续了。
工作原理
在. fx文件中,使用下列代码替换xLightDirection参数,让你可以将光源的3D位置从XNA项目传递到HLSL effect中:
float3 xLightPosition;
然后,对每个顶点,你要计算从光源指向顶点的方向。从A指向B的方向可以通过B减去A获得。记住你需要顶点的最终3D位置,这意味着初始3D位置需要通过是世界矩阵进行变换:
float3 final3DPosition = mul(inPos, xWorld); float3 lightDirection = final3DPosition - xLightPosition; lightDirection = normalize(lightDirection); Output.LightFactor = dot(rotNormal, -lightDirection);
因为光源指向顶点的方向可能比1大得多,所以要确保将这个方向归一化。有了光线方向,你可以像以前的教程那样继续处理了。
当运行代码时,对每个顶点都要计算光源到这个顶点的方向,所以这个方向往往是不同的。
距离衰减
要让效果变得更加真实,你想在光源与顶点的距离变大时让点光源的光照变弱。要做到这点,可以在归一化lightDirection之前对它使用length函数获得distance,然后将LightFactor除以这个distance:
float3 final3DPosition = mul(inPos, xWorld); float3 lightDirection = final3DPosition - xLightPosition; float distance = length(lightDirection); lightDirection = normalize(lightDirection); Output.LightFactor = dot(rotNormal, -lightDirection); Output.LightFactor /= distance;
代码
在XNA项目中,你可以在任何你想的位置放置点光源:
effect.CurrentTechnique = effect.Techniques["VertexShading"]; effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); effect.Parameters["xLightPosition"].SetValue(new Vector3(1, 5, 0)); effect.Parameters["xAmbient"].SetValue(0.0f);
下面是完整的vertex shader:
VSVertexToPixel VSVertexShader(float4 inPos: POSITION0, float3 inNormal: NORMAL0) { VSVertexToPixel Output = (VSVertexToPixel)0; float4x4 preViewProjection = mul(xView, xProjection); float4x4 preWorldViewProjection = mul(xWorld, preViewProjection); Output.Position = mul(inPos, preWorldViewProjection); float3 normal = normalize(inNormal); float3x3 rotMatrix = (float3x3)xWorld; float3 rotNormal = mul(normal, rotMatrix); float3 final3DPosition = mul(inPos, xWorld); float3 lightDirection = final3DPosition - xLightPosition; lightDirection = normalize(lightDirection); Output.LightFactor = dot(rotNormal, -lightDirection); return Output; }