Unity中的光源类型(向前烘托途径进行光照核算)
Unity中的光源类型
Unity中共支撑4种光源类型:
- 平行光
- 点光源
- 聚光灯
- 面光源(在光照烘焙时才能够发挥作用)
光源的特点:
- 方位
- 方向(到某个点的方向)
- 色彩
- 强度
- 衰减(到某个点的衰减)
-
平行光
平行光的几许界说是最简略的,平行光能够照亮的规模是无限远的,且对与场景中的各个点的方向和强度都是共同的。在场景中作为太阳这样的人物呈现。
-
点光源
点光源照亮的空间是有限的,它是由空间中的一个球体界说的。其能够表明由一个点宣布的、向一切方向延伸的光。
需求留意的是点光源的方向特点是由某个点减去点光源方位所得出的向量,表明点光源在该点的光照方向。点光源会衰减,跟着物体逐步原理点光源,其接收到的光照强度也会逐步减小。
-
聚光灯
聚光灯是这3种光源类型中最杂乱的一种。它的照亮空间相同是有限的,但不再是简略的球体,而是由空间中的一块锥形区域界说的。聚光灯能够用于表明由一个特定方位动身、向特定方向延伸的光。
这块锥形区域的半径由面板中的Range特点决议,而锥体的打开视点由Spot Angle特点决议。咱们相同也能够在 Scene视图中直接迁延聚光灯的线框(如中心的黄色控制点以及四周的黄色控制点)来修正它的特点。聚光灯的方位相同是由Transform组件中的Position特点界说的。关于方向特点,咱们需求用聚光灯的方位减去某点的方位来得到它到该点的方向。聚光灯的衰减也是跟着物体逐步远离点光源而逐步减小,在锥形的极点处光照强度最强,在锥形的鸿沟处强度为0。其中心的衰减值能够由一个函数界说,这个函数相关于点光源衰减核算公式要愈加杂乱,由于咱们需求判别一个点是否在锥体的规模内。
在向前烘托中处理不同的光照类型
Shader "Custom/ForwardRanderingLearn"
{
Properties{
_Diffuse("Diffuse", Color) = (1,1,1,1) //漫反射色彩
_Specular("Specular",Color) = (1,1,1,1)//高光反射色彩
_Gloss("Gloss",Range(8.0,256)) = 20 //高光反射强度
}
SubShader{
Tags { "RenderType" ="Opaque" }
Pass
{
//设置烘托形式
Tags{ "LightMode"="ForwardBase" }
CGPROGRAM
//增加宏引证
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //平行光的方向
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //环境光
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0,dot(worldNormal,worldLightDir));
//核算高光反射
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);
//平行光的衰减因子
fixed atten = 1.0;
return fixed4(ambient + (diffuse + specular) * atten,1.0);
}
ENDCG
}
Pass
{
Tags {"LightMode" = "ForwardAdd"}
//敞开混合形式
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
//依据光照类型确认光源方向
#ifdef USING_DIRECTIONAL_LIGHT
//平行光
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
//非平行光
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
//漫反射光
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0,dot(worldNormal,worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);
//依据光源类型来设置衰减函数
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;
#else
#if defined(POINT)
float3 lightCoord = mul(unity_WorldToLight,float4(i.worldPos,1.0)).xyz;
fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
#elif defined (SPOT)
float4 lightCoord = mul(unity_WorldToLight,float4(i.worldPos,1.0)).xyz;
fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#else
fixed atten = 1.0;
#endif
#endif
return fixed4((diffuse + specular) * atten,1.0);
}
ENDCG
}
}
FallBack "Specular"
}
在此shader中,在Base Pass中处理场景中最重要的平行光。
本场景中只要一个平行光,因而Base Pass只会履行一次。假如场景中包括多个平行光,Unity则会挑选最亮的平行光传递给Base Pass进行逐像素处理,其它平行光会依照逐极点或在Additional Pass中依照逐像素方法处理。
假如场景中没有任何平行光,那么Base Pass会当满足黑的光源处理。
关于Base Pass来说,它处理的逐像素光源类型一定是平行光。咱们能够运用__WorldSpaceLightPos0来得到这个平行光的方向(方位对平行光来说没有意义),运用_LightColor0来得到它的色彩和强度(_LightColor0已经是色彩和强度相乘后的成果),由于平行光能够认为是没有衰减的,因而这儿咱们直接令衰减值为1.0。相关代码如下:
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
...
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)),
_Gloss);
// The attenuation of directional light is always 1
fixed atten = 1.0;
return fixed4(ambient + (diffuse + specular) * atten, 1.0);
接下来,咱们需求为场景中其他逐像素光源界说Additional Pass。为此,咱们首要需求设置Pass的烘托途径标签:
Pass {
// Pass for other pixel lights
Tags { "LightMode"="ForwardAdd" }
Blend One One
CGPROGRAM
// Apparently need to add this declaration
#pragma multi_compile_fwdadd
与Base Pass不同的是,咱们还运用Blend指令敞开和设置了混合形式。这是由于,咱们期望Additional Pass核算得到的光照成果能够在帧缓存中与之前的光照成果进行叠加。假如没有运用Blend指令的话,Additional Pass会直接覆盖掉之前的光照成果。在本例中,咱们挑选的混合系数是Blend One One,这不是必需的,咱们能够设置成Unity支撑的任何混合系数。常见的还有Blend SrcAlpha One。
一般来说,Additional Pass的光照处理和Base Pass的处理方法是相同的,因而咱们只需求把Base Pass的极点和片元着色器代码粘贴到Additional Pass中,然后再略微修正一下即可。这些修正往往是为了去掉Base Pass中环境光、自发光、逐极点光照、SH光照的部分,并增加一些对不同光源类型的支撑。因而,在Additional Pass的片元着色器中,咱们没有再核算场景中的环境光。
因而在核算光源的5个特点——方位、方向、色彩、强度以及衰减时,色彩和强度咱们依然能够运用_LightColor0来得到,但关于方位、方向和衰减特点,咱们就需求依据光源类型别离核算。首要,咱们来看怎么核算不同光源的方向:
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPosition.xyz);
#endif
处理不同光源的衰减:
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;
#else
float3 lightCoord = mul(_LightMatrix0, float4(i.worldPosition, 1)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#endif
咱们相同经过判别是否界说了USING_DIRECTIONAL_LIGHT来决议当时处理的光源类型。假如是平行光的话,衰减值为1.0。假如是其他光源类型,那么处理更杂乱一些。虽然咱们能够运用数学表达式来核算给定点相关于点光源和聚光灯的衰减,但这些核算往往触及开根号、除法等核算量相对较大的操作,因而Unity挑选了运用一张纹路作为查找表(Lookup Table, LUT),以在片元着色器中得到光源的衰减。咱们首要得到光源空间下的坐标,然后运用该坐标对衰减纹路进行采样得到衰减值。
注:本文为冯乐乐《Unity Shader入门精要读书笔记》