美文网首页
36. PBR-IBL漫反射辐照度

36. PBR-IBL漫反射辐照度

作者: 天叔 | 来源:发表于2020-08-27 00:50 被阅读0次

todo:对天空盒的实现原理感觉理解的并不透彻,比如深度测试的细节等,回头有时间再深入钻研.

这一篇是对环境光照(漫反射)进行更精确的渲染,涉及到Monte Carlo积分,笔者数学功底不好,一开始在理解公式上浪费了些时间。

另外:求辐照度卷积,其实是只求天空盒正中间的一个点,各个方向的辐照度卷积值(漫反射),渲染物体时,方向相同的点采样的漫反射强度是一样的,参考Article - Physically Based Rendering 或者教程中所述,实际工程中,会选空间中4个Position来计算四分卷积图,其他的图从这四个Position对应的采样进行差值运算

一、背景:

1. 为什么要用IBL

对环境光照进行积分,在理论上是可行的,也更精确,而计算机运算是基于离散的值,只能说不断扩大采样,无限逼近理论值,这在工程领域是不可取的的,有巨大的计算资源消耗。

所以采用IBL的思路,把环境光照通过Monte Carlo积分的方式简化,再通过一张辐照度纹理存储实现预计算,真正渲染的时候,是通过对辐照度纹理取值。

learnOpengl这一章讲述的不太好,可以参考Article - Physically Based Rendering来理解。

2.蒙特卡洛积分
备注一个歧义点:
更新[1],再认真看一遍,教程上说的是采用黎曼和来求积分,为啥这两种积分的结果不一样呢?,可能是取平均值,除以积分总数,1/n1n2 并不属于黎曼和的范畴??但是为啥要求平均值呢?所有的光照是真实的照在物体上的,求平均值干啥?这里先过吧,卡了好几天了,不能再浪费时间了,项目快做不完了。
更新[2],这个是求间接环境光照,就是一种算法实践,求平均值,是为了逼近真实环境光的效果,如果不求平均,那太亮了

learnOpengl-IBL 教程中关于蒙特卡洛积分公式貌似写的有问题,π写在分母上了

引用learnOpengl 公式的推导是没有问题的注意经度和维度的积分域分别是[0,2π]和[0, π/2 ]: 引用learnOpengl

Article - Physically Based Rendering一文中阐述的不同:

二、渲染环境贴图

为IBL实现打基础,这里也有不少细节,每一步都需要花时间吃透

效果

涉及的知识点依次说明

1. 2D的环境贴图采样成立体贴图处理
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(localPos)); // make sure to normalize localPos
    vec3 color = texture(equirectangularMap, uv).rgb;

    FragColor = vec4(color, 1.0);
}

代码说明:


  1. 根据position求经纬度, asin(v.y),根据y坐标求角度,注意半径r已经归一化处理成了1,注意图中的角度的规则;atan(v.z, v.x)求经度,同样注意角度的取值规则,这里网上的很多帖子都一笔带过,笔者在这里竟然琢磨半天
  2. 求出来的角度是弧度值,asin取值范围[-π/2, π/2],atan取值范围[-π, π],需要归一化到[0,1]之间,因为环境贴图纹理是[0, 1]的取值范围。
    这里注意invAtan是个常量值,用来归一化
    invAtan = vec2(0.1591, 0.3183)
    0.1591 = 1/6.28319(=>2PI)
    0.3183 = 1/3.14159(=>PI)

invAtan参考stackflow:https://stackoverflow.com/questions/48494389/how-does-this-code-sample-from-a-spherical-map/48534536

asin atan取值参考 opengl文档 https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/

剩下的逻辑不复杂,绘制完天空盒后,再按照上一篇绘制球体和光照
完整代码参考:https://github.com/JoeyDeVries/LearnOpenGL/tree/master/src/6.pbr/2.1.1.ibl_irradiance_conversion

画图是个好习惯,梳理思路:


三、IBL

渲染环境光照卷积 最终渲染效果:

求光照卷积这一段中TBN切面空间是怎么算的没懂,苦思不得求解

更新[1] 目前能想到的是,根据worldPos传进来的坐标(作为法线),更新半球方向,从Model坐标 -->转换到世界坐标。注意,球面上每一个方向的卷积积分的角度是不一样的。

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 world
        vec3 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));

五、参考

https://zhuanlan.zhihu.com/p/66518450

相关文章

网友评论

      本文标题:36. PBR-IBL漫反射辐照度

      本文链接:https://www.haomeiwen.com/subject/pduqsktx.html