问题

直到现在,你都是用单向光照亮场景,这对在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; 
}

image