XNA Shader编程教程18-多个点光源

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

程序截图

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

多个点光源

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

光照方程

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

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

多个点光源

实现shader

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

float4 LightColor;
float4 LightColor2;
float4 LightColor3;
float3 vecLightPos;
float3 vecLightPos2;
float3 vecLightPos3;
float LightRange;
float LightRange2;
float LightRange3;

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

// calculate distance to light in world space
float3 L = vecLightPos - PosWorld;
float3 L2 = vecLightPos2 - PosWorld;
float3 L3 = vecLightPos3 - PosWorld;

// Transform light to tangent space
Out.Light.xyz = normalize(mul(worldToTangentSpace, L));  // L, light
Out.Light2.xyz = normalize(mul(worldToTangentSpace, L2));  // L2, light
Out.Light3.xyz = normalize(mul(worldToTangentSpace, L3));  // L3, light

// Add range to the light, attenuation
Out.Light.w = saturate( 1 - dot(L / LightRange, L / LightRange));
Out.Light2.w = saturate( 1 - dot(L2 / LightRange2, L2 / LightRange2));
Out.Light3.w = saturate( 1 - dot(L3 / LightRange3, L3 / LightRange3));

其实没什么新东西,只不过将教程17中的步骤做了3次而已。

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

// Get light direction/view from vertex shader output
float3 LightDir = normalize(L.xyz); // L
float3 LightDir2 = normalize(L2.xyz); // L2
float3 LightDir3 = normalize(L3.xyz); // L3

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

// diffuse
float D = saturate(dot(N, LightDir)); 
float D2 = saturate(dot(N, LightDir2)); 
float D3 = saturate(dot(N, LightDir3)); 

// Self shadow - used to avoid light artifacts
float Shadow = saturate(4.0 * LightDir.z);
float Shadow2 = saturate(4.0 * LightDir2.z);
float Shadow3 = saturate(4.0 * LightDir3.z);
    
// reflection
float3 R = normalize(2 * D * N - LightDir);  // R
float3 R2 = normalize(2 * D2 * N - LightDir2);  // R
float3 R3 = normalize(2 * D3 * N - LightDir3);  // R

// specular
float S = min(pow(saturate(dot(R, ViewDir)), 3), Color.w);
float S2 = min(pow(saturate(dot(R2, ViewDir)), 3), Color.w);
float S3 = min(pow(saturate(dot(R3, ViewDir)), 3), Color.w);

现在我们已经从光照方程中计算得出需要的东西,下面使用公式(2)实现最终的颜色:

// calculate three point lights:
// Ambient +  Shadow*((Diffuse + Specular)*Attenuation in L.w);
float4 light1final = Shadow*((Color * D  * LightColor + S*LightColor) * (L.w));
float4 light2final = Shadow2*((Color * D2  * LightColor2 + S2*LightColor2) * (L2.w));
float4 light3final = Shadow3*((Color * D3  * LightColor3 + S3*LightColor3) * (L3.w));
return 0.1 * Color + light1final + light2final + light3final; 

好了,就是这些!简单地说,要获得n个光照的效果,你需要计算光照方程n次。

使用shader

与教程17相比并没有什么新东西。但是这次我们需要传递3个光源的位置、颜色和光照范围:

// Set the light positions for each of the lights 
effect.Parameters["vecLightPos"].SetValue(vLightPosition); 
effect.Parameters["vecLightPos2"].SetValue(vLightPosition2); 
effect.Parameters["vecLightPos3"].SetValue(vLightPosition3); 

// Set the light range and color for each of the lights 
effect.Parameters["LightRange"].SetValue(3.0f); 
effect.Parameters["LightColor"].SetValue(vLightColor); 
effect.Parameters["LightRange2"].SetValue(3.0f); 
effect.Parameters["LightColor2"].SetValue(vLightColor2); 
effect.Parameters["LightRange3"].SetValue(3.0f); 
effect.Parameters["LightColor3"].SetValue(vLightColor3); 

注意:你可能注意到这里我没有使用effect.commitChanges(); 如果使用这个shader绘制许多物体,你应该在pass.Begin()中添加这个代码,这样改变才会作用到当前pass,而不是下一个pass,如果你在pass内部设置了shader参数这一步是必须的。

文件下载(已下载 1382 次)

发布时间:2010/6/21 下午4:41:12  阅读次数:8610

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号