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