一、ForwardAdd
// Additive forward pass (one light per pass)Pass{Name "FORWARD_DELTA"Tags { "LightMode" = "ForwardAdd" }Blend [_SrcBlend] OneFog { Color (0,0,0,0) } // in additive pass fog should be blackZWrite OffZTest LEqualCGPROGRAM#pragma target 3.0// -------------------------------------#pragma shader_feature_local _NORMALMAP#pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON#pragma shader_feature_local _METALLICGLOSSMAP#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A#pragma shader_feature_local_fragment _SPECULARHIGHLIGHTS_OFF#pragma shader_feature_local_fragment _DETAIL_MULX2#pragma shader_feature_local _PARALLAXMAP#pragma multi_compile_fwdadd_fullshadows#pragma multi_compile_fog// Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.//#pragma multi_compile _ LOD_FADE_CROSSFADE#pragma vertex vertAdd#pragma fragment fragAdd#include "UnityStandardCoreForward.cginc"ENDCG}
引用了UnityStandardCoreForward.cginc
中的vertAdd
和fragAdd
以下是UnityStandardCoreForward.cginc
的源碼
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)#ifndef UNITY_STANDARD_CORE_FORWARD_INCLUDED
#define UNITY_STANDARD_CORE_FORWARD_INCLUDED#if defined(UNITY_NO_FULL_STANDARD_SHADER)
# define UNITY_STANDARD_SIMPLE 1
#endif#include "UnityStandardConfig.cginc"#if UNITY_STANDARD_SIMPLE#include "UnityStandardCoreForwardSimple.cginc"VertexOutputBaseSimple vertBase (VertexInput v) { return vertForwardBaseSimple(v); }//------關鍵代碼--------VertexOutputForwardAddSimple vertAdd (VertexInput v) { return vertForwardAddSimple(v); }half4 fragBase (VertexOutputBaseSimple i) : SV_Target { return fragForwardBaseSimpleInternal(i); }//------關鍵代碼--------half4 fragAdd (VertexOutputForwardAddSimple i) : SV_Target { return fragForwardAddSimpleInternal(i); }
#else#include "UnityStandardCore.cginc"VertexOutputForwardBase vertBase (VertexInput v) { return vertForwardBase(v); VertexOutputForwardAdd vertAdd (VertexInput v) { return vertForwardAdd(v); }half4 fragBase (VertexOutputForwardBase i) : SV_Target { return fragForwardBaseInternal(i); }half4 fragAdd (VertexOutputForwardAdd i) : SV_Target { return fragForwardAddInternal(i); }
#endif#endif // UNITY_STANDARD_CORE_FORWARD_INCLUDED
UNITY_STANDARD_SIMPLE
屬于 ?簡化版前向渲染路徑? 的標識符,指令區分兩種實現:
- 簡化版?:減少復雜的光照計算(如間接光照、高光反射的精細處理),適用于移動端或低性能設備?
- 標準版?:完整支持基于物理的渲染(PBR)特性,包含金屬度、粗糙度等完整材質屬性計算?
標準版之前看過,現在看看簡化版的
引用了UnityStandardCoreForwardSimple.cginc
的vertForwardAddSimple
和fragForwardAddSimpleInternal
二、vertForwardAddSimple
struct VertexOutputForwardAddSimple
{UNITY_POSITION(pos);float4 tex : TEXCOORD0;float3 posWorld : TEXCOORD1;#if !defined(_NORMALMAP) && SPECULAR_HIGHLIGHTSUNITY_FOG_COORDS_PACKED(2, half4) // x: fogCoord, yzw: reflectVec
#elseUNITY_FOG_COORDS_PACKED(2, half1)
#endifhalf3 lightDir : TEXCOORD3;#if defined(_NORMALMAP)#if SPECULAR_HIGHLIGHTShalf3 tangentSpaceEyeVec : TEXCOORD4;#endif
#elsehalf3 normalWorld : TEXCOORD4;
#endifUNITY_LIGHTING_COORDS(5, 6)UNITY_VERTEX_OUTPUT_STEREO
};VertexOutputForwardAddSimple vertForwardAddSimple(VertexInput v)
{// 定義輸出結構體VertexOutputForwardAddSimple o;// 設置實例ID,確保多實例渲染正確工作UNITY_SETUP_INSTANCE_ID(v);// 初始化輸出結構體的成員變量UNITY_INITIALIZE_OUTPUT(VertexOutputForwardAddSimple, o);// 初始化立體視圖輸出(如果需要)UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);// 將頂點從對象空間轉換到世界空間float4 posWorld = mul(unity_ObjectToWorld, v.vertex);// 將頂點從對象空間轉換到裁剪空間(用于最終渲染)o.pos = UnityObjectToClipPos(v.vertex);// 獲取紋理坐標o.tex = TexCoords(v);// 存儲世界空間中的頂點位置o.posWorld = posWorld.xyz;// 傳遞陰影和光照信息UNITY_TRANSFER_LIGHTING(o, v.uv1);// 計算光照方向向量half3 lightDir = _WorldSpaceLightPos0.xyz - posWorld.xyz * _WorldSpaceLightPos0.w;// 如果不是使用方向光,則對光照方向進行歸一化#ifndef USING_DIRECTIONAL_LIGHTlightDir = NormalizePerVertexNormal(lightDir);#endif// 如果啟用了鏡面高光效果,計算視線方向向量#if SPECULAR_HIGHLIGHTShalf3 eyeVec = normalize(posWorld.xyz - _WorldSpaceCameraPos);#endif// 將法線從對象空間轉換到世界空間half3 normalWorld = UnityObjectToWorldNormal(v.normal);// 如果啟用了法線貼圖#ifdef _NORMALMAP// 如果啟用了鏡面高光效果#if SPECULAR_HIGHLIGHTS// 調用 TangentSpaceLightingInput 函數,將光照方向和視線方向轉換到切線空間TangentSpaceLightingInput(normalWorld, v.tangent, lightDir, eyeVec, o.lightDir, o.tangentSpaceEyeVec);#else// 如果未啟用鏡面高光效果,忽略視線方向half3 ignore;TangentSpaceLightingInput(normalWorld, v.tangent, lightDir, 0, o.lightDir, ignore);#endif#else// 如果未啟用法線貼圖,直接使用光照方向o.lightDir = lightDir;o.normalWorld = normalWorld;// 如果啟用了鏡面高光效果,計算反射向量并存儲在 fogCoord 中#if SPECULAR_HIGHLIGHTSo.fogCoord.yzw = reflect(eyeVec, normalWorld);#endif#endif// 傳遞霧效信息UNITY_TRANSFER_FOG(o, o.pos);// 返回輸出結構體return o;
}
三、vertForwardAddSimple和vertForwardAdd對比
以下是 vertForwardAddSimple
和 vertForwardAdd
兩個頂點著色器函數的主要區別,分條列舉如下:
1. 輸出結構體類型
-
vertForwardAddSimple
:使用VertexOutputForwardAddSimple
作為輸出結構體。 -
vertForwardAdd
: 使用VertexOutputForwardAdd
作為輸出結構體。
2. 霧效處理方式
-
vertForwardAddSimple
:使用UNITY_TRANSFER_FOG
來傳遞霧效信息。UNITY_TRANSFER_FOG(o,o.pos);
-
vertForwardAdd
:使用UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC
來傳遞霧效信息。這個宏通常用于結合視線方向的霧效計算。UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o, o.pos);
3. 法線和切線空間處理
-
vertForwardAddSimple
:根據是否啟用了_NORMALMAP
宏來決定如何處理法線和切線空間。#ifdef _NORMALMAP#if SPECULAR_HIGHLIGHTSTangentSpaceLightingInput(normalWorld, v.tangent, lightDir, eyeVec, o.lightDir, o.tangentSpaceEyeVec);#elsehalf3 ignore;TangentSpaceLightingInput(normalWorld, v.tangent, lightDir, 0, o.lightDir, ignore);#endif#elseo.lightDir = lightDir;o.normalWorld = normalWorld;#if SPECULAR_HIGHLIGHTSo.fogCoord.yzw = reflect(eyeVec, normalWorld);#endif#endif
-
vertForwardAdd
:根據是否啟用了_TANGENT_TO_WORLD
宏來決定如何處理法線和切線空間。#ifdef _TANGENT_TO_WORLDfloat4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);o.tangentToWorldAndLightDir[0].xyz = tangentToWorld[0];o.tangentToWorldAndLightDir[1].xyz = tangentToWorld[1];o.tangentToWorldAndLightDir[2].xyz = tangentToWorld[2];#elseo.tangentToWorldAndLightDir[0].xyz = 0;o.tangentToWorldAndLightDir[1].xyz = 0;o.tangentToWorldAndLightDir[2].xyz = normalWorld;#endif
4. 視圖方向(視線方向)處理
-
vertForwardAddSimple
: 僅在啟用了SPECULAR_HIGHLIGHTS
宏時計算視線方向 (eyeVec
)#if SPECULAR_HIGHLIGHTShalf3 eyeVec = normalize(posWorld.xyz - _WorldSpaceCameraPos);#endif
-
vertForwardAdd
:始終計算視線方向 (eyeVec
) ,無論是否啟用了SPECULAR_HIGHLIGHTS
。o.eyeVec.xyz = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
5. 光照方向處理
-
vertForwardAddSimple
:計算光照方向 (lightDir
) 存儲在lightDir 。half3 lightDir = _WorldSpaceLightPos0.xyz - posWorld.xyz * _WorldSpaceLightPos0.w; #ifndef USING_DIRECTIONAL_LIGHTlightDir = NormalizePerVertexNormal(lightDir); #endif#ifdef _NORMALMAP #elseo.lightDir = lightDir; #endif
-
vertForwardAdd
: 計算光照方向存儲在tangentToWorldAndLightDir
float3 lightDir = _WorldSpaceLightPos0.xyz - posWorld.xyz * _WorldSpaceLightPos0.w;#ifndef USING_DIRECTIONAL_LIGHTlightDir = NormalizePerVertexNormal(lightDir);#endifo.tangentToWorldAndLightDir[0].w = lightDir.x;o.tangentToWorldAndLightDir[1].w = lightDir.y;o.tangentToWorldAndLightDir[2].w = lightDir.z;
6. 附加功能支持
-
vertForwardAddSimple
:不包含對視差貼圖的支持。 -
vertForwardAdd
:包含對視差貼圖的支持。#ifdef _PARALLAXMAPTANGENT_SPACE_ROTATION;o.viewDirForParallax = mul (rotation, ObjSpaceViewDir(v.vertex));#endif
7. 初始化和設置
-
vertForwardAddSimple
:- 初始化輸出結構體
VertexOutputForwardAddSimple
,并設置實例 ID 和立體視圖輸出。
- 初始化輸出結構體
-
vertForwardAdd
:- 初始化輸出結構體
VertexOutputForwardAdd
,并設置實例 ID 和立體視圖輸出。
- 初始化輸出結構體
8. 細節處理
-
vertForwardAddSimple
:-
在未啟用
_NORMALMAP
的情況下,直接將法線存儲在o.normalWorld
中,并在啟用了SPECULAR_HIGHLIGHTS
時計算反射向量。#ifdef _NORMALMAP//......#elseo.lightDir = lightDir;o.normalWorld = normalWorld;#if SPECULAR_HIGHLIGHTSo.fogCoord.yzw = reflect(eyeVec, normalWorld);#endif#endif
-
-
vertForwardAdd
:-
在未啟用
_TANGENT_TO_WORLD
的情況下,將法線存儲在o.tangentToWorldAndLightDir[2].xyz
中,并不進行額外的反射向量計算。#ifdef _TANGENT_TO_WORLD//...... #elseo.tangentToWorldAndLightDir[0].xyz = 0;o.tangentToWorldAndLightDir[1].xyz = 0;o.tangentToWorldAndLightDir[2].xyz = normalWorld; #endif
-
總結
特性/功能 | vertForwardAddSimple | vertForwardAdd |
---|---|---|
輸出結構體 | VertexOutputForwardAddSimple | VertexOutputForwardAdd |
霧效處理 | UNITY_TRANSFER_FOG | UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC |
法線和切線空間處理 | 根據 _NORMALMAP 處理 | 根據 _TANGENT_TO_WORLD 處理 |
視線方向處理 | 僅在 SPECULAR_HIGHLIGHTS 時計算 | 始終計算并存儲 |
光照方向處理 | 直接存儲或傳遞給 TangentSpaceLightingInput | 分別存儲在 tangentToWorldAndLightDir 的 .w 分量中 |
支持視差貼圖 | 否 | 是 (通過 _PARALLAXMAP 宏) |
反射向量計算 | 在 SPECULAR_HIGHLIGHTS 時計算 | 不計算 |
四、fragForwardAddSimpleInternal
half4 fragForwardAddSimpleInternal (VertexOutputForwardAddSimple i)
{// 應用抖動交叉淡入效果,以減少在低分辨率下可能出現的鋸齒現象。UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);// 根據傳入的頂點信息,設置片段的基本材質屬性(如漫反射顏色、鏡面反射顏色和平滑度)。FragmentCommonData s = FragmentSetupSimpleAdd(i);// 計算直接光源下的BRDF(雙向反射分布函數),考慮了材質的漫反射顏色、鏡面反射顏色和平滑度以及視角與光方向之間的關系。half3 c = BRDF3DirectSimple(s.diffColor, s.specColor, s.smoothness, dot(REFLECTVEC_FOR_SPECULAR(i, s), i.lightDir));// 如果啟用了鏡面高光,則將計算出的顏色乘以光源的顏色,否則使用預乘了光顏色的diffColor。#if SPECULAR_HIGHLIGHTS // else diffColor has premultiplied light colorc *= _LightColor0.rgb;#endif// 獲取當前片段的光照衰減,并將其應用于顏色值上。同時,根據法線和光方向間的夾角調整最終顏色強度。UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld)c *= atten * saturate(dot(LightSpaceNormal(i, s), i.lightDir));// 應用霧效,使得物體在遠處時逐漸融合到背景色中。這里特別地,霧效會趨向于黑色。UNITY_APPLY_FOG_COLOR(i.fogCoord, c.rgb, half4(0,0,0,0)); // fog towards black in additive pass// 返回最終的顏色結果,包括alpha通道的信息。return OutputForward (half4(c, 1), s.alpha);
}
五、fragForwardAddSimpleInternal和fragForwardAddInternal對比
以下是 fragForwardAddSimpleInternal
和 fragForwardAddInternal
兩個片段著色器函數的主要區別,如下:
1. 輸入參數類型:
fragForwardAddSimpleInternal
接受的參數類型是VertexOutputForwardAddSimple
。fragForwardAddInternal
接受的參數類型是VertexOutputForwardAdd
。
2. 材質屬性設置:
fragForwardAddSimpleInternal
使用FragmentSetupSimpleAdd(i)
來設置基本的材質屬性(如漫反射顏色、鏡面反射顏色和平滑度)。fragForwardAddInternal
使用FRAGMENT_SETUP_FWDADD(s)
來設置材質屬性,并且包含更多的細節處理(如視角向量和法線向量)。
3. 光照計算方法:
fragForwardAddSimpleInternal
使用BRDF3DirectSimple
函數來計算直接光照下的顏色貢獻。這個函數相對簡單,只考慮了基本的BRDF計算。fragForwardAddInternal
使用UNITY_BRDF_PBS
函數來進行更復雜的物理基礎渲染(PBR)計算,包括漫反射和鏡面反射的組合,并考慮了間接光照(雖然這里設為noIndirect
)。
4. 光源衰減和法線方向處理:
fragForwardAddSimpleInternal
直接使用UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld)
獲取光源衰減,并通過saturate(dot(LightSpaceNormal(i, s), i.lightDir))
計算法線與光方向之間的夾角影響最終顏色。fragForwardAddInternal
在獲取光源衰減后,使用AdditiveLight (IN_LIGHTDIR_FWDADD(i), atten)
創建一個UnityLight
結構體,包含了光源方向和衰減信息。
5. 立體視覺支持:
fragForwardAddSimpleInternal
沒有涉及立體視覺的支持。fragForwardAddInternal
包含了對立體視覺的支持,通過UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
來設置立體眼索引。
6. 霧效應用:
fragForwardAddSimpleInternal
使用UNITY_APPLY_FOG_COLOR(i.fogCoord, c.rgb, half4(0,0,0,0));
直接應用霧效。fragForwardAddInternal
首先通過UNITY_EXTRACT_FOG_FROM_EYE_VEC(i);
提取霧坐標,然后使用UNITY_APPLY_FOG_COLOR(_unity_fogCoord, c.rgb, half4(0,0,0,0));
應用霧效。
7. 輸出函數調用:
fragForwardAddSimpleInternal
最終調用OutputForward (half4(c, 1), s.alpha);
返回結果。fragForwardAddInternal
最終調用OutputForward (c, s.alpha);
返回結果,注意這里的c
已經是一個half4
類型。
總結來說,fragForwardAddSimpleInternal
更加簡化,適用于基本的光照和材質計算;而 fragForwardAddInternal
則提供了更復雜和全面的物理基礎渲染(PBR),并且支持更多高級特性如立體視覺等。這兩者的選擇取決于具體的渲染需求和性能考量。