当前位置: 首页 > news >正文

Unity Standard Shader 解析(四)之ForwardAdd(简化版)

一、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中的vertAddfragAdd

以下是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.cgincvertForwardAddSimplefragForwardAddSimpleInternal

二、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对比

以下是 vertForwardAddSimplevertForwardAdd 两个顶点着色器函数的主要区别,分条列举如下:

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
      

总结

特性/功能vertForwardAddSimplevertForwardAdd
输出结构体VertexOutputForwardAddSimpleVertexOutputForwardAdd
雾效处理UNITY_TRANSFER_FOGUNITY_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对比

以下是 fragForwardAddSimpleInternalfragForwardAddInternal 两个片段着色器函数的主要区别,如下:

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),并且支持更多高级特性如立体视觉等。这两者的选择取决于具体的渲染需求和性能考量。

http://www.dtcms.com/a/306651.html

相关文章:

  • 机器视觉halcon7-缺陷检测
  • SpringCloud -- MQ高级
  • 关于获取某目录及子目录下所有文件且不包含隐藏文件
  • dify + mcp 实现图片 ocr 识别
  • Apache RocketMQ 的核心概念(Core Concepts)
  • 解决 Node.js 托管 React 静态资源的跨域问题
  • SpringBoot之整合SSM步骤
  • 基因组选择育种-2.3多性状与多组学整合GS-GWAS
  • Python 使用pandas库实现Excel字典码表对照自动化处理
  • 从单体到分布式:解锁架构进化密码
  • MS Access 数据库修复:修复损坏的 MDB 文件的快速指南
  • llama factory本地部署常见问题
  • 大模型的开发应用(二十):AIGC原理
  • 从数据到预测:InfluxDB+Prophet时间序列分析案例实战
  • Promise完全体总结
  • 会吸的簸箕专利拆解:迷你真空组件的吸力控制与吸入口设计原理
  • React 编程式导航
  • 基于 Flask 和 MySQL 的期货数据分析系统
  • 5.Origin2021如何绘制柱状+折线双Y轴图?
  • Gemini CLI 怎么保存会话?两个命令解决 AI 失忆问题
  • 嵌入式开发学习———Linux环境下数据结构学习(五)
  • 一个使用共享内存进行进程间通信的程序,主要功能是创建并读取共享内存中的数据。具体作用如下:
  • 《Flutter篇第二章》MasonryGridView瀑布流列表
  • 机器视觉引导机器人修磨加工系统助力芯片封装
  • 机器人在动态表面上行走的强化学习研究
  • Rust在土木工程中的创新应用
  • Kotlin -> Kotlin Lambda 表达式与 Function 接口的关系
  • EC2 Amazon Linux 快速安装Airbyte (abctl)
  • B 站排名优化:不同领域的差异化实战策略
  • Ubuntu24.04换源方法