基于图像的光照(Image Based Lighting)

在开始之前,我们先来了解下什么是基于图像的光照(IBL)。一个物体,不会单独的存在一个空空的环境里面,它的周围一定有其他的物体。当光源照射到其他物体上的时候,一定也会反射,其中就有很多反射的光线会反射到该物体上去。上一篇文章中我们模拟的是直接光照。对于直接光照系统,像上面那种其他物体反射过来的光,我们一般就只是使用一个Ambient项来模拟。这种模拟方法只能够模拟单调的环境光照效果,想要更加丰富,更加精细的效果,我们就需要使用更加丰富的环境光照系统,而IBL就是实现它的一种方式。

一般来说,我们通过一张环境贴图(Environment Map)来保存一个物体周围的环境信息,然后通过某种处理,来实现丰富的环境光照效果。本文就是讲述,如何通过对环境贴图进行处理,然后实现丰富的环境光照效果。

从渲染方程解释IBL

根据前面对环境光照的描述,环境光照也应该符合这个公式,只不过相对于直接光照,它需要计算更多的入射光线。

Lo(p,ωo)=∫Ω(kdcπ+ksDFG4(ωo⋅n)(ωi⋅n))Li(p,ωi)n⋅ωidωiL_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i Lo​(p,ωo​)=Ω∫​(kd​πc​+ks​4(ωo​⋅n)(ωi​⋅n)DFG​)Li​(p,ωi​)n⋅ωi​dωi​

同时从渲染方程可以看出,我们可以把渲染方程拆成两个部分进行处理:

Lo(p,ωo)=∫Ω(kdcπ)Li(p,ωi)n⋅ωidωi+∫Ω(ksDFG4(ωo⋅n)(ωi⋅n))Li(p,ωi)n⋅ωidωiL_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi}) L_i(p,\omega_i) n \cdot \omega_i d\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_i d\omega_i Lo​(p,ωo​)=Ω∫​(kd​πc​)Li​(p,ωi​)n⋅ωi​dωi​+Ω∫​(ks​4(ωo​⋅n)(ωi​⋅n)DFG​)Li​(p,ωi​)n⋅ωi​dωi​

本篇文章集中于处理:

Lo(p,ωo)=kdcπ∫ΩLi(p,ωi)n⋅ωidωiL_o(p,\omega_o) = k_d\frac{c}{\pi} \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i Lo​(p,ωo​)=kd​πc​Ω∫​Li​(p,ωi​)n⋅ωi​dωi​

对于这个方程,我们就可以将周围环境的所有光照信息保存在一张环境贴图中,而这个环境贴图就模拟了所有的。

环境贴图

在图形领域,用于保存周围环境信息的环境贴图有多种形式,如

现在业界,对于IBL普遍使用的是Cube Map的形式。本篇文章也将主要使用Cube Map来进行IBL。

HDR对于PBR非常重要,没有了HDR,PBR的效果将大大折扣。所以,对于IBL来说,我们依然需要使用HDR。也就说,对于周围环境光照的描述,需要通过HDR的格式文件来保存。

本文的所有使用的环境光照贴图将从sIBL中获取,这个网站里面有很多免费使用的HDR光照贴图,我们将从这些图中选择一些进行测试。

需要注意的是,这个网站里面的HDR贴图并不是CubeMap的形式,而是EquirectangularMap的形式进行保存的,所以接下来我们需要解决两个问题:如何读取.hdr文件,如何对这个贴图进行filter。

.hdr文件读取

在sIBL网站上,已经给出了.hdr文件格式的详细描述。我这里为了方便就直接使用了github上开源的stb_image库来读取.hdr文件。这个库里面都是一些单个文件的c代码库,感兴趣的读者可以自行探索。

得到.hdr文件保存的HDR数据了,然后可以通过图形API创建一个2D的HDR纹理

Equirectangular Map Filter

我们前面说过,我们将使用Cube Map来进行IBL。所以,我们需要一种方法来将该Equirectangular Map转换为Cube Map。为此,我们先简单的绘制一个球体,然后将这个Equirectangular Map贴上去,然后使用传统的创建Cube Map的方式产生一张Cube Map。

#version 330 core
out vec4 FragColor;
in vec3 WorldPos;uniform sampler2D equirectangularMap;const vec2 invAtan = vec2(0.1591, 0.3183);
vec2 SampleSphericalMap(vec3 v)
{vec2 uv = vec2(atan(v.z, v.x), asin(v.y));uv *= invAtan;uv += 0.5;return uv;
}void main()
{       vec2 uv = SampleSphericalMap(normalize(WorldPos));vec3 color = texture(equirectangularMap, uv).rgb;FragColor = vec4(color, 1.0);
}

通过计算atan(n.z, n.x)就能够得到具有该法线顶点的UV坐标的U值,通过计算asin(n.y)就能够得到具有该法线顶点的UV坐标的V值。同时,由于atan函数返回的结果在[−π,π]
之间,而asin返回的结果在[−π/2,π/2]之间,所有需要把它们都映射到[0,1]之间。

在得到了这个球体之后,我们就可以简单的使用传统的方法来创建CubeMap,主要就是通过设置FOV为90度的摄像机,分别朝着+X,-X,+Y,-Y,+Z,-Z去观察该球体,然后渲染CubeMap的6个面,从而得到一张HDR的CubeMap。

预计算辐射光照贴图

为了方便进行积分运算,一般都将渲染方程改为球面坐标的积分形式,其中:

n⋅ωi=cos⁡θn⋅\omega_i=\cos\theta n⋅ωi​=cosθ

dωi=sin⁡θdϕdθd\omega_i=\sin\theta d\phi d\theta dωi​=sinθdϕdθ

所以,方程转变为如下形式:

(1)Lo(p,ϕo,θo)=kdcπ∫ϕ=02π∫θ=012πLi(p,ϕi,θi)cos⁡θsin⁡θ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 \tag{1}Lo​(p,ϕo​,θo​)=kd​πc​∫ϕ=02π​∫θ=021​π​Li​(p,ϕi​,θi​)cosθsinθdϕdθ(1)

上述公式转换为Riemann Sum(黎曼和)的表述:

Riemann Sum是一种很简单的积分方法,当我们的步进值越小的时候,通过这种方法计算出来的h值就越加的接近真实值。

(2)Lo(p,ϕo,θo)=kdcπ1n1n2∑ϕ=0n1∑θ=0n2Li(p,ϕi,θi)cos⁡(θ)sin⁡(θ)dϕdθL_o(p,\phi_o, \theta_o) = k_d\frac{c}{\pi} \frac{1}{n1 n2} \sum_{\phi = 0}^{n1} \sum_{\theta = 0}^{n2} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta \tag{2}Lo​(p,ϕo​,θo​)=kd​πc​n1n21​ϕ=0∑n1​θ=0∑n2​Li​(p,ϕi​,θi​)cos(θ)sin(θ)dϕdθ(2)

vec3 irradiance = vec3(0.0);  vec3 up    = vec3(0.0, 1.0, 0.0);
vec3 right = cross(up, normal);
up         = cross(normal, 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){// spherical to cartesian (in tangent space)vec3 tangentSample = vec3(sin(theta) * cos(phi),  sin(theta) * sin(phi), cos(theta));// tangent space to worldvec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);nrSamples++;}
}
irradiance = PI * irradiance * (1.0 / float(nrSamples));

预计算辐射光照贴图(irradianceMap). 这个计算过程输入为一张环境贴图 CubeMap .输出也是一张 CubeMap. 如下图所示.

在球谐面卷积采样, 生成的图像有点类似模糊的效果。离线生成的irradianceMap大小就32x32, 精度不需要那么高, 中间值使用插值就可以得到。

整个流程:

equirectangularmap(.hdr) -> envCubemap -> irradianceMap

在引擎里看到的效果,就是如下所示:

PBR之基于图像的光照IBL (Diffuse)相关推荐

  1. CSharpGL(54)用基于图像的光照(IBL)来计算PBR的Specular部分

    CSharpGL(54)用基于图像的光照(IBL)来计算PBR的Specular部分 接下来本系列将通过翻译(https://learnopengl.com)这个网站上关于PBR的内容来学习PBR(P ...

  2. unity-shader-基于图像的光照IBL

    title: unity-shader-基于图像的光照IBL categories: Unity3d-Shader tags: [unity, shader, 间接光, IBL, pbr] date: ...

  3. GraphicsLab Project之基于物理的着色系统(Physical based shading) - 基于图像的光照(Image Based Lighting)(Diffuse篇)

    作者:i_dovelemon 日期:2018-01-21 来源:CSDN 主题:PBR, Equrectangular Map, Cube Map, Irradiance Map, HDR Image ...

  4. OpenGL基于PBR的图像的光照IBL的实例

    OpenGL 基于PBR的图像的光照IBL 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <glad/glad.h> #includ ...

  5. PBR理论基础3.1:基于图像的光照(下)

    接上文:PBR理论基础3:基于图像的光照(上) 四.蒙特卡洛积分 蒙特卡洛积分在前篇计算漫反射项时就已经应用过了 对于定积分    ,如果无法简单求得原函数,一个尝试去求得其近似解的方法就是蒙特卡洛积 ...

  6. PBR理论基础3:基于图像的光照(上)

    一.基于图像的光照(Image based lighting, IBL) 只要了解了下面这些,就很容易理解基于图像的光照了: 直接光与间接光的区别(UnityShader9:光照基础回顾) PBR理论 ...

  7. 基于图像的光照(Image-Based Lighting, IBL)概述

    现代的游戏引擎或者渲染引擎在渲染光照时为了渲染的效率往往会使用一些二维纹理贴图来储存一些计算光照以及着色的影像数据,这就是基于图像的光照(Image-Based Lighting, IBL).当然在部 ...

  8. GPU Gems1 - 19 基于图像的光照(Image-Based Lighting)

    这篇文章打破了当时立方体贴图环境(Cube-Map Environment)用法的桎梏,深入研究了更多可能的逼真光照效果.文章主要研究了基于图像的光照(Image-Based Lighting,IBL ...

  9. 【Shader】基于图像的光照(Image Based Lighting,IBL)

    一.立方体贴图(CubeMap) CubeMap是什么? CubeMap是一个由六个独立的正方形纹理组成的集合,它将多个纹理组合起来映射到一个单一纹理,通常被用来作为具有反射属性物体的反射源. Cub ...

最新文章

  1. django template语法
  2. 2021年春季学期-信号与系统-第十三次作业参考答案-第九小题
  3. hdu 3183 st表
  4. 每周论文清单:高质量文本生成,多模态情感分析,还有一大波GAN | PaperDaily #26
  5. H3C 单区域OSPF配置示例二
  6. php正则式去掉问号后所有,正则表达式如何过滤问号
  7. (转)Hibernate框架基础——一对多关联关系映射
  8. SAP License:BCS进阶第一篇-BCS相关概念
  9. svm (opencv)几个主要函数
  10. bzoj4517[Sdoi2016]排列计数(组合数,错排)
  11. Google cloud 存储 Storage
  12. Windows10磁盘分区和扩展
  13. 数字/模拟信号中带宽的含义
  14. 计算机管理 超级用户,Win10如何开启Administrator超级管理员帐户?
  15. 八大算法实现(Java)
  16. Verilog之计数器设计实现
  17. 计算机专业毕业文案,毕业微信朋友圈说说 2020毕业文案经典个性
  18. 校尉羽书飞瀚海,顺序表中增删改(Pt.2)
  19. 黄金分割线在UI设计中的应用
  20. 该如何才能更快且有效的学习?

热门文章

  1. C++部分函数调用形式耗时测试
  2. 启用群晖 Drive 的团队文件夹
  3. 【学习亚马逊AWS IOT体系有感---万物互联(物联网)】
  4. 服务器文件增量备份,服务器文件增量备份
  5. 为多孔介质的当量直径_CFX多孔介质模型介绍
  6. python请输入一个数字_python里面如何输入数字
  7. spyder pyecharts不显示_微星PAG272QRZ显示器好不好用 微星PAG272QRZ显示器全面评测_显示器_硬件教程...
  8. 项目启动初始化SQL脚本
  9. 梯度下降法求解方程的极值
  10. 微信小程序时间加法_微信小程序获取系统时间、时间戳、时间时间戳加减