欢迎光临散文网 会员登陆 & 注册

URP | PBR材质(三)-自定义PBR

2022-08-18 21:54 作者:那个人真狗  | 我要投稿

内容偏多

注意 因为太复杂,本人才疏学浅有很多错误的地方,

  • 目的,以Unity默认URP Shader效果为参考效果。

基础公式

PBR基础公式

光照的公式我们可以总结为:

最终结果 = 直接光的漫反射 + 直接光的高光反射  + 间接光的高光反射 + 间接光的漫反射 + 其他光照效果(自发光什么什么等)

  • D   法线分布函数:描述微观法线N和半角向量H的趋同性比重。粗糙度越低,物体光滑度越高,

  • G  高光

  • F  菲尼尔

PBR 流程

  • Metallic粗糙度工作流

    贴图 (纹理,法线,环境遮蔽,金属度,粗糙度,自发光)

  • Specular工作流

    纹理,法线,环境遮蔽,高光,自发光

我们这次以及金属粗糙度光照流程。

准备基础Shader

  • 准备Shader ,默认Shader 有基础属性。

  • 我们前输入需要准备的贴图 变量



这里定义 颜色贴图,法线贴图,Mask贴图,

Mask 贴图

  • 金属度贴图储存在 = R通道

  • 粗糙度贴图储存到 =  G通道

  • AO贴图储存到 =  B通道

  • 自发光贴图储存到 = Alpha

目前我们需要数值,后面替换成贴图。

  • 声明定义变量,贴图

  • 片元着色器阶段输出

输出贴图颜色

  • 输出颜色贴图

计算法线

  • 因为我们使用的是法线贴图,我们在顶点着色器阶段求出的是模型的法线。

    我们需要把这两融合起来。

    在顶点着色器简单求出切线,副切线等。

  • 对应的片元结构体中输出

  • 在片元着色器阶段计算出法线

  • 效果

  • 这样N就计算完成

  • 测试 NormalScale 控制法线强度是否起作用。

扩展 UnpackNormalScale 函数

UnpackNormalScale 法线纹理采样

首先呢,我们都知道法线是一个三维向量,其每个分量的范围都是(-1,1),但在我们存储的法线图中每个通道的范围是(0,1),这其中存在一个转换关系。以R通道分量为例:

所以逆过程就是先乘以2再加上1

再者,Unity对法线贴图的压缩算法采用的格式被称为DXT5nm,这种格式的突出特点是利用A通道存储x分量,利用G通道存储y分量,因为这两个通道的bit位数最多(RGBA分别为5,6,5,8)。所以需要我们反映射,至于Z分量,由于法线向量是单位向量,所以我们可以利用几何关系求得Z分量。

环境准备

  • 准备10个球体,10个材质球

  • 指定基础贴图,颜色贴图,法线贴图,粗糙度,金属度使用数值调整。

直接光高光反射部分

镜面反射部分

镜面反射部分包含三个函数   D  F  G ,分母部分还有一个标准化因子. $4(w_o ⋅ n)(w_i ⋅ n)$

高光反射 | D

  • 法线分布函数GGX(Normal Distribution Function)

  • 它是描述微观法线N和半角向量H的趋同性比重。

实现粗糙度高光过程

  • 在Shader里定义一个函数,计算D

    我们这里使用上面的12shader

这个函数是在计算D    需要输入2个变量,一个是NdotH ,  一个是粗糙度变量。

  • 需要的基础数据

  • 我们需要计算出 NdotH

在片元着色器阶段获取灯光信息,归一化传过来的的N V,使用V + L计算出H

  • 在定义一个变量扩展粗糙度

目前需要对粗糙度进行映射处理

这里 1 - _Roughness  反向粗糙度 。

单独输出D

  • 输出看一下结果,

    现在调整粗糙度就可以看到高光的变化了。

注意: 这里是高光范围,所以需要限制最小值也是有高光点的。

几何遮蔽 | G

几何函数G,(Geometry function)

即核心方程的G项,它是描述入射射线(即光照方向)和出射射线(视线方向)被自己的微观几何形状遮挡的比重。

         

G%3D%20%E5%87%A0%E4%BD%95%E5%87%BD%E6%95%B0%20%20%20%20%20%20%20%20%20%20%20%3D%20%20G_%7BSchlickGGX%7D(n%2Cv%2Ck)%3D%5Cfrac%20%7Bn%E2%8B%85v%7D%7B(n%E2%8B%85v)(1%E2%88%92k)%2Bk%7D

使用粗糙度作为几何函数的输入参数

  • 这里的K是做a基于几何函数是针对直接光照还是针对IBL光照的重映射

扩展 这里几何函数是因为需要考虑观察方向(V)和光线入射方向(L)的遮蔽情况

  • 几何函数

几何函数具有两种主要形式:G1和G2

我们前求出K的值,在计算G1 G2相乘。

这里 K的值 使用拟合曲线:float k = pow(1 + roughness, 2) * 0.5;

  • 这里我们需要哪些属性,,

    • a 粗糙度

    • NdotL

    • NdotV

再去定义真正的G项,把NL和NV代入子项,相乘即可

注意 :K系数,在直射光和间接光的计算方式略有不同,代码里是直接光的,而间接光的K=pow(roughness,2)/2,这里要注意。


  • 在片元着色器阶段计算 NdotL   NdotV

  • 效果

    粗糙度 0.1 - 1.0

菲涅尔函数 | F

float hl = max(saturate(dot(halfDir, lightDir)), 0.0001);

菲涅尔反射(Fresnel Reflectance)。光线以不同角度入射会有不同的反射率。相同的入射角度,不同的物质也会有不同的反射率。万物皆有菲涅尔反射。F0是即 0 度角入射的菲涅尔反射值。大多数非金属的F0范围是 0.02 - 0.04,大多数金属的F0范围是 0.7 - 1.0。

  • Schlick 的模型的公式

但是由于我们需要的法线方向并不是模型本身的宏观法线n,而是经过D项筛选通过的微观法线H,故需把N改成H。

FSchlick%E2%80%8B(h%2Cv%2CF0%E2%80%8B)%3DF0%E2%80%8B%2B(1%E2%88%92F0%E2%80%8B)(1%E2%88%92(h%E2%8B%85v))5


UNITY 对这个计算进行了优化,视线方向V换成了L,如下所示,这是我们所使用的函数。

其中的5次方计算量比较大,把它变成自然对数函数进行计算可以节省计算量,后续文章里所有的5次方计算都可以换算成对数计算。

  • F 函数


这里输入 HdotL在输入一个F0 计算

  • 那在片元着色器阶段计算HdotL和F0



  • 效果

    无贴图 金属度 0 -1


  • 有颜色贴图

注意:菲涅尔效果是由金属度影响的。

  • 这里金属度给到0的时候是没有颜色贴图,

合并输出

  • 直接光高光部分我们都计算完成,把D,G,F代入公式,计算出高光部分。


这里计算出的高光点数值是大于1的,如果没有打开后处理的泛光效果的话,也看不出来,但是如果打开之后,就会发现效果不对。我这里强行把它限制到了0到1的区间了。

  • 效果

    公式结果 金属度 0-1 粗糙度 0-1

  • 注意:这里的黄色是贴图的颜色

  • 增加光照颜色

注意:这里经过半球积分后会乘PI,不要丢了;注意这里并未再次乘KS,因为KS=F,已经计算过了一次不需要重复计算。

  • 金属度=1 粗糙度 0-1

  • 金属度 = 0 粗糙度 0-1

  • 金属度 0-1 粗糙度 0-1

直接光漫反射部分

上面准备好数据,计算一下直接光漫反射部分。

漫反射部分

  • 获取计算漫反射需要的数据

  • 漫反射计算

  • k_d  =  漫反射部分占比率

  • k_s  =  反射部分占比率

注意: 由于分母带了PI,而半球积分后会乘PI,两者就约掉了这就没有写。

入射光线和出射光线最大是180度,计算的时候是在一个半球空间计算。

f_%7Blambert%7D%20%20%20%20%20%20%20%20%20%20%20%20%3D%20Lambertian%E6%BC%AB%E5%8F%8D%E5%B0%84%20%20%20%20%20%20%20%3D%20%20%20%0A%20%20%20%5Cfrac%7Bc%7D%7B%CF%80%7D

这里的C = Albedo

扩展 这里为什么不叫Diffuse了,反而叫Albedo? 这是因为,Albedo 都是颜色信息,原来比如需要做一些阴影到贴图上,PBR完全不需要处理阴影,只需要颜色,所以起名叫Albedo

而除以 π 的原因是为归一化BRDF,从而使得BRDF符合能量守恒的条件,不会导致反射出去的光线比入射的光线还要多的情况。

所以上面这个公式可以这样表示

f_r%3Dk_d%5Cfrac%7Bc%7D%7B%CF%80%7D%2Bk_sf_%7Bcook%E2%88%92torrance%7D

  • 这里我们现在没有计算出kd ,前计算 NdaoL * 光照颜色

  • 效果

  • 这里处理一下漫反射,我们上面只是简单的制作的漫反射效果,我们还需要和金属度颜色关联到一起。

  • 效果

    金属度 = 0-1    粗糙度 1

  • 合并直接光漫反射和直接光高光反射

  • 效果

    金属度 0- 1   粗糙度 0-1



  • 金属度  1  粗糙度 0- 1



  • 粗糙度 1 金属度 0-1

代码

间接光漫反射部分

球体型光照

  • 在场景中创建球型光照,使用函数获取球型光照的颜色。

  • 输出结果看一下效果


  • 效果


IndirF_Function

  • 定义一个函数 计算间接光的F

  • 分别计算出间接光的 ks kd效果

输出间接光漫反射

  • 金属 0 粗糙度 0-1

注意:金属度是1 都是黑色。

间接光镜面反射部分

间接光照的高光反射本质是对于反射探针(360全景相机)拍的一张图进行采样,把采样到的颜色当成光照去进行计算,这种光照称为基于图像的光照IBL(Image-Based Lighting),前面的漫反射也是IBL,关于IBL有非常复杂的理论,

URP | Reflection Probes 反射探针 - 哔哩哔哩 (bilibili.com)

  • 前定义一个函数,对Cube采样,根据不同的粗糙度进行采样。

注意:这里也考虑AO的效果

  • 单独输出看结果

  • 效果

    粗糙度

  • 还需要间接光高光影响因子,这里就使用Unity默认的方法, unity使用的曲线拟合去得到结果.

    smoothness   这里就单独粗糙度,因为前面的粗糙度我们重新映射过了。

  • 输出查看高光影响因子

  • 效果

  • 在计算间高光反射接光

  • 效果

    • 金属度 1 粗糙度 0-1


  • 金属度 0  粗糙度 0-1

  • 合并输出,

  • 效果


  • 金属度 1 粗糙度 0-1

  • 金属度 0  粗糙度 0-1

效果对比

  • 这是自定义和 URP 自带的Lit Shader对比。

全代码

总结

  • PBR其实和其他光照模型一样,都是根据入射光和出射光计算,只是使用了更物理的算法BRDF算法。

  • 漫反射部分,是在半球空间计算,C = 颜色贴图,

  • Fresnel 需要考虑金属和非金属对光的反射区别,Fresnel的公式。

  • UE4和unity的粗糙度反射是不一样的,

    • UE4

  • Unity


内容太多分开,下面会进行代码整理完善。

URP | PBR材质(三)-自定义PBR的评论 (共 条)

分享到微博请遵守国家法律