Unity shader内置standard代码解析

news2024/11/20 15:16:40

最近有相关需求制作,所以这里编写一个文档,方便后续的流程查看。

下载源码

由于unity内置的shader是无法查看源码的,你需要去官网下载对应版本内置源码查看
在这里插入图片描述
在引擎下载那里,会有一个Built in Shaders,下载
在这里插入图片描述
打开以后,就是对应的shader
在这里插入图片描述
内置的shandard在DefaultResourcesExtra目录内,打开便是。

shader解析

Standard里面分了两套,一套正常的,一套精简版的,
在这里插入图片描述
这两套渲染的切换是通过设置shader的lod进行切换的。
每个shader下面由5个pass组成(简化版的不支持延迟渲染)

  1. 前向渲染主光源
  2. 前向渲染副光源
  3. 阴影渲染
  4. 延迟渲染
  5. 烘焙
    在这里插入图片描述

简化版本的渲染也不支持视差偏移,它们是通过宏去控制的,更多不同在渲染代码内部。

ForwardBase 和ForwardAdd引用的一套渲染逻辑,然后通过定义的宏和调用不同的顶点/片元着色器函数来区分到底是base还是add。

base的是这样在这里插入图片描述

add是这样
在这里插入图片描述
它们都引用的UnityStandardCoreForward渲染逻辑

在这里插入图片描述
在这个文件里面,是一些主要函数的定义,区分是否为简化的shader,如果简化的shader,则引入简化的库文件,非简化,则引入了UnityStandardCore.cginc,在这里定义了pass里面调用的顶点着色器和片元着色器函数,函数内直接调用了对应的UnityStandardCore库里的函数,这里也是standard的核心代码。
在这里插入图片描述
上面还引入了UnityStandardConfig.cginc,这个文件则是一些配置,主要定义的宏,抽几个比较重要的
下面定义了立体图贴图的曝光度以及lod层级数
在这里插入图片描述
定义brdf GGX
在这里插入图片描述

UnityStandardCore.cginc

里面代码的一些库的引用,核心库也是基于这些库实现的最终渲染
在这里插入图片描述
ForwardBase渲染主要就是调用了

VertexOutputForwardBase vertBase (VertexInput v) { return vertForwardBase(v); }
half4 fragBase (VertexOutputForwardBase i) : SV_Target { return fragForwardBaseInternal(i); }

VertexInput 就是在UnityStandardInput.cginc内实现的需要传入到顶点着色器的数据
VertexOutputForwardBase 则是从顶点传入到片元的数据
在这里插入图片描述
vertForwardBase 函数里对位置,UV,法向等做了一些处理,更复杂的还有lightmap的UV,还有视差偏移
在这里插入图片描述
重点函数,片元着色器fragForwardBaseInternal,有点代码越少越狠的节奏,后面我将一个个的函数解析
在这里插入图片描述

UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy)

这个是为了实现淡入淡出的效果
在这里插入图片描述
unity_DitherMask 为unity内置生成的抖动noise贴图
unity_LODFade 为需要设置的变量,在UnityShaderVariables.cginc里面定义

float4 unity_LODFade; // x is the fade value ranging within [0,1]. y is x quantized into 16 levels

FRAGMENT_SETUP(s)

这个函数主要是生成后续使用的数据FragmentCommonData,定义为FragmentSetup函数

#define FRAGMENT_SETUP(x) FragmentCommonData x = FragmentSetup(i.tex, i.eyeVec.xyz, IN_VIEWDIR4PARALLAX(i), i.tangentToWorldAndPackedData, IN_WORLDPOS(i));

i.tex 顶点着色器计算的uv
i.eyeVec.xyz 摄像机朝向
IN_VIEWDIR4PARALLAX(i) 摄像机朝向基于视差偏移的法向值
i.tangentToWorldAndPackedData 切线坐标系转世界坐标系矩阵 [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
IN_WORLDPOS(i) 渲染目标世界坐标位置

FragmentCommonData 则是返回从顶点着色器拿到的数据处理后的数据,后续获取通过s变量获取。oneMinusReflectivity 为1-反射率
在这里插入图片描述
然后就是函数FragmentSetup,设置数据,截图里面我也加了注释
在这里插入图片描述
这里主要讲的是UNITY_SETUP_BRDF_INPUT函数,它可以根据工作流去设置数据,有三种 SpecularSetup RoughnessSetup MetallicSetup分别对应 高光工作流 粗糙度工作里 金属度工作流,standard.shader里面定义了金属度工作流
在这里插入图片描述
如果没有定义的话,会切换高光工作流
在这里插入图片描述
由于我这里使用的是金属度工作流,这里讲解一些金属度工作流的相关内容,MetallicSetup函数,函数内有两个函数,第一个函数去获取贴图的值,第二个函数为计算漫反射颜色,镜面反射颜色和反射率
在这里插入图片描述
MetallicGloss内返回二维向量,x为金属度,y为光滑度,光滑度还可以选择是使用的_MetallicGlossMap的a通道还是_MainTex的a通道
在这里插入图片描述
DiffuseAndSpecularFromMetallic
unity_ColorSpaceDielectricSpec的值在线性空间中默认是 half4(0.04, 0.04, 0.04, 1.0 - 0.04),这也是物理渲染中默认反射率
在这里插入图片描述
根据金属度求出反射率
在这里插入图片描述

MainLight

    UnityLight mainLight = MainLight(); //主光源
    UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld); //合并阴影

UnityLight 结构里面有三个值 color 光的颜色 dir 光的朝向 ndotl 法向和光的点乘值(已弃用),MainLight函数里面就是获取第一盏灯的颜色和朝向
在这里插入图片描述
UNITY_LIGHT_ATTENUATION 计算阴影遮挡。会根据光的类型调用不同的函数,一般主光源都是平衡光,这里看一下平衡光的实现,代码在AutoLight.cginc里面
在这里插入图片描述
在AutoLight.cginc中,对多种情况的处理,比如屏幕空间阴影,包含烘焙阴影
在这里插入图片描述
这里我们看最简单的使用SHADOW_ATTENUATION生成的unitySampleShadow函数,这个函数会去获取shadowmap的值来做处理

#define UNITY_SAMPLE_SCREEN_SHADOW(tex, uv) UNITY_SAMPLE_TEX2DARRAY(tex, float3((uv).x / (uv).w, (uv).y / (uv).w, (float)unity_StereoEyeIndex)).r

在这里插入图片描述
这一块解析起来确实麻烦,如果你需要阴影的话,记得直接使用UNITY_LIGHT_ATTENUATION函数。第一个值就是阴影的值。

FragmentGI

全局光照,包含了lightmap,sh球谐光照,ibl等对物体影响的内容

UnityGI gi = FragmentGI(s, occlusion, i.ambientOrLightmapUV, atten, mainLight); //全局光照

UnityGI包含全局光照有光的数据,以及间接光的漫反射和镜面反射颜色
在这里插入图片描述
FragmentGI 函数主要是设置一些所需要的值,然后调用UnityGlobalIllumination生成最终所需的UnityGI数据
在这里插入图片描述
UnityGlossyEnvironmentSetup 主要是求出了两个值 SmoothnessToPerceptualRoughness是通过光滑度求出粗糙度,也就是1-光滑度,reflUVW,根据眼睛和法向求出反射方向
在这里插入图片描述
准备好需要的全局光照计算数据以后,就要开始调用UnityGlobalIllumination计算了,分别去计算间接光漫反射,以及间接光镜面反射
在这里插入图片描述
在间接光漫反射里面,考虑光照贴图和动态光照贴图,这个我在之前我的文章里面说过,这里就不再多解释。
在这里插入图片描述
解析一下,上图比较重要的几行代码:

o_gi.light.color *= data.atten;
o_gi.indirect.diffuse = ShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
o_gi.indirect.diffuse *= occlusion;

ShadeSHPerPixel 计算间接光漫反射,相对于lightmap里面获取的,它具有动态性。球谐光照是由七个四维向量组成,
在这里插入图片描述

由引擎设置参数。unity还兼容的3d纹理方式的SHEvalLinearL0L1_SampleProbeVolume
在这里插入图片描述
计算完成间接光漫反射以后,就是计算间接光镜面反射,在unity里面是通过,实现原理就是通过立方体贴图去拾取颜色作为镜面反射的颜色
在这里插入图片描述
里面主要的方法就是Unity_GlossyEnvironment,这个去拾取引擎设置的立方体贴图,并获取颜色
在这里插入图片描述
perceptualRoughnessToMipmapLevel就是粗糙度乘以LOD级数UNITY_SPECCUBE_LOD_STEPS,粗糙度越低,表示越光滑,那么lod层级就越低,图片拾取的也最清晰。
在这里插入图片描述
最后,将全局光的灯光颜色,间接光漫反射,间接光镜面反射计算完成,交给物理渲染BRDF函数实现最后的颜色。

UNITY_BRDF_PBS

half4 c = UNITY_BRDF_PBS(s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect); //基于物理的渲染

看代码,standard里面内置了三套方式,

  1. BRDF1_Unity_PBS 是基于物理的BRDF(Bidirectional Reflectance Distribution Function,双向反射分布函数)
  2. BRDF2_Unity_PBS 是基于极简的微表面理论的BRDF http://www.thetenthplanet.de/archives/255
  3. BRDF3_Unity_PBS 是不是微表面的基于修正归一化的 Blinn-Phong BRDF
    在这里插入图片描述
    这里,我只介绍质量最好的第一种BRDF1_Unity_PBS,看注释,也能了解到它的模型是如何计算的
    直接光漫反射 kD / pi
    直接光镜面反射 kS * (D * V * F) / 4
    最后乘以NdotL
    BRDF里面还有两种,一种是GGX的高光,另一种是老旧的BlinnPhong的
    在这里插入图片描述
    首先函数获取到需要用的数据,粗糙度,半角向量halfDir,NdotV,NdotL,NdotH,LdotV,LdotH
    在这里插入图片描述
    然后基于数据求直接光漫反射
    // Diffuse term
    half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;

求漫反射,还给注释迪士尼的漫反射必须除以PI,在函数外实现,貌似unity都亮了PI,所以不用除以PI了
在这里插入图片描述
然后解释了一下为什么不除以PI
在这里插入图片描述
接下来,先求出BRDF的D项和V项
在这里插入图片描述
然后在最后颜色合并的时候,求出菲涅尔项 F,怪不得之前听朋友说,unity的BRDF写的很难看,确实难看
在这里插入图片描述
最终计算出来了颜色加上自发光合并雾效,返回片元颜色
在这里插入图片描述

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

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

相关文章

刷一下算法

记录下自己的思路与能理解的解法,可能并不是最优解法,不定期持续更新~ 1.盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容…

AURIX TC3XX内存映射分析

TC3XX内存映射Features AURIX TC3xx系列设备的内存映射中包含的各个部分。这些内存部分在设备上有各自的用途和特性。以下是这些部分的一些概念解释: Program Flash Interface (PFI) 和 Program Flash Memory (PF) 是用来存储程序代码的闪存。即使在断电时&#xf…

【学习笔记】Java 一对一培训(3.1)Spring Boot介绍和基础

【学习笔记】Java 一对一培训(3.1)Spring Boot介绍和基础 关键词:Java、Spring Boot、Idea、数据库、一对一、培训、教学本文主要内容含Spring Boot相关的基础介绍、快速入门、Maven介绍、代码结构介绍、打包运行、配置介绍等计划1小时完成&…

TOTP算法实现

TOTP算法实现 1 什么是双因子认证(2FA)2 TOTP原理2.1 HOTP原理2.2 TOTP 3 实现参考文章 最近发现github天天给我发通知要启用双因子认证(2FA),受不了了只能想办法启用了。看到它支持采用基于TOTP算法的应用的认证方式&…

彻底搞懂线程池原理以及创建方式

1. 为什么要使用线程池 在实际使用中,线程是很占用系统资源的,如果对线程管理不善很容易导致系统问题。因此,在大多数并发框架中都会使用线程池来管理线程,使用线程池管理线程主要有如下好处: 降低资源消耗。通过复用…

Python机器学习实战-建立Gradient Boosting模型预测肾脏疾病(附源码和实现效果)

实现功能 建立Gradient Boosting模型预测肾脏疾病 实现代码 import pandas as pd import warnings warnings.filterwarnings("ignore") pd.set_option(display.max_columns, 26)#读取数据 df pd.read_csv("E:\数据杂坛\datasets\kidney_disease.csv") …

vMAP——论文解析

vMAP: Vectorised Object Mapping for Neural Field SLAM vMAP 是一个物体级稠密图 neural SLAM,每一个物体都用一个 mlp 来表征,而不需要 3D 先验。当 RGB-D 相机在没有任何先验信息的情况下时,vMAP 会即时检测物体 instance,并将…

在Ubuntu 18.04上支持C++17的std::filesystem的方法

在Ubuntu 18.04上通过命令sudo apt install gcc g安装的gcc/g版本为7.5,此版本并不直接支持filesystem,如下图所示: Ubuntu 18.04上的g 7.5支持experimental的filesystem,即std::experimental::filesystem,若想使Ubuntu 18.04支持…

购物系统设计与实现

目 录 1 绪 论 1 1.1 本课题研究的背景和意义 1 1.1.1 本课题研究的背景 1 1.1.2 本课题研究的意义 2 1.1.3 本课题的发展现状及前景 2 1.2 系统的实现任务 7 2 系统概述及实现技术介绍 8 2.1 网上商城简介 8 2.2 相关实现技术介绍 10 2.2.1 JSP语言及其特点 10 2.2.2 Dreamwe…

快速学会搭建微信小程序的基础架构

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 基础架构 构建界面 引入 uni-ui 组件库 组件自动引入 配置TS类型 状态管理 持久化 数据交互 请…

Unity中Shader特性PerRendererData

文章目录 前言一、优化前是对使用了相同材质球的不同物体间shader分别设置,比较消耗性能二、使用[PerRendererData]标签,可以在脚本中使用SetPropertyBlock()对使用同一材质球的不同物体进行修改其Shader属性 前言 Unity中Shader特性PerRendererData 一…

Python学习 -- 常用数据交换格式(CSV、XML、JSON)

数据交换格式是在不同系统之间交换数据时使用的一种标准化格式。在Python中,我们常用的数据交换格式有CSV、XML和JSON。本篇技术博客将介绍这三种数据交换格式的详细使用方法,并提供具体的代码案例,帮助初学者快速掌握这些格式的使用。 CSV&…

第二章 进程与线程 六、线程的实现方式和多线程模型

目录 一、线程的实现方式 1、用户级线程 2、内核级线程 二、多线程模型 注意: 1、一对一模型 (1)定义: (2)优点: (3)缺点: 2、多对一模型 (1&…

Linkerd的部署与入门--service mesh初步体验

Linkerd2初探 部署环境Linkerd简介安装Linkerd客户端在k8s上安装Linkerd控制平面(服务端)实验:数据平面代理注入demo应用安装viz插件(可视化面板)部署grafana 其他 部署环境 k8s环境: KIND 模拟kubernetes 1.21.1 kub…

【python】使用Reddit API爬取数据

这篇文章介绍如何使用reddit api获数据,文档地址如下:https://www.reddit.com/dev/api/ 首先需要创建应用,页面如下:https://www.reddit.com/prefs/apps 这里name随意填写,reditect uri随意写一个网址 如图所示,创建好应用以后,可以得到CLIENT_ID和SECRET_KEY: 编写代…

线性回归网络

李沐大神的《动手学深度学习》,是我入门机器学习的首课,因此在这里记录一下学习的过程。 线性回归的从零开始实现 线性回归是理解机器学习的基础,它经常用来表示输入和输出之间的关系。   线性回归基于几个简单的假设: 首先&am…

【计算机视觉】Vision and Language Pre-Trained Models算法介绍合集(一)

文章目录 一、ALIGN二、Contrastive Language-Image Pre-training(CLIP)三、Learning Cross-Modality Encoder Representations from Transformers(LXMERT)四、BLIP: Bootstrapping Language-Image Pre-training五、Vision-and-La…

Json-Jackson和FastJson

狂神: 测试Jackson 纯Java解决日期格式化 设置ObjectMapper FastJson: 知乎:Jackson使用指南 1、常见配置 方式一:yml配置 spring.jackson.date-format指定日期格式,比如yyyy-MM-dd HH:mm:ss,或者具体的…

机器学习 day35(决策树)

决策树 上图的数据集是一个特征值X采用分类值,即只取几个离散值,同时也是一个二元分类任务,即标签Y只有两个值 上图为之前数据集对应的决策树,最顶层的节点称为根节点,椭圆形节点称为决策节点,矩形节点称…

ffplay源码解析-FrameQueue队列

帧队列架构位置 结构体源码 FrameQueue结构体 /* 这是一个循环队列,windex是指其中的首元素,rindex是指其中的尾部元素. */ typedef struct FrameQueue {Frame queue[FRAME_QUEUE_SIZE]; // FRAME_QUEUE_SIZE 最大size, 数字太大时会占用大量的…