天天看点

unity urp内置lit材质源码解析(上)

之前我发布过一篇对urp的内置shader lit的结构解析,发现自己说的也不完善,这次直接对源码进行一个解析,并提升一下自己的记忆。

如果你找不到这个shader,那么就有可能你不是urp渲染管线。自己搜索。

在前向渲染pass里面,我们可以看到,urp里面已经不再使用cg语言,而是改用了HLSL渲染语言,其实没大差别,只是里面的一些函数,和一些实现方法出现了变动。

#pragma exclude_renderers gles gles3 glcore 上来你会看到这么一句话,就是后面那几个渲染器不会生成对应渲染器的shader。这里再补一句,我们写的shader不是最终在程序上运行的,因为为了兼容多平台,我们只需要写一套shader,然后unity会根据我们写的shader生成对应平台的运行的shader。

接下来,就是一大堆的宏:

unity urp内置lit材质源码解析(上)
unity urp内置lit材质源码解析(上)

unity会根据你设置的宏,生成相应的shader变体,就相当于一个开关,所以,如果宏过多的话,shader变体会几何倍增,那么占用的内存也会集合倍增。shader的变体其实就是实际运行时的shader,你开关一个宏,它会生成相应的两个shader。如果宏过多,它会生成两套,和其它宏叠加的,你会发现你的shader变体巨多,在哪里看你的变体数量:

unity urp内置lit材质源码解析(上)

你点击后面的show,会打开有宏关键字的变体列表。

unity urp内置lit材质源码解析(上)

这里定义了你的顶点着色器和片元着色器。渲染的时候,渲染引擎会找到相应的shader去运行。

unity urp内置lit材质源码解析(上)

最后,引入了相应的代码,#include就是引入相应的文件代码。

litInput.hlsl 里面主要声明了变量,还有就是初始化pbr使用的表面数据。

LitForwardPass.hlsl 里面就是定义顶点着色器和片元着色器函数和其它运算函数,以及一些获取数据的函数。接下来,我们一个一个的解析。

LitInput.hlsl

unity urp内置lit材质源码解析(上)

开头的这个是为了防止多次引入,所以,设置了一个宏,这个宏只是为了防止多次引用,占用多余内存,不会生成额外变体。

unity urp内置lit材质源码解析(上)

再下面就是引入了多个文件,都是一些内置的。

Core.hlsl 就是unity官方内置的一些函数和宏,方便我们使用。

CommonMaterial.hlsl 定义了一些pbr材质使用的函数

SurfaceInput.hlsl 定义了表面材质通用的属性和函数,比如基础贴图什么的,都在这里面定义的,里面的函数也都是通用的,比如

unity urp内置lit材质源码解析(上)

这个函数,可以让我们求出当前的透明度,以及是否需要裁切。

ParallaxMapping.hlsl 这个是时差偏移的相应函数

DBuffer.hlsl 这个是使用延迟渲染管线用到的函数,我们前向渲染不需要。

unity urp内置lit材质源码解析(上)

这里是如果你使用了细节贴图,将根据宏定义一个新的宏,防止后面判断宏的时候麻烦。

unity urp内置lit材质源码解析(上)

这些就是内置pbr材质所需要的一些属性,为什么要用CBUFFER括起来,是因为为了使用urp的新功能srp合批,srp合批优势在于,我们不再需要保持相同的材质球也能合批,只要使用了相同的shader,就可以实现srp合批。

ps:这里需要提醒一句,如果你要实现srp合批,那么,你的shader里面的pass里面使用的CBUFFER必须保持一致,要不然你shader上面就会提醒你这个:

unity urp内置lit材质源码解析(上)

再往下面看:

unity urp内置lit材质源码解析(上)

这里声明的是对正交相机使用的一些常量,我一般修改的时候,会把这一段删除掉。

unity urp内置lit材质源码解析(上)

这里声明了pbr的一些贴图,urp里面每个贴图需要携带其贴图的采样器,这个采样器就是你在贴图上面设置的贴图拼接模式,

unity urp内置lit材质源码解析(上)

如果你不想使用贴图的,而是想固定住它的采样模式,不管它贴图怎么设置的,我们可以直接写内置的这些采样器的命名:

unity urp内置lit材质源码解析(上)

此截图至:https://blog.csdn.net/lsccsl/article/details/118086659

接下来

unity urp内置lit材质源码解析(上)

这段就是,根据你使用的工作流,来实现不同的SAMPLE_METALLICSPECULAR函数定义,供后面使用,如果你是高光工作流,那么我引擎将去获取高光贴图,如果不是,那么引擎将使用金属度贴图。我们一般都是使用自己定义的工作流,一般还是金属工作流,比如将 ao roughness metallic 设置道一张一张贴图里面。(一般都把粗糙度的使用G通道,意思是G通道的更准确一些,毕竟粗糙度的渐变比较多,ao只是烘焙的环境遮蔽,金属度一般不是0就是1,要求最低)

unity urp内置lit材质源码解析(上)

这个函数,就和上面定义的函数呼应上了,这个函数,主要是通过你设置的数据来求出当前材质的高光光滑度的。你可以打开一个lit材质看一下

unity urp内置lit材质源码解析(上)

我们可以在这里选择相应的工作流,然后选择unity也非常善解人意的让你可以选择你的光滑度是在基础贴图里面,还是在定义的高光颜色贴图a通道里面(高光工作流)或者金属度贴图a通道里面(金属工作流)。然后,我们再看这个函数,就明了了很多,如果你设置了金属高光光滑度贴图,我们将获取金属贴图,然后根据你设置从哪个a通道去获取高光光滑度进行设置。如果没有设置,我们将判断你是哪个工作流,获取高光颜色,然后再根据你的选择生成高光光滑度。

unity urp内置lit材质源码解析(上)

这个就简单了,获取烘焙的AO贴图,用了g通道的值,并且根据你设置的AO强度,和白色进行了插值。

unity urp内置lit材质源码解析(上)

这个是生成涂层的遮罩和涂层光滑度,返回二维向量。如果你没设置贴图,或者没开启涂层,那么将返回默认值,也就意味着没有开启。

unity urp内置lit材质源码解析(上)

这个是获取视差偏移的值,转换到uv偏移上面,时差偏移这里就不讲解实现原理了。

unity urp内置lit材质源码解析(上)

这里是进行缩放细节贴图的基本色。

unity urp内置lit材质源码解析(上)

这个是混合细节贴图基本色和默认的基本色。

unity urp内置lit材质源码解析(上)

这里是细节法向和默认法向的混合。

这里多提一嘴,细节贴图的作用,细节贴图主要是为了展现物体的细节,如果将细节直接烘焙到基本颜色贴图和基本法向贴图上,那么要求基本贴图的精度以及分辨率要高,降低渲染性能,增加内存占用。所以,我们通常会使用额外的细节贴图,来进行重复使用,来实现细节表现,比如放大以后,皮肤的细节。

unity urp内置lit材质源码解析(上)

这个是初始化pbr表面材质所需数据的函数,每个pass渲染中都需要使用。如果你需要Lit的shader自定义,那么需要保留这个函数,就是这个LitInput函数实现是shader里面的多个pass都可以引入的那种。所以,我们需要保证SurfaceData数据的统一

unity urp内置lit材质源码解析(上)

看一下函数内部的运算。

unity urp内置lit材质源码解析(上)

首先实现了基本色和透明度。

unity urp内置lit材质源码解析(上)

然后调用上面的SampleMetallicSpecGloss函数,获取到高光颜色和光滑度。

随后计算出来了最终基本色,就是贴图颜色*基本色颜色。

后面就是根据工作流,设置金属度和高光颜色。高光工作流没有金属度一说,所以设置了1,颜色是从函数里面算出来的颜色。金属工作流,r通道就是金属度,高光颜色直接设置0.

然后下面就是直接从specGloss的a通道获取到光滑度(和粗糙度相反)

unity urp内置lit材质源码解析(上)

然后从贴图上获取到切线空间的法向。

unity urp内置lit材质源码解析(上)

获取烘焙的AO。

如果你设置了自发光,将获取到自发光的颜色。

unity urp内置lit材质源码解析(上)

如果你有涂层,那么我们将计算涂层的遮罩和涂层的光滑度。

unity urp内置lit材质源码解析(上)

如果你设置的细节贴图,那么我们将计算细节相关,并和基本的贴图进行融合。

由于篇幅比较长,这一篇就先解析到这吧。具体实现留到下一篇。

继续阅读