基于内置渲染管线中的不同渲染路径,鬼知道看这一节课踩了多少坑。个人推荐了解即可,因为阴影的处理和多光源光照是一个大坑,不是简简单单就能实现出来的,等以后有机会了再深入了解
前向渲染路径,延迟渲染路径,顶点照明渲染路径,旧版延迟渲染路径(后两个已经很少用了)。
指定渲染路径后,Unity才会对对应的Pass填充对应的内置光照变量等等。
一、前向渲染
渲染原理: 就按前面的光照模型正常渲染。
多光源的处理:最亮平行光按照Base Pass处理,其余设置为重要的光源按照Additional Pass处理,不过不重要的光源也是放在Base Pass中处理,不懂为什么。光源和物体的数量越多,负担越大,每一次都是从头计算。
光照衰减:平行光默认为1,即不衰减,其他种类的光源,用Unity内置的光照衰减纹理采样即可,为什么这样采样不知道。
使用两个Pass,分别渲染不同光源,只含光照衰减。
1.1 多光照处理,含光照衰减:
// 前向渲染,光照衰减
Shader "Custom/Test0"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8, 256)) = 20
}
SubShader
{
Tags { "RenderType" = "Opaque" }
//第一个Pass正常来
pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
// 编译指令,保证在pass中得到Pass中得到正确的光照变量
#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: TEXCOORD;
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);
return o;
}
fixed4 frag(v2f i): SV_TARGET
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
float3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed atten = 1.0;
return fixed4(ambient + (diffuse + specular) * atten, 1);
}
ENDCG
}
//这个Pass计算其他重要光源
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);
//关于光源那个矩阵,unity疯狂报错,有时结果正确,有时错误,不知道是不是自己的问题。
//计算光源方向
#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)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr)
.UNITY_ATTEN_CHANNEL;
#elif defined(SPOT) // 聚光灯
float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
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
// 纹理采样方法可以减少计算衰减时的复杂度,但有时也可以使用纯数学公式计算光照衰减
// float distance = length(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
// float atten = 1.0 / distance;
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}
}
1.2:阴影处理
阴影分为投射阴影和接受阴影。
投射阴影:简单的说将物体相关信息输出到阴影映射纹理。阴影投射也会受到物体正反面的影响,有时需要在Mesh Renderer设置双面,Addtional Pass按书上的说法也无法投射阴影,不知道怎么回事。
Pass
{
Tags{"LightMode"="ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
}
fixed4 frag(v2f i):SV_Target
{
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
}
接受阴影:根据阴影映射纹理采样结果计算。
接受阴影使用的内置宏需要开发者定义的变量名和Unity使用的变量名相同,否则会报错。
// 前向渲染,光照衰减
Shader "Custom/Test0"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8, 256)) = 20
}
SubShader
{
Tags { "RenderType" = "Opaque" }
Pass
{
Tags{"LightMode"="ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
}
fixed4 frag(v2f i):SV_Target
{
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
}
pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
// 编译指令,保证在pass中得到Pass中得到正确的光照变量
#pragma multi_compile_fwdbase
#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: TEXCOORD;
float3 worldPos: TEXCOORD1;
SHADOW_COORDS(2)
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i): SV_TARGET
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
float3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed atten = 1.0;
fixed shadow=SHADOW_ATTENUATION(i);
return fixed4(ambient+(diffuse+specular)*atten*shadow,1);
}
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)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr)
.UNITY_ATTEN_CHANNEL;
#elif defined(SPOT) // 聚光灯
float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
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
// 尽管纹理采样方法可以减少计算衰减时的复杂度,有时也可以使用数学公式计算光照衰减:
// float distance = length(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
// float atten = 1.0 / distance;
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}
}
1.3 统一管理光照衰减和接受阴影相关:
// 前向渲染,光照衰减
Shader "Custom/Test0"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8, 256)) = 20
}
SubShader
{
Tags { "RenderType" = "Opaque" }
Pass
{
Tags{"LightMode"="ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
}
fixed4 frag(v2f i):SV_Target
{
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
}
pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
// 编译指令,保证在pass中得到Pass中得到正确的光照变量
#pragma multi_compile_fwdbase
#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: TEXCOORD;
float3 worldPos: TEXCOORD1;
SHADOW_COORDS(2)
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i): SV_TARGET
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
float3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
return fixed4(ambient+(diffuse+specular)*atten,1);
}
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;
SHADOW_COORDS(2)
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
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);
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
return fixed4((diffuse+specular)*atten,1);
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}
}
关于透明物体阴影,只提一点生成阴影时要考虑物体的透明通道即可,Unity有内置宏,可以用FallBack的方式,我的建议是直接不看。
二、延迟渲染
原理大致就是只有两个Pass,第一个Pass只计算那些片元可见,可见然后放到G缓冲区,之后由第二个Pass计算,也就是说,不和上面的那种分光源计算,所有光源都用一个Pass。
没有例子。