一、ForwardBase
// Base forward pass (directional light, emission, lightmaps, ...)
Pass
{
Name "FORWARD"
Tags { "LightMode" = "ForwardBase" }
Blend [_SrcBlend] [_DstBlend]
ZWrite [_ZWrite]
CGPROGRAM
#pragma target 3.0
// -------------------------------------
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature_fragment _EMISSION
#pragma shader_feature_local _METALLICGLOSSMAP
#pragma shader_feature_local_fragment _DETAIL_MULX2
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature_local_fragment _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature_local_fragment _GLOSSYREFLECTIONS_OFF
#pragma shader_feature_local _PARALLAXMAP
#pragma multi_compile_fwdbase
#pragma multi_compile_fog
#pragma multi_compile_instancing
// 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 vertBase
#pragma fragment fragBase
#include "UnityStandardCoreForward.cginc"
ENDCG
}
引用了UnityStandardCoreForward.cginc
中的vertBase
和fragBase
以下是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)特性,包含金属度、粗糙度等完整材质属性计算
先看标准版的
引用了UnityStandardCore.cginc
的vertForwardBase
和fragForwardBaseInternal
二、vertForwardBase
// ------------------------------------------------------------------
// Base forward pass (directional light, emission, lightmaps, ...)
struct VertexOutputForwardBase
{
UNITY_POSITION(pos);
float4 tex : TEXCOORD0;
float4 eyeVec : TEXCOORD1; // eyeVec.xyz | fogCoord
float4 tangentToWorldAndPackedData[3] : TEXCOORD2; // [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
half4 ambientOrLightmapUV : TEXCOORD5; // SH or Lightmap UV
UNITY_LIGHTING_COORDS(6,7)
// next ones would not fit into SM2.0 limits, but they are always for SM3.0+
#if UNITY_REQUIRE_FRAG_WORLDPOS && !UNITY_PACK_WORLDPOS_WITH_TANGENT
float3 posWorld : TEXCOORD8;
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
// 顶点着色器函数:将输入的顶点数据转换为前向渲染所需的输出结构
VertexOutputForwardBase vertForwardBase (VertexInput v)
{
// 设置实例ID(用于GPU实例化渲染)
UNITY_SETUP_INSTANCE_ID(v);
// 声明输出结构变量
VertexOutputForwardBase o;
// 初始化输出结构中的各个字段(宏内部会设置默认值)
UNITY_INITIALIZE_OUTPUT(VertexOutputForwardBase, o);
// 将输入的实例ID传递给输出(保证实例数据一致)
UNITY_TRANSFER_INSTANCE_ID(v, o);
// 初始化立体渲染相关数据(用于VR或立体渲染)
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
// 计算世界空间位置:将顶点从对象空间转换到世界空间
float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
// 如果要求片元阶段需要世界空间位置数据
#if UNITY_REQUIRE_FRAG_WORLDPOS
// 如果需要将世界位置打包到切线数据的w分量中(节省传输带宽)
#if UNITY_PACK_WORLDPOS_WITH_TANGENT
o.tangentToWorldAndPackedData[0].w = posWorld.x;
o.tangentToWorldAndPackedData[1].w = posWorld.y;
o.tangentToWorldAndPackedData[2].w = posWorld.z;
#else
// 否则直接将世界位置赋值给输出结构的posWorld变量(仅XYZ分量)
o.posWorld = posWorld.xyz;
#endif
#endif
// 将顶点从对象空间转换到裁剪空间,用于后续屏幕映射
o.pos = UnityObjectToClipPos(v.vertex);
// 计算纹理坐标
o.tex = TexCoords(v);
// 计算视线向量:从世界空间摄像机位置指向顶点位置,并进行归一化
o.eyeVec.xyz = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
// 计算世界空间法线:将顶点法线从对象空间转换为世界空间
float3 normalWorld = UnityObjectToWorldNormal(v.normal);
#ifdef _TANGENT_TO_WORLD
// 如果定义了将切线转换到世界空间,则计算切线方向
float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
// 计算切线到世界空间的变换矩阵,依据法线、切线方向和切线的符号(w分量)
float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
// 将变换矩阵的各行存入输出结构的tangentToWorldAndPackedData数组中(XYZ分量)
o.tangentToWorldAndPackedData[0].xyz = tangentToWorld[0];
o.tangentToWorldAndPackedData[1].xyz = tangentToWorld[1];
o.tangentToWorldAndPackedData[2].xyz = tangentToWorld[2];
#else
// 如果没有使用切线到世界空间的转换,直接将输出中第三组数据设置为法线
o.tangentToWorldAndPackedData[0].xyz = 0;
o.tangentToWorldAndPackedData[1].xyz = 0;
o.tangentToWorldAndPackedData[2].xyz = normalWorld;
#endif
// 为阴影接收做数据传递:将第二套UV(v.uv1)传递给输出,用于光照和阴影计算
UNITY_TRANSFER_LIGHTING(o, v.uv1);
// 计算环境光或光照贴图的UV(用于全局光照信息)
o.ambientOrLightmapUV = VertexGIForward(v, posWorld, normalWorld);
#ifdef _PARALLAXMAP
// 如果启用了视差贴图效果,则进行切线空间旋转(通常用于计算视差效果)
TANGENT_SPACE_ROTATION;
// 计算视差用的视线方向(在物体空间),通过旋转矩阵转换
half3 viewDirForParallax = mul(rotation, ObjSpaceViewDir(v.vertex));
// 将视差计算结果存入切线数据的w分量中(覆盖之前的打包数据)
o.tangentToWorldAndPackedData[0].w = viewDirForParallax.x;
o.tangentToWorldAndPackedData[1].w = viewDirForParallax.y;
o.tangentToWorldAndPackedData[2].w = viewDirForParallax.z;
#endif
// 传递雾效信息:结合雾效和视线数据,计算最终雾信息
UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o, o.pos);
// 返回最终的顶点输出数据
return o;
}
三、fragForwardBaseInternal
half4 fragForwardBaseInternal (VertexOutputForwardBase i)
{
// 应用抖动交叉淡化(用于消除伪影或提高过渡平滑性),基于屏幕空间位置 i.pos.xy
UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
// 设置片元阶段所需的局部变量结构 s(从顶点到片元传递的光照和材质数据)
FRAGMENT_SETUP(s)
// 设置实例ID,用于支持GPU实例化渲染
UNITY_SETUP_INSTANCE_ID(i);
// 设置立体渲染后处理的眼睛索引(用于VR或立体渲染)
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
// 获取主光源(通常为场景中的定向光)
UnityLight mainLight = MainLight ();
// 计算主光源对当前片元的光照衰减,基于世界空间位置 s.posWorld
UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld);
// 计算遮蔽值,使用片元的纹理坐标 i.tex.xy(例如环境遮蔽、AO等效果)
half occlusion = Occlusion(i.tex.xy);
// 计算全局光照(GI)信息,传入基本材质参数、遮蔽值、环境/光照贴图UV、衰减值和主光源信息
UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);
// 使用基于物理的BRDF计算片元颜色
// 参数包括漫反射色 s.diffColor、镜面反射色 s.specColor、反射率的补值 s.oneMinusReflectivity、
// 表面光滑度 s.smoothness、世界空间法线 s.normalWorld、观察方向(负的眼向量 -s.eyeVec),
// 以及来自主光源和间接光照的光照信息
half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
// 添加自发光(Emission)效果,基于片元的纹理坐标 i.tex.xy
c.rgb += Emission(i.tex.xy);
// 从顶点输出中提取雾效所需的相关数据(例如雾的深度信息)
UNITY_EXTRACT_FOG_FROM_EYE_VEC(i);
// 根据计算出的雾坐标 _unity_fogCoord 对片元颜色进行雾效混合
UNITY_APPLY_FOG(_unity_fogCoord, c.rgb);
// 调用 OutputForward 函数输出最终的片元颜色,同时传递 alpha 值
return OutputForward (c, s.alpha);
}