Unity支持多种渲染路径。具体我们应该使如何使用渲染路径,取决于我们项目中的灯光、阴影。
渲染路径需要GPU的支持,如果GPU不支行该渲染路径,将依次降级使用下一个Deffered -> Forward -> Vertex Lit。
同一个项目可以用同一种渲染路径,也可以多个渲染路径混合着用。
在Unity3d工程中,我们有三个地方可以设置渲染路径。
1.我们可以在GraphicsSettings面版下全局设置渲染路径,默认是Forward即前向渲染。(见下图)
2.不同的Camera可以有不同的渲染路径。(见下图)
3.Shader的Pass中可以设置渲染路径。(见下图)
果我们都使用指定Forward Rendering(默认),在shader的Pass中不做指定,那么使用的是VertexLit。
- http://docs.unity3d.com/Manual/RenderTech-DeferredShading.html
- http://docs.unity3d.com/Manual/RenderTech-ForwardRendering.html
- http://docs.unity3d.com/Manual/RenderTech-VertexLit.html
Forward Rendering 前向渲染
前向渲染渲染每个物体的时候会用到一个或多个Pass,具体用多少个Pass依赖于作用于该物体的灯光。
前向渲染会根据灯光的设置以及这些灯光对物体的形象程度对这灯光进度一个重要程序排序。重要的灯光认真对待(效果好),不重要的灯光高效率对待(效果差),照射不到该物体的灯光不做处理。
在前向渲染 (Forward Rendering) 中,影响每个对象的一定数量的最亮光源以全逐像素光照模式被渲染。然后,最多 4 个点光灯会逐顶点被计算。其他灯根据球谐函数 (SH) 进行计算,这种计算方式更快速,但只能得到近似值。根据以下内容判断某个光源是否是逐像素光源:
- 渲染模式 (Render Mode) 设置为不重要 (Not Important) 的光源通常是逐顶点或球谐函数。
- 最亮的方向灯通常为逐像素。
- 渲染模式 (Render Mode) 设置为重要 (Important) 的光源通常是逐像素。
- 如果以上内容导致光源数量小于当前像素光源数量 (Pixel Light Count)质量设置,则为了降低亮度,会有更多的光源以逐像素的方式进行渲染。
BasePass和AddPass:
- BasePass用一个逐像素方向灯和所有逐顶点、球谐函数光源渲染对象。该通道还从着色器中添加任何光照贴图、环境光照和放射性光照。该通道中渲染的方向灯可以有阴影 (Shadows)。请注意,使用了光照贴图的对象不会获得来自球谐函数光源的光照。
- AddPass中处理其他逐像素光源在其他通道的渲染,一个通道对应一个光源。这些通道中的光源不能有阴影。
效果图如下:
核心代码:
Name "FORWARD_BASE"
"LightMode" = "ForwardBase"
#pragma multi_compile_fwdbase
#include "Lighting.cginc"
Name "FORWARD_ADD"
"LightMode" = "ForwardAdd"
#pragma multi_compile_fwdadd
#include "Lighting.cginc"
#include "Autolight.cginc"
Blend One One
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
#else
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz - i.vertexWorldPos.xyz);
#endif
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.vertexWorldPos.xyz);
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;
#else
float3 lightCoord = mul(unity_WorldToLight, float4(i.vertexWorldPos, 1)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#endif
下面上代码:
Shader "mgo/study/forward_render_path"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_SpecularTex("_SpecularTex", 2D) = "white" {}
_SpecularGloss("_SpecularGloss", Range(0.01, 100)) = 30
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Name "FORWARD_BASE"
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vertBase
#pragma fragment fragBase
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;//纹理
float4 vertex : SV_POSITION;//裁剪空间下的顶点坐标
float3 worldNormal : TEXCOORD1;//世界空间下的法线
float3 vertexWorldPos : TEXCOORD2;//世界空间下的顶点坐标
};
uniform sampler2D _MainTex;
uniform sampler2D _SpecularTex;
uniform float4 _MainTex_ST;
uniform fixed _SpecularGloss;
v2f vertBase(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject).xyz;
o.vertexWorldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 fragBase(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.vertexWorldPos.xyz);
fixed3 diffuse = _LightColor0.rgb * (dot(worldNormal, worldLight) * 0.5 + 0.5);
fixed3 hDir = normalize(viewDir + worldLight);
fixed3 specular = _LightColor0.rgb * pow(saturate(dot(worldNormal, hDir)), _SpecularGloss) * tex2D(_SpecularTex, i.uv).r;
col.rgb = (col.rgb * diffuse.rgb + specular);
return col;
}
ENDCG
}
Pass
{
Name "FORWARD_ADD"
Tags{ "LightMode" = "ForwardAdd" }
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd
#pragma vertex vertAdd
#pragma fragment fragAdd
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "Autolight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;//纹理
float4 vertex : SV_POSITION;//裁剪空间下的顶点坐标
float3 worldNormal : TEXCOORD1;//世界空间下的法线
float3 vertexWorldPos : TEXCOORD2;//世界空间下的顶点坐标
};
uniform sampler2D _MainTex;
uniform sampler2D _SpecularTex;
uniform float4 _MainTex_ST;
uniform fixed _SpecularGloss;
v2f vertAdd(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject).xyz;
o.vertexWorldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 fragAdd(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed3 worldNormal = normalize(i.worldNormal);
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
#else
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz - i.vertexWorldPos.xyz);
#endif
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.vertexWorldPos.xyz);
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;
#else
float3 lightCoord = mul(unity_WorldToLight, float4(i.vertexWorldPos, 1)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#endif
fixed3 diffuse = _LightColor0.rgb * (dot(worldNormal, worldLight) * 0.5 + 0.5) * atten;
fixed3 hDir = normalize(viewDir + worldLight);
fixed3 specular = _LightColor0.rgb * pow(saturate(dot(worldNormal, hDir)), _SpecularGloss) * tex2D(_SpecularTex, i.uv).r * atten;
col.rgb = (col.rgb * diffuse.rgb + specular);
return col;
}
ENDCG
}
}
}