Unity支持多种类型的渲染路径:前向渲染路径,延迟渲染路径,顶点照明渲染路径(好像没了,我也没用过)。
在项目设置里可以设置整体的渲染路径,摄像机里也可以设置是否采用项目设置里的渲染路径。
这里列出的都是在Pass Tags LightMode 里的标签,看情况加。
先说说关于前向渲染:前向渲染里有三种光照方式:逐顶点,逐像素,球谐函数(SH)处理,。光源的渲染模式是设置该光源是否重要,根据是否重要在前向渲染种决定是使用哪一种光照方式。
前向渲染是两个Pass,一个是BasePass,就是计算平行光,环境光,自发光,光照纹理,平行光的阴影
,简单理解就是 逐像素计算平行光,其他的都是逐顶点的。
在BasePass里的平行光默认是支持阴影的计算,对于环境光和自发光,都是放在了这个pass计算,这两个是因为只要计算一次,而不是在后面的AddPass里再计算,造成OverDraw。
第二个pass是Additional Pass,我简称为addpass,这个pass就是补充了第一个pass的不足,去计算之前没有逐像素计算的一些灯光(就是那些你认为影响比较大的灯光),出现一个逐像素的灯光,这个pass就被调用一次。
配套使用的编译指令:BasePass “LightMode”=“FordwardBase”
#pragma multi_compile_fwdbase
AdditionalPass “LightMode”="FordwardAdd
Blend One One(用One One,是与上一个pass中得出的计算结果进行线性减淡的叠加(这在我之前的02笔记里提到了这种混合方式),不加就会覆盖上一个计算)
#pragma multi_compile_fwdadd
下面上个代码:
Shader “Unlit/Forward Rendering” {
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 {
// Pass for ambient light & first pixel light (directional light)
Tags { “LightMode” = “ForwardBase” }
CGPROGRAM
// Apparently need to add this declaration
// #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);//之前提到的Blinn-Phone
return fixed4(ambient + (diffuse + specular), 1.0);
}
ENDCG
}
Pass {
// Pass for other pixel lights
Tags { “LightMode” = “ForwardAdd” }
Blend One One
CGPROGRAM
// Apparently need to add this declaration
#pragma multi_compile_fwdadd_fullshadows
#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;
}
// 不在需要环境光的叠加,在BasePass中已经计算过环境光的影响了,多次叠加会导致色彩异常
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
// 1.漫反射
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
// 2.高光反射
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
// 3.计算光照衰减 attenuation,这里是根据灯光类型不行,采用了不同的计算(好像还有个Unity自己的自动灯光计算,以后加)
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;//平行光没有衰减
#else
#if defined (POINT)//点光源
// 计算顶点世界坐标到光源的 光源空间位置 【注意:是unity_WorldToLight】
float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;//unity_WorldToLight也就是_LightMatrix0,坐标转换到光源空间
// 用了_LightTexture0用来计算光照衰减,避免了复杂的数学计算。(0,0)表示最近点的衰减,(1,1)表示最远点的衰减如果用
//了cookie,需要使用_LightTextureB0来计算
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;//使用了这个坐标的模的平方来做衰减,因为
//点光源的衰减是平方衰减,用这方法避免了开方操作,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
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}
//FallBack “Specular”
}
关于延迟渲染:也是两个Pass,第一个Pass用于渲染G缓冲,把物体的漫反射颜色,高光反射颜色,平滑度,法线,自发光和深度等信息渲染到屏幕空间的G缓冲区中。对于整个物体来说,这个Pass只会执行一次。说白了,就是一种预处理,我先把计算光照要用到的玩意都先给你了,然后下一步就是你去干光照吧。
第二个Pass开始真正的计算
//https://www.cnblogs.com/wangchengfeng/p/3440097.html 延迟渲染的
总结:缺点比较多,对于计算量大的时候可以使用。
ShadowMap:就是把摄像机的位置放在与光源重合的位置上,看的到的没阴影,看不到的地方出现阴影。所以再前向渲染中,这也算是一种深度图吧,记录了看到的东西(距离最近的表面位置,即深度信息)
Pass:“LightMode”=“ShadowCaster” (中文是阴影投射?物体上勾选的castshadow和这玩意有关系嘛?)
如果写的pass里没有,会再fallback里找这玩意,所以有时候写了fallback diffuse就有阴影了,不过用的是fallback里的,如果你有裁剪啊,透明度等变化,这里还是要重新写一下,不然阴影就不对了。
这里说一下啊,投射阴影和接受阴影是两码事,要接受阴影,就要对阴影映射纹理进行采样,混合光照结果产生阴影。
而投射阴影就要把这物体加入到光源的阴影映射纹理中!这操作就是执行shadowcaster这个pass。
投射阴影四部曲:1.LightMode设置为ShadowCaster
2.编译指令#pragma multi_compile_shadowcaster
3.v2f结构体定义V2F_SHADOW_CASTER
4.顶点着色器:TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
5.表面着色器:SHADOW_CASTER_FRAGMENT(i);
接受阴影四部曲:1.在前向渲染的pass里添加程序集
#include"AutoLight.cginc"
2.v2f结构体里添加内置宏SHADOW_COORDS(n),n是一个数量的序号//声明个变量的
3.在顶点着色器里添加TRANSFER_SHADOW(o);//转换空间的
4.在片元着色器里也使用内置宏SHADOW_ATTENUATION(i) //采样的
接受阴影补充:将阴影和光线衰减统一,使用SHADOW_LIGHT_ATTENUATION(atten,i,i.worldPos)
atten也就是衰减因子,都由unity计算好了,直接用就行了