PBR中引入IBL——漫反射篇

PBR中引入IBL——漫反射篇

IBL

  • IBL(Image based lighting,基于图像的光照)是一类光照技术的集合
  • 在IBL中,将周围环境整体视为一个大光源
  • IBL通常使用环境立方体贴图实现,可以理解为:将立方体贴图的采样值认为是渲染方程的计算结果,使用这个采样值可以在着色器中直接进行光照计算
  • 环境立方体贴图取自现实世界或从场景中生成
  • IBL是一种比较精确的环境光照输入格式,也可以说是一种间接光照的粗略近似(全局光照可以理解为直接光照+间接光照,IBL只相当于间接光照部分)

分解渲染方程

  • Cook-Torrance反射方程:
    L o ( p , ω o ) = ∫ Ω ( k d c π + k s D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) ) L i ( p , ω i ) n ⋅ ω i d ω i L_o(p,\omega_o) = \int\limits_\Omega(k_d\frac{c}{π} + k_s\frac{DFG}{4(\omega_o\cdot n)(\omega_i\cdot n)})L_i(p,\omega_i)n\cdot\omega_id\omega_i Lo(p,ωo)=Ω(kdπc+ks4(ωon)(ωin)DFG)Li(p,ωi)nωidωi

L o ( p , ω o ) = ∫ Ω ( k d c π ) L i ( p , ω i ) n ⋅ ω i d ω i + ∫ Ω ( k s D F G 4 ( ω o ⋅ n ) ( ω i ⋅ n ) ) L i ( p , ω i ) n ⋅ ω i d ω i L_o(p,\omega_o) = \int\limits_\Omega(k_d\frac{c}{π})L_i(p,\omega_i)n\cdot\omega_id\omega_i + \int\limits_\Omega(k_s\frac{DFG}{4(\omega_o\cdot n)(\omega_i\cdot n)})L_i(p,\omega_i)n\cdot\omega_id\omega_i Lo(p,ωo)=Ω(kdπc)Li(p,ωi)nωidωi+Ω(ks4(ωon)(ωin)DFG)Li(p,ωi)nωidωi

  • 本篇内容只关注前半部分,即漫反射部分,所以就有:
    L o ′ ( p , ω o ) = ∫ Ω ( k d c π ) L i ( p , ω i ) n ⋅ ω i d ω i L_o'(p,\omega_o) = \int\limits_\Omega(k_d\frac{c}{π})L_i(p,\omega_i)n\cdot\omega_id\omega_i Lo(p,ωo)=Ω(kdπc)Li(p,ωi)nωidωi

  • 提出常数部分:
    L o ′ ( p , ω o ) = k d c π ∫ Ω L i ( p , ω i ) n ⋅ ω i d ω i L_o'(p,\omega_o) = k_d\frac{c}{π}\int\limits_\Omega L_i(p,\omega_i)n\cdot\omega_id\omega_i Lo(p,ωo)=kdπcΩLi(p,ωi)nωidωi

  • 结合IBL的思路,将 ∫ Ω L i ( p , ω i ) n ⋅ ω i d ω i \int\limits_\Omega L_i(p,\omega_i)n\cdot\omega_id\omega_i ΩLi(p,ωi)nωidωi的结果存入一个立方体纹理,我们给这个纹理起个名字叫irradiance map,当需要 ω o \omega_o ωo方向上间接光的漫反射时,只用对irradiacne map采样即可

irradiance map的生成和作用

  • 在生irradiance map时,会遇到一系列问题:

irradiance map生成过程

  • 首先要有一个环境纹理envTexture,这个envTexture的目的是提供 ω i \omega_i ωi方向上的radiance,即 L i ( p , ω i ) L_i(p,\omega_i) Li(p,ωi)

  • envTexture可以是由美术人员提前做好的、也可以是在场景中提前生成的,具有HDR颜色值的立方体纹理、或者是可以变换成立方体纹理的其他类型纹理

  • irradiance map是在envTexture基础上生成的

  • 通过对envTexture上每个纹素的半球 Ω 上所有可能的 ω i \omega_i ωi进行采样,来计算 ∫ Ω L i ( p , ω i ) n ⋅ ω i d ω i \int\limits_\Omega L_i(p,\omega_i)n\cdot\omega_id\omega_i ΩLi(p,ωi)nωidωi,并将结果保存到irradiance map中,即生成irradiance map;这个计算结果表示点p在所有可能的 ω i \omega_i ωi上的irradiance的和,这个结果再乘以 k d c π k_d\frac{c}{\pi} kdπc就等于 ω o \omega_o ωo方向上的radiance,即 L o ′ ( p , ω o ) L_o'(p,\omega_o) Lo(p,ωo)

  • 用来采样envTexture的半球,要面向 ω o \omega_o ωo方向
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QnyHw5Pq-1631687144758)(en-resource://database/1777:1)]

  • 对于生成的irradiance map,通过每个采样方向 ω o \omega_o ωo,得到的是场景中所有能够击中 ω o \omega_o ωo的表面的间接漫反射光的总和

  • 此后就可以通过采样irradiance map获取间接光照的漫反射,进行光照计算

  • 生成irradiance map的过程看似简单,却每一步都暗藏玄机

环境纹理envTexture的格式

  • 由上一篇的内容可知,PBR渲染中一定要使用HDR颜色值,而这个envTexture当然也不能例外
  • 因此这里要引入一个新的图片格式——“.hdr”,这种格式的图片专门用来存储HDR颜色值

环境纹理envTexture的类型

  • 一般常见的envTexture并不是立方体纹理,而是一个等距柱状投影图的2d纹理
  • 等距柱状投影图(Equirectangular Map)的特点:图像扭曲,水平视角附近分辨率较高,而底部和顶部方向分辨率较低
  • 因此需要将这种envTexture变换成立方体贴图envCubeTexture
  • 此后就是对envCubeTexture进行操作了

计算irradiance map

  • 实际上,不可能对envCubeTexture的每个纹素,在半球Ω的所有可能的 ω i \omega_i ωi进行采样,我们采用的方法是:在半球Ω的大量 ω i \omega_i ωi上进行离散采样,并对其采样值 L i L_i Li进行积分计算后再取平均值,这个过程其实是对envCubeTexture进行卷积

  • 卷积的特性是,对数据集其中的一个条目做一些计算时,要考虑到数据集其中的所有其他条目;这里的数据集就是场景的 L i L_i Li或envCubeTexture

  • 在求积分时,积分变量 d ω d\omega dω比较难处理,将其变换成球坐标的 θ \theta θ ϕ \phi ϕ,如下图
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SEBwlrNV-1631687144760)(en-resource://database/1797:1)]

  • 在变换了积分变量后, n ⋅ ω d ω i = c o s θ d ϕ d θ n\cdot \omega d\omega_i = cos\theta d\phi d\theta nωdωi=cosθdϕdθ,所以整个积分变成了
    L o ′ ( p , ϕ o , θ o ) = k d c π ∫ ϕ = 0 2 π ∫ θ = 0 1 2 π L i ( p , ϕ i , θ i ) c o s θ d ϕ d θ L_o'(p,\phi_o,\theta_o) = k_d\frac{c}{\pi}\int_{\phi=0}^{2\pi}\int_{\theta=0}^{\frac{1}{2}\pi}L_i(p,\phi_i,\theta_i)cos\theta d\phi d\theta Lo(p,ϕo,θo)=kdπcϕ=02πθ=021πLi(p,ϕi,θi)cosθdϕdθ

  • 然而,由于球的一般性质,当采样区域朝向中心顶部会聚时,天顶角 θ 变小,半球的离散采样区域也变小;为了平衡较小的区域贡献度,使用 sinθ 来权衡区域的贡献度,即:
    L o ′ ( p , ϕ o , θ o ) = k d c π ∫ ϕ = 0 2 π ∫ θ = 0 1 2 π L i ( p , ϕ i , θ i ) c o s θ s i n θ d ϕ d θ L_o'(p,\phi_o,\theta_o) = k_d\frac{c}{\pi}\int_{\phi=0}^{2\pi}\int_{\theta=0}^{\frac{1}{2}\pi}L_i(p,\phi_i,\theta_i)cos\theta sin\theta d\phi d\theta Lo(p,ϕo,θo)=kdπcϕ=02πθ=021πLi(p,ϕi,θi)cosθsinθdϕdθ

  • 积分的求解需要我们在半球 Ω 内采集固定数量的离散样本并对其结果求平均值;分别给每个球坐标轴指定离散样本数量 n1 和 n2 求其黎曼和( ∫ a b f ( x ) d x = ∑ k = 1 n f ( ε k ) Δ x k \int_a^bf(x)dx = \sum_{k=1}^{n}f(\varepsilon_k)\Delta x_k abf(x)dx=k=1nf(εk)Δxk),即:
    L o ′ ( p , ϕ o , θ o ) = k d c π 1 n 1 n 2 ∑ ϕ = 0 n 1 ∑ θ = 0 n 2 L i ( p , ϕ i , θ i ) c o s θ s i n θ d ϕ d θ L_o'(p,\phi_o,\theta_o) = k_d\frac{c}{\pi}\frac{1}{n1n2}\sum_{\phi=0}^{n1}\sum_{\theta=0}^{n2}L_i(p,\phi_i,\theta_i)cos\theta sin\theta d\phi d\theta Lo(p,ϕo,θo)=kdπcn1n21ϕ=0n1θ=0n2Li(p,ϕi,θi)cosθsinθdϕdθ

  • 在变换积分变量后,对envCubeTextre采样的向量也有相应的变化:用 θ \theta θ ϕ \phi ϕ表示空间中的方向v=(sinθcosϕ,sinθsinϕ,cosθ);球坐标的z轴正方向始终是竖直向上,因此这个向量v相当于是切线空间中的坐标,在对envCubeTexter进行采样时,要将他变换到世界坐标

  • 在绘制立方体贴图时,有一个方便的特性:顶点坐标与纹理坐标一致

  • 在生成irradiance map时,假设对于envCubeTexture的每个片段,表面的半球朝向法向量 N ,对envCubeTexture进行卷积等于计算朝向 N 的半球 Ω 中每个 ω i \omega_i ωi方向的总平均irradiance。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1NiL26X4-1631687144762)(en-resource://database/1802:1)]

vec3 N = normalize(WorldPos);
vec3 irradiance = vec3(0.0);
vec3 up = vec3(0.0, 1.0, 0.0);
vec3 right = cross(up, N);
up = cross(N, right);
float sampleDelta = 0.025;
float nrSamples = 0.0;
for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta)
{
    for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta) 
    {
        vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
        vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; 
        irradiance += texture(envCubeTexture, sampleVec).rgb * cos(theta) * sin(theta);          nrSamples++;
    }
}
irradiance = PI * irradiance * (1.0 / float(nrSamples));//为啥乘以pi???
  • 完成这一步,我们就得到了一个预计算好的irradiance map,可以直接将其用于IBL 计算

计算间接光照中的漫反射

  • 给定一张irradiance map,它存储了场景中的所有间接漫反射光,获取片段的irradiance就简化为给定法线的一次纹理采样:
//顶点着色器
//...
layout (location = 2) in vec3 aNormal;
uniform mat4 model;
out vec3 Normal;
void main()
{
    Normal = mat3(model) * aNormal;   
    //...
}
//片段着色器
in vec3 Normal;
void main()
{
    //...
   vec3 irradiance = texture(irradianceMap, Normal).rgb;
    //...
}
  • 由于间接光照包括漫反射和镜面反射两部分,我们可以使用菲涅耳函数来计算表面的间接反射率 k d k_d kd
  • 由于环境光来自半球内围绕法线 N 的所有方向,因此没有一个确定的半程向量来计算菲涅耳函数;为了模拟菲涅耳函数,我们用法线和视线之间的夹角计算菲涅耳系数,但是这样的近似忽略了表面粗糙度的影响,我们可以通过在 Sébastien Lagarde 提出的 Fresnel-Schlick 方程中加入粗糙度项来缓解这个问题:
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
{
    return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}

void main()
{
    //...
    vec3 kS = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness);
    vec3 kD = 1.0 - kS;
    vec3 irradiance = texture(irradianceMap, N).rgb;
    //..
}
  • 根据 L o ′ ( p , ω o ) = k d c π ∫ Ω L i ( p , ω i ) n ⋅ ω i d ω L_o'(p,\omega_o) = k_d\frac{c}{\pi}\int\limits_\Omega L_i(p,\omega_i)n\cdot \omega_id\omega Lo(p,ωo)=kdπcΩLi(p,ωi)nωidω
vec3 diffuse  = Lo' = kD * albedo * irradiance;//为啥没有pi分之一???

效果对比

  • 只有间接漫反射光的效果
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xTUzpxou-1631687144764)(en-resource://database/1807:1)]

  • 可见漫反射的作用:金属没有漫反射所以表现的比较暗,非金属主要是靠漫反射提供颜色或亮度

  • 下一篇将介绍间接光照中的镜面反射部分,他将长这个样子
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aIh3WcOy-1631687144766)(en-resource://database/1812:1)]

  • 可见镜面反射的作用:可以使表面光滑的物体反射出周围的环境,金属主要靠镜面反射提供颜色,而非金属主要靠漫反射提供颜色,所以最下边一排的非金属小球都显示了黑色

  • 将漫反射和镜面反射合起来
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C8E6pFyx-1631687144768)(en-resource://database/1817:1)]

  • 对于传说中的全局光照,还差一个直接光照,现再将直接光照加上就会是这个样子
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i8n4tTrT-1631687144769)(en-resource://database/1827:1)]

  • 可以看出所有的物体的亮度又增加了,而且光滑物体的表面出现了的高光

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值