Shader 多个点光源

 

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

本教程基于教程17中实现的点光源+自阴影,要实现多个光源,原理上并没有什么新东西。

多个点光源

要实现多个点光源,我们需要在shader中使用n次光照方程。因此,如果有三个光源,我们需要为三个光源的漫反射、镜面高光、自阴影和衰减,下面是光照方程:

如你所见,除了需要对阴影、漫反射、镜面高光和衰减求和,上面的光照方程与教程17是相同的。

例如有三个光源,一个紫色、一个绿色、一个蓝色位于物体的周围,则结果如下图所示:

实现shader

我们需要计算光照方程n次。本例中,我实现了3个光源。这意味着我需要发送3个位置、光照范围和颜色到shader中:

view source
print ?
1 float4 LightColor;
2 float4 LightColor2;
3 float4 LightColor3;
4 float3 vecLightPos;
5 float3 vecLightPos2;
6 float3 vecLightPos3;
7 float LightRange;
8 float LightRange2;
9 float LightRange3;

然后,在顶点着色器中,我想需要计算顶点到光源的距离,将光源位置转换到切线空间中(法线映射)并计算每个光源的衰减:

view source
print ?
01 // calculate distance to light in world space
02 float3 L = vecLightPos - PosWorld;
03 float3 L2 = vecLightPos2 - PosWorld;
04 float3 L3 = vecLightPos3 - PosWorld;
05   
06 // Transform light to tangent space
07 Out.Light.xyz = normalize(mul(worldToTangentSpace, L)); // L, light
08 Out.Light2.xyz = normalize(mul(worldToTangentSpace, L2)); // L2, light
09 Out.Light3.xyz = normalize(mul(worldToTangentSpace, L3)); // L3, light
10   
11 // Add range to the light, attenuation
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次而已。

下面看一下像素着色器。首先需要计算每个光源的方向:

view source
print ?
1 // Get light direction/view from vertex shader output
2 float3 LightDir = normalize(L.xyz);// L
3 float3 LightDir2 = normalize(L2.xyz);// L2
4 float3 LightDir3 = normalize(L3.xyz);// L3

然后使用上面的光照公式(1)计算漫反射、镜面高光和自阴影:

view source
print ?
01 // diffuse
02 float D = saturate(dot(N, LightDir)); 
03 float D2 = saturate(dot(N, LightDir2)); 
04 float D3 = saturate(dot(N, LightDir3)); 
05   
06 // Self shadow - used to avoid light artifacts
07 float Shadow = saturate(4.0 * LightDir.z);
08 float Shadow2 = saturate(4.0 * LightDir2.z);
09 float Shadow3 = saturate(4.0 * LightDir3.z);
10       
11 // reflection
12 float3 R = normalize(2 * D * N - LightDir); // R
13 float3 R2 = normalize(2 * D2 * N - LightDir2); // R
14 float3 R3 = normalize(2 * D3 * N - LightDir3); // R
15   
16 // specular
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)实现最终的颜色:

view source
print ?
1 // calculate three point lights:
2 // Ambient +  Shadow*((Diffuse + Specular)*Attenuation in L.w);
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个光源的位置、颜色和光照范围:

view source
print ?
01 // Set the light positions for each of the lights 
02 effect.Parameters["vecLightPos"].SetValue(vLightPosition); 
03 effect.Parameters["vecLightPos2"].SetValue(vLightPosition2); 
04 effect.Parameters["vecLightPos3"].SetValue(vLightPosition3); 
05   
06 // Set the light range and color for each of the lights 
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参数这一步是必须的。

文件下载,已下载146次

你可能感兴趣的:(each,float,shader,output,distance)