PBR_IBL镜面部分
- 镜面部分并不能像漫反射部分一样将BRDF部分像常量一样提取出来,因为它整个积分上不是常数,因为它受到wi和w0的影响,就比如一个x的等式,不能把x部分提取出来一样,他是随着等式变化的 
- 如果试图解算所有入射光方向加所有可能的视角方向的积分,二者组合数会极其庞大,实时计算太昂贵。
- 我们把镜面部分再次拆分为两个部分:镜面部分再次分解(近似):积分ab = 积分a * 积分b 
- 分割求和近似法(split sum approximation):预滤波环境贴图(辐照度) + BRDF积分贴图
- 镜面部分(分解后:Cook-Torrance 积分的BRDF,积分的镜面辐照度(考虑粗糙度和重要性采样)
预滤波环境贴图(镜面部分):
- 这次会考虑粗糙度,因为对于不同粗糙度的表明会使用不同的mip图采样
- 粗糙度越高,采样越分散,贴图越模糊,将保存到不同的mipmap级别的贴图中, 
- 为了确保为其 mip 级别分配足够的内存,一个简单方法是调用 glGenerateMipmap(GL_TEXTURE_CUBE_MAP)。
- GL_LINEAR_MIPMAP_LINEAR 启用三线性过滤
- mip 级别每增加一级,尺寸缩小为一半。 
重要性采样:
- 如果使用均匀的半球采样,计算镜面部分的辐照度,效果会很差,镜面反射依赖于表面的粗糙度,形成镜面波瓣,如果以类似方式选取采样向量将是有意义的,因此需要重要性采样
- 如果要预计算,我们并不知道视角方向,如果我们假设视角方向==输出采样方向w0,这个部分中就不用考虑视角方向,从而预计算,虽然效果不是很好
- 对于视线wo方向:N法线 = R反射向量 = V视线方向
- //
- 重要性采样:具有更快收敛速度的采样
-  蒙特卡洛积分(完全随机 | 伪随机): 
-  蒙特卡洛积分:离散的解决问题,不必考虑所有 -  在 a 到 b 上采样 N 个随机样本的子集,求和并求平均数,pdf 代表概率密度函数 
-  随着样本数量的不断增加,我们最终将收敛到积分的精确 
-  估算是有偏的,集中于特定的值或方向,具有更快的收敛速度,但是可能永远不会收敛到精确解 
 
-  
-  // 
-  低差异序列(均匀 | 半随机): 
-  该序列生成的仍然是随机样本,但样本分布更均匀:具有更快的收敛速度,它们会以更快的速度收敛到精确解,这使得它对于性能繁重的应用很有用。 
-  Hammersley 序列(XI):该序列是把十进制数字的二进制表示镜像翻转到小数点右边而得 
float RadicalInverse_VdC(uint bits) //生成随机数
{
     bits = (bits << 16u) | (bits >> 16u);
     bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
     bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
     bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
     bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
     return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// ----------------------------------------------------------------------------
vec2 Hammersley(uint i, uint N)//(i在n个样本中的第i个样本,N个子样本)生成低差异序列
{
	return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}
-  // 
-  GGX几何函数 重要性采样: 
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)//生成大体围绕镜面波瓣的采样方向
{
	float a = roughness*roughness;
	
	float phi = 2.0 * PI * Xi.x;
	float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
	float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
	
	// from spherical coordinates to cartesian coordinates - halfway vector
	vec3 H;
	H.x = cos(phi) * sinTheta;
	H.y = sin(phi) * sinTheta;
	H.z = cosTheta;
	
	// from tangent-space H vector to world-space sample vector计算TBN
	vec3 up          = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
	vec3 tangent   = normalize(cross(up, N));
	vec3 bitangent = cross(N, tangent);
	
	vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;//变换到世界空间
	return normalize(sampleVec);
}
-  有别于均匀或纯随机地,采样会根据粗糙度a,偏向微表面的半向量h的宏观反射方向,将 GGX 和 NDF应用到采样中。使用低差异序列值 Xi作为输入来生成采样向量: 
-  我们需要一些方法定向和偏移采样向量,以使其朝向特定粗糙度的镜面波瓣方向: 
预过滤 mipmap 级别
 
 
  -  帧缓冲区缩放到适当的 mipmap 尺寸, mip 级别每增加一级,尺寸缩小为一半。 
-  访问该贴图时指定的 mip 等级越高,获得的反射就越模糊。 
-  基于积分的 PDF 和粗糙度采样环境贴图的 mipmap 
预过滤卷积的伪像:接缝和亮点:
 
 
  -  较低的 mip 级别具有更低的分辨率,并且预过滤贴图代表了与更大的采样波瓣卷积,因此缺乏立方体的面和面之间的滤波的问题就更明显: 
-  可以启用 GL_TEXTURE_CUBE_MAP_SEAMLESS,以为我们提供在立方体贴图的面之间进行正确过滤的选项: 
-  // 
-  对镜面反射进行卷积需要大量采样,才能正确反映 HDR 环境反射的混乱变化。 
-  我们可以在预过滤卷积时,开启三线性过滤,不直接采样环境贴图,而是采样mapmap 
BRDF积分贴图:
 
 
  - 假设每个方向的入射辐射度L都是白色的,在给定粗糙度、光线 ωi 法线 n 夹角 n⋅ωi 的情况下,可以预计算结果,
- 使用RGB通道的纹理,R为菲涅耳响应的系数,G为偏差值,
 n⋅ωi为横坐标,粗糙度作为纵坐标
- 需要生成一张 512 × 512 分辨率的 2D 纹理 
- 对于brdf部分,我们可以再次移项变换和近似,将得到这个公式
合成最终颜色:
 
 
   
 
  - 根据菲涅尔fresnelSchlick(法线n视线v的点乘,f0,粗糙度)返回ks计算镜面部分占比,1-ks = kd计算漫反射部分占比
vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness);
vec3 kS = F;//漫反射部分权重
vec3 kD = 1.0 - kS;//镜面部分权重
kD *= 1.0 - metallic;     
vec3 irradiance = texture(irradianceMap, N).rgb;//漫反射LBL
vec3 diffuse    = irradiance * albedo;//漫反射部分
const float MAX_REFLECTION_LOD = 4.0;
vec3 prefilteredColor = textureLod(prefilterMap, R,  roughness * MAX_REFLECTION_LOD).rgb;//镜面LBL   
vec2 envBRDF  = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg;//BRDF
vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y);//镜面部分
vec3 ambient = (kD * diffuse + specular) * ao; //计算顶点的环境光


















