Unity Standard Shader 解析(一)之ForwardBase(标准版)

一、ForwardBase

csharp 复制代码
		//  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中的vertBasefragBase

以下是UnityStandardCoreForward.cginc的源码,

csharp 复制代码
// 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.cgincvertForwardBasefragForwardBaseInternal

二、vertForwardBase

csharp 复制代码
// ------------------------------------------------------------------
//  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

csharp 复制代码
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);
}

四、相关宏定义

五、相关函数定义

相关推荐
启诚科技2 小时前
虚拟现实--->unity学习
学习·unity
WarPigs2 小时前
Unity声音管理系统笔记
笔记·unity·音频
虾球xz3 小时前
游戏引擎学习第194天
c++·学习·游戏引擎
Kermit20233 小时前
unity一个图片的物体,会有透明的效果
unity
咩咩觉主4 小时前
Unity 一个丝滑的3D下--XY轴2D平台跳跃--控制器模板(FSM)
3d·unity·游戏引擎
avi911113 小时前
Unity打包崩溃SRP-URP-管线的问题:Shader::SRPBatcherInfoSetup()
unity·android studio·调试·crash·崩溃
徐子竣21 小时前
Unity编辑器功能及拓展(2) —Gizmos编辑器绘制功能
unity·编辑器·游戏引擎
Tatalaluola1 天前
【Unity】 鼠标拖动物体移动速度跟不上鼠标,会掉落
学习·unity·c#·游戏引擎
徐子竣1 天前
Unity编辑器功能及拓展(1) —特殊的Editor文件夹
unity·编辑器·游戏引擎
DanmF--1 天前
Unity中UDP异步通信常用API使用
网络·网络协议·unity·udp·c#