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

news2025/3/31 13:40:00

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

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

二、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);
}

四、相关宏定义

五、相关函数定义

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2323430.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Selenium Web自动化如何快速又准确的定位元素路径,强调一遍是元素路径

如果文章对你有用,请给个赞! 匹配的ChromeDriver和浏览器版本是更好完成自动化的基础,可以从这里去下载驱动程序: 最全ChromeDriver下载含win linux mac 最新版本134.0.6998.165 持续更新..._chromedriver 134-CSDN博客 如果你问…

鸿蒙-全屏播放页面(使用相对布局)---持续更新中

最终实现效果图: 实现步骤 创建FullScreenPlay.ets全品播放页面 并将其修改为启动页面。 全屏播放,屏幕必然横过来,所以要将窗口横过来。 编辑 src/main/ets/entryability/EntryAbility.ets 若写在/EntryAbility.ets中,则所有…

全面讲解python的uiautomation包

在常规的模拟鼠标和键盘操作,我们一般使用pyautogui,uiautomation模块不仅能直接支持这些操作,还能通过控件定位方式直接定位到目标控件的位置,而不需要自己去获取对应坐标位置。uiautomation模块不仅支持任意坐标位置截图&#x…

CentOS 7 源码安装libjsoncpp-1.9.5库

安装依赖工具 sudo yum install cmake make gcc cmake 需要升级至 3.8.0 以上可参考:CentOS安装CMakegcc 需要升级至9.0 以上可参考:CentOS 7升级gcc版本 下载源码 wget https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/1.9.5.…

备赛蓝桥杯之第十六届模拟赛第1期职业院校组第五题:回忆画廊

提示:本篇文章仅仅是作者自己目前在备赛蓝桥杯中,自己学习与刷题的学习笔记,写的不好,欢迎大家批评与建议 由于个别题目代码量与题目量偏大,请大家自己去蓝桥杯官网【连接高校和企业 - 蓝桥云课】去寻找原题&#xff0…

Windows下docker使用教程

docker安装 镜像制作镜像加载容器创建更新镜像导出镜像 Windows10安装dockerdocker image制作docker 镜像加载docker 容器创建更新imageimage 导出为.tar文件 #以Windows10 、11为例 linux和Windows区别在于docker安装的程序是哪个操作系统的,后面的内容其实不变 …

Java项目生成接口文档的方案

文章目录 问题:Java项目生成接口文档的方案方案一:Swagger3.0方案二:Apipost两者对比 问题:Java项目生成接口文档的方案 需求 1、需要生成生成时间,作者名称,项目名称,接口名称,请…

案例实践 | 招商局集团以长安链构建“基于DID的航运贸易数据资产目录链”

概览 案例名称 基于DID的航运贸易数据资产目录链 业主单位 招商局集团 上线时间 2024年10月 用户群体 供数用数企业和个人 用户规模 集团内20企业 案例背景 招商局集团深入落实“促进数据高效流通使用、赋能实体经济”精神,深化集团数字化水平&#xff0c…

2025年移动端开发性能优化实践与趋势分析

启动速度优化 本质:缩短首次可见帧渲染时间。 方法: iOS:利用Core ML本地模型轻量化部署,减少云端等待。Android:强制启用SplashScreen API,通过setKeepOnScreenCondition控制动画时长。冷启动需将耗时操…

Docker Compose介绍

基本概念 Docker-Compose是Docker官方的开源项目,负责实现对docker容器集群的快速编排。 可以这么理解,docker compose是docker提出的一个工具软件,可以管理多个docker容器组成一个应用,只需要编写一个YAML格式的配置文件docker…

头歌实践教学平台--【数据库概论】--SQL

一、表结构与完整性约束的修改(ALTER) 1.修改表名 USE TestDb1; alter table your_table rename TO my_table; 2.添加与删除字段 #语句1:删除表orderDetail中的列orderDate alter table orderDetail drop orderDate; #语句2:添加列unitPrice alter t…

算法基础——模拟

目录 1 多项式输出 2.蛇形方阵 3.字符串的展开 模拟,顾名思义,就是题⽬让你做什么你就做什么,考察的是将思路转化成代码的代码能⼒。这类题⼀般较为简单,属于竞赛⾥⾯的签到题(但是,万事⽆绝对&#xff…

【第30节】MFC编程:ListCtrl控件和TreeCtrl控件

目录 引言 一、高级控件ListCtrl 二、高级控件TreeCtrl 三、Shell控件 四、CImageList 五、综合代码示例 引言 在MFC编程里,高级控件能大幅提升应用程序的交互性与功能性。接下来,咱们会详细讲讲ListCtrl和TreeCtrl这两个高级控件。不仅会介绍它们…

JavaScript 手写 call、apply、bind 和 new

1. 手写 call 方法 核心思路:改变函数的 this 指向并立即执行,通过将函数临时挂载到目标对象上调用。 Function.prototype.myCall function (context, ...args) {// 如果 context 为 null 或 undefined,则默认为 windowcontext context |…

计算机网络基础:量子通信技术在网络中的应用前景

计算机网络基础:量子通信技术在网络中的应用前景 一、前言二、量子通信技术基础2.1 量子通信的基本概念2.2 量子通信的主要原理2.2.1 量子密钥分发(QKD)原理2.2.2 量子隐形传态原理三、量子通信技术的特点3.1 绝对安全性3.2 超高通信速率潜力3.3 抗干扰能力强四、量子通信技…

Postman 下载文件指南:如何请求 Excel/PDF 文件?

在 Postman 中进行 Excel/PDF 文件的请求下载和导出,以下是简明的步骤,帮助你轻松完成任务。首先,我们将从新建接口开始,逐步引导你完成整个过程。 Postman 请求下载/导出 excel/pdf 文件教程

Stereolabs ZED Box Mini:机器人与自动化领域的人工智能视觉新选择

在人工智能视觉技术快速发展的今天,其应用场景正在持续拓宽,从智能安防到工业自动化,从机器人技术到智能交通,各领域都在积极探索如何利用这一先进技术。而 Stereolabs 推出的ZED Box Mini,正是一款专为满足这些多样化…

arm之s3c2440的I2C的用法

基础概念 IC(Inter-Integrated Circuit)又称I2C,是是IICBus简称,所以中文应该叫集成电路总线。 IIC的总线的使用场景,所有挂载在IIC总线上的设备都有两根信号线,一根是数据线SDA,另一 根是时钟…

redis部署架构

一.redis多实例 如上图所示,我们经常使用实例的端口号来作为实例的安装目录名称。 1.创建实例安装目录 如上图所示,这是创建实例的安装目录, 2.拷贝实例的配置文件 如上图所示,将redis解压目录下的配置文件拷贝到对应的conf目录…

深入理解指针(4)(C语言版)

文章目录 前言一、回调函数是什么(一)定义(二)工作原理(三)应用场景 二、qsort举例(一)qsort函数简介(二)比较函数的定义(三)使用示例…