Unity Shader入门教程(十二):渲染路径与光源类型
发表于2018-06-06
一、渲染路径
1.渲染路径设置
渲染路径决定了光照是如何应用到UnityShader中的。
指定了特定的渲染路径则Unity会把特定的光照信息存储在那些特定的数据里,这样就可以访问了。
在Unity项目中设置渲染路径:
在相机中的RenderPath设置可以覆盖PlayerSettings中的设置
[如果物体材质shader的Pass通道没有使用摄像机对应的渲染模式,则无法被渲染]
LightMode标签支持的设置
2.关于Light的设置
culling Mask 设置那些Layer的物体会被该光源影响
Render Mode决定渲染模式
二、前向渲染
1.前向渲染路径的原理
每进行一次完整的前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:一个是颜色缓冲区,一个是深度缓冲区。利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲区的颜色值。
对于每个逐像素光源,我们都需要进行上面一次完整的渲染流程。
假设,场景中有N个物体 每个物体受到M个光源的影响 那么要渲染整个场景一共需要N*M个Pass
2.Unity中的前向渲染
Unity中,前向渲染路径有3种处理光照的方式:逐顶点处理、逐像素处理、球谐函数处理。
决定一个光源使用哪种处理模式取决于它的光源类型和渲染模式。
在前向渲染中,当我们渲染一个物体时,Unity会根据场景中各个光源的设置以及这些光源对物体的影响程度对这些光源进行一个重要度排序。
其中,一定数目的光源会按照逐像素的方式处理,然后最多4个光源按照逐顶点的方式处理,剩下的光源可以按SH方式处理。
·场景中最亮的平行光总是按逐像素处理的。 ·渲染模式被设置成Not Important的光源,会按照逐顶点 ·渲染模式被设置成Important的光源,会按逐像素处理。 ·如果根据以上规则得到的逐像素光源数量小于QualitySetting中的逐像素光源数量(Pixel Light Count),会有更多的光源一逐像素的方式进行渲染。
前向渲染路径中有两种Pass:BasePass和AdditionalPass
·这里使用了#pragma multi_compile_fwdbase ,这是在告诉编译器,我们要指定自定义的additional pass ·Additional Pass还开启和设置了混合模式,是因为我们瓷王每个AdditionalPass可以遇上一次的光照结果在缓冲帧中进行叠加 ·对于前向渲染来说,一个UnityShader 通常只会定义一个BasePass(也可以被定义多次,如需要双面渲染等情况)以及一个AdditionalPass。每个BasePass仅会被执行一次,而一个AdditionalPass会根据影响该物体的其它逐像素光源的数目被多次调用,即每个逐像素光源会执行一次AdditionalPass
前向渲染中可以使用的内置光照函数
三、延迟渲染
1.延迟渲染的原理
延迟渲染主要包含了两个Pass。
在第一个Pass中,我们不进行任何光照计算,而是仅仅计算那些片元是可见的,这主要是通过深度缓冲技术来实现的,当发现一个片元是可见的,我们就把它的先关信息存储到G缓冲区(图形缓冲区?)。
在第二个Pass中,我们利用G缓冲区的各个偏远信息,例如表面法线、视角方向、漫反射系数等,进行真正的光照计算。
2.Unity中的延迟渲染
如果游戏中使用了大量的实时光照,那么我们希望能选择延迟渲染路径,但这种路径需要一定的硬件支持。
延迟渲染的缺点:
·不支持真正的抗锯齿功能 ·不能处理半透明物体 ·对显卡有一定要求
四、光源类型
1.Unity中一共支持4种光源类型:平行光、点光源、聚光灯和面光源
2.前向渲染中处理不同的光源类型
代码实践:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Custom/Edu/ForwardRendering" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Gloss("Gloss",Range(8,256)) = 40 } SubShader { Tags { "RenderType"="Opaque" "Queue" = "Geometry" "IgnoreProjector" = "ture" } //在第一个Pass中处理最重要的平行光 //如果场景中没有任何平行光,那么base pass将会被当做全黑处理 Pass { Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma multi_compile_fwdbase #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; float _Gloss; struct a2v { float4 vertex:POSITION; float4 texcoord:TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float4 pos:SV_POSITION; float3 worldNormal:TEXCOORD0; float2 uv:TEXCOORD1; float3 worldPos:TEXCOORD2; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); return o; } fixed4 frag(v2f i):SV_Target { float3 worldNormal = normalize(i.worldNormal); float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 albedo = tex2D(_MainTex,i.uv).rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal,worldLightDir)); fixed3 halfDir = normalize(worldLightDir + worldViewDir); fixed3 specular = _LightColor0.rgb * pow(saturate(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 "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; float _Gloss; struct a2v { float4 vertex:POSITION; float4 texcoord:TEXCOORD0; float3 normal:NORMAL; }; struct v2f { float4 pos:SV_POSITION; float3 worldNormal:TEXCOORD0; float2 uv:TEXCOORD1; float3 worldPos:TEXCOORD2; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); return o; } fixed4 frag(v2f i):SV_Target { float3 worldNormal = normalize(i.worldNormal); float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); #ifdef USING_DIRECTIONAL_LIGHT fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); #else fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz); #endif fixed3 albedo = tex2D(_MainTex,i.uv).rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal,worldLightDir)); fixed3 halfDir = normalize(worldLightDir + worldViewDir); fixed3 specular = _LightColor0.rgb * pow(saturate(dot(worldNormal,halfDir)),_Gloss); #ifdef USING_DIRECTIONAL_LIGHT fixed atten = 1.0; #else float3 lightCoord = mul(_LightMatrix0,float4(i.worldPos,1)).xyz; fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL; #endif return fixed4(ambient + (diffuse + specular)*atten ,1.0); } ENDCG } } FallBack "Diffuse" }
3.效果图
4.后记
Base Pass会处理场景中最重要的平行光。 如果场景中包含了多个平行光,Unity会选择最亮的平行光传递给Base Pass进行逐像素处理。 最亮平行光以外其它平行光会按照逐顶点或在AdditionalPass中按照逐像素的方式处理。 如果场景中没有平行光,那么BasePass会当成全黑的光源处理。
5.如果AdditionalPass中不设置并开启融合:Blend One One,那么后续的光照效果便会覆盖前面的效果,未开启融合的效果图: