原文地址:http://digierr.spaces.live.com/blog/cns!2B7007E9EC2AE37B!636.entry。

本教程基于教程17中实现的点光源+自阴影,要实现多个光源,原理上并没有什么新东西。
多个点光源
要实现多个点光源,我们需要在shader中使用n次光照方程。因此,如果有三个光源,我们需要为三个光源的漫反射、镜面高光、自阴影和衰减,下面是光照方程:

如你所见,除了需要对阴影、漫反射、镜面高光和衰减求和,上面的光照方程与教程17是相同的。
例如有三个光源,一个紫色、一个绿色、一个蓝色位于物体的周围,则结果如下图所示:

实现shader
我们需要计算光照方程n次。本例中,我实现了3个光源。这意味着我需要发送3个位置、光照范围和颜色到shader中:
然后,在顶点着色器中,我想需要计算顶点到光源的距离,将光源位置转换到切线空间中(法线映射)并计算每个光源的衰减:
02 | float3 L = vecLightPos - PosWorld; |
03 | float3 L2 = vecLightPos2 - PosWorld; |
04 | float3 L3 = vecLightPos3 - PosWorld; |
07 | Out.Light.xyz = normalize(mul(worldToTangentSpace, L)); |
08 | Out.Light2.xyz = normalize(mul(worldToTangentSpace, L2)); |
09 | Out.Light3.xyz = normalize(mul(worldToTangentSpace, L3)); |
12 | Out.Light.w = saturate( 1 - dot(L / LightRange, L / LightRange)); |
13 | Out.Light2.w = saturate( 1 - dot(L2 / LightRange2, L2 / LightRange2)); |
14 | Out.Light3.w = saturate( 1 - dot(L3 / LightRange3, L3 / LightRange3)); |
其实没什么新东西,只不过将教程17中的步骤做了3次而已。
下面看一下像素着色器。首先需要计算每个光源的方向:
2 | float3 LightDir = normalize(L.xyz); |
3 | float3 LightDir2 = normalize(L2.xyz); |
4 | float3 LightDir3 = normalize(L3.xyz); |
然后使用上面的光照公式(1)计算漫反射、镜面高光和自阴影:
02 | float D = saturate(dot(N, LightDir)); |
03 | float D2 = saturate(dot(N, LightDir2)); |
04 | float D3 = saturate(dot(N, LightDir3)); |
07 | float Shadow = saturate(4.0 * LightDir.z); |
08 | float Shadow2 = saturate(4.0 * LightDir2.z); |
09 | float Shadow3 = saturate(4.0 * LightDir3.z); |
12 | float3 R = normalize(2 * D * N - LightDir); |
13 | float3 R2 = normalize(2 * D2 * N - LightDir2); |
14 | float3 R3 = normalize(2 * D3 * N - LightDir3); |
17 | float S = min(pow(saturate(dot(R, ViewDir)), 3), Color.w); |
18 | float S2 = min(pow(saturate(dot(R2, ViewDir)), 3), Color.w); |
19 | float S3 = min(pow(saturate(dot(R3, ViewDir)), 3), Color.w); |
现在我们已经从光照方程中计算得出需要的东西,下面使用公式(2)实现最终的颜色:
3 | float4 light1final = Shadow*((Color * D * LightColor + S*LightColor) * (L.w)); |
4 | float4 light2final = Shadow2*((Color * D2 * LightColor2 + S2*LightColor2) * (L2.w)); |
5 | float4 light3final = Shadow3*((Color * D3 * LightColor3 + S3*LightColor3) * (L3.w)); |
6 | return 0.1 * Color + light1final + light2final + light3final; |
好了,就是这些!简单地说,要获得n个光照的效果,你需要计算光照方程n次。
使用shader
与教程17相比并没有什么新东西。但是这次我们需要传递3个光源的位置、颜色和光照范围:
02 | effect.Parameters[ "vecLightPos" ].SetValue(vLightPosition); |
03 | effect.Parameters[ "vecLightPos2" ].SetValue(vLightPosition2); |
04 | effect.Parameters[ "vecLightPos3" ].SetValue(vLightPosition3); |
07 | effect.Parameters[ "LightRange" ].SetValue(3.0f); |
08 | effect.Parameters[ "LightColor" ].SetValue(vLightColor); |
09 | effect.Parameters[ "LightRange2" ].SetValue(3.0f); |
10 | effect.Parameters[ "LightColor2" ].SetValue(vLightColor2); |
11 | effect.Parameters[ "LightRange3" ].SetValue(3.0f); |
12 | effect.Parameters[ "LightColor3" ].SetValue(vLightColor3); |
注意:你可能注意到这里我没有使用effect.commitChanges(); 如果使用这个shader绘制许多物体,你应该在pass.Begin()中添加这个代码,这样改变才会作用到当前pass,而不是下一个pass,如果你在pass内部设置了shader参数这一步是必须的。