当场景中有多个光源的时候,如何计算多个光源叠加之后的颜色呢?
找到unity cginc中的ShadeVertexLights函数,其最终调用的函数为:ShadeVertexLightsFull
// Used in Vertex pass: Calculates diffuse lighting from lightCount lights. Specifying true to spotLight is more expensive
// to calculate but lights are treated as spot lights otherwise they are treated as point lights.
//注意这个计算多个光源颜色的函数只能在pass为Vertex中使用,为啥呢?我们后面会讲到。
float3 ShadeVertexLightsFull (float4 vertex, float3 normal, int lightCount, bool spotLight)
{
float3 viewpos = UnityObjectToViewPos (vertex);
float3 viewN = normalize (mul ((float3x3)UNITY_MATRIX_IT_MV, normal));
float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz; //叠加了一个环境光颜色,这个你可以去除掉,直接写成:
//float3 lightColor = float3(0,0,0);
for (int i = 0; i < lightCount; i++)
{
float3 toLight = unity_LightPosition[i].xyz - viewpos.xyz * unity_LightPosition[i].w;
float lengthSq = dot(toLight, toLight);
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max(lengthSq, 0.000001);
toLight *= rsqrt(lengthSq);
float atten = 1.0 / (1.0 + lengthSq * unity_LightAtten[i].z);
if (spotLight)
{
float rho = max (0, dot(toLight, unity_SpotDirection[i].xyz));
float spotAtt = (rho - unity_LightAtten[i].x) * unity_LightAtten[i].y;
atten *= saturate(spotAtt);
}
float diff = max (0, dot (viewN, toLight)); //计算入射光和顶点法线的点积
lightColor += unity_LightColor[i].rgb * (diff * atten); //累加多个光源的的颜色
}
return lightColor;
}
补1:unity_LightAtten[i] / unity_LightColor[i] 在哪里可以看到其对应的值。答案是打开frame debug看到。
举例,我们的项目如下:
这里有一个cube,两个光源(包括红色光源和绿色光源)。其属性配置如下:
redlight:
greenlight:
camera:
然后我们的计算光源颜色的shader如下,我们是把unity中的那个光源函数移动到了自己的shader去之后,内容如下:
Shader "Demo/vertexLMRGBM"
{
Properties
{
_MainTex("MainTexture",2D) = "white"{}
_Color("Base Color",Color) = (1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode" = "Vertex"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
float4 _Color;
struct vertOut
{
float4 pos:SV_POSITION;
float4 color:COLOR;
};
float3 CalculateMultipleLights(float4 vertex, float3 normal, int lightCount, bool spotLight)
{
float3 viewpos = UnityObjectToViewPos(vertex);
float3 viewN = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, normal));
float3 lightColor = float3(0,0,0); //忽略环境光颜色
for (int i = 0; i < lightCount; i++) {
float3 toLight = unity_LightPosition[i].xyz - viewpos.xyz * unity_LightPosition[i].w;
float lengthSq = dot(toLight, toLight);
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max(lengthSq, 0.000001);
toLight *= rsqrt(lengthSq);
float atten = 1.0 / (1.0 + lengthSq * unity_LightAtten[i].z);
if (spotLight)
{
float rho = max(0, dot(toLight, unity_SpotDirection[i].xyz));
float spotAtt = (rho - unity_LightAtten[i].x) * unity_LightAtten[i].y;
atten *= saturate(spotAtt);
}
float diff = max(0, dot(viewN, toLight));
lightColor += unity_LightColor[i].rgb * (diff * atten);
}
return lightColor;
}
vertOut vert(appdata_base v)
{
float3 c = CalculateMultipleLights(v.vertex, v.normal, 2, false);
vertOut o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = _Color*float4(c,1);
return o;
}
float4 frag(vertOut i) :COLOR
{
return i.color;
}
ENDCG
}
}
}
看到的效果为:
CalculateMultipleLights函数要写在调用之前声明,符合C规范。下面看看,其补1中的各个变量的值如何查看,打开framedebug:
这里找到画cube的那个batch,看到unity_LightColor0/unity_LightPosition0/unity_LightAtten0等多个变量都是可以查看到的。我们打开unity_LightColor0,可以看到:
[0] = (2,0,0,1),为redlight的Intensity*Color
[1] = (0,1,0,1),为greenlight的Intensity*Color
这样,你就可以很方便的知道各个灯对应的索引在哪里了,结束。
补2:上面提到这个计算多个光源颜色的函数只能用在pass为vertex中,为啥呢?我们不妨把上面的shader的改为:
Tags{“LightMode” = “ForwardBase”}。
此时我们看到的效果为:
为灰色,这个值哪里来的呢?我们不妨看下framedebug,如下:
可以看到除了[0]=(0.67,0.67,0.67,1)其余都为0,而这个值,正式我们立方体最终的颜色。因为我们在:
float3 c = CalculateMultipleLights(v.vertex, v.normal, 2, false);
中计算了两个光源的颜色,其中只有一个为灰色。所以这个函数只有在pass为vertex的时候,才能给出正确的灯光颜色数组。unity_LightColor0[8]才有意义。