[toc]

# 调用时机

Vulkan_RHI 初始化完毕之后

# 数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct PIBLResourceData
{
void* _brdfLUT_texture_image_pixels;
uint32_t _brdfLUT_texture_image_width;
uint32_t _brdfLUT_texture_image_height;
PILOT_PIXEL_FORMAT _brdfLUT_texture_image_format;
std::array<void*, 6> _irradiance_texture_image_pixels;
uint32_t _irradiance_texture_image_width;
uint32_t _irradiance_texture_image_height;
PILOT_PIXEL_FORMAT _irradiance_texture_image_format;
std::array<void*, 6> _specular_texture_image_pixels;
uint32_t _specular_texture_image_width;
uint32_t _specular_texture_image_height;
PILOT_PIXEL_FORMAT _specular_texture_image_format;
};

可以看出,这个结构中主要记录了几个常见的贴图
- brdfLUT : 镜面反射中 BRDF 的预处理项 什么是 IBL 的 LUT-learnOpenGL

# BRDF 镜面反射项

首先,Cook-T 的反射率方程如下 $$ L_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 $$ 其中k_sk\_s 开头的部分就是镜面反射的方程,提取出来表示如下: $$ L_o (p,\omega_o) = \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 = \int\limits_{\Omega} f_r (p, \omega_i, \omega_o) L_i (p,\omega_i) n \cdot \omega_i d\omega_i

其中BRDF中的一项 $f\_r(p, w\_i, w\_o) = \\frac{DFG}{4(\\omega\_o \\cdot n)(\\omega\_i \\cdot n)}$ 在这项中可以看出和实际的位置并没有关系.他只和入射与出射方向有关,出射方向就是视角方向。 但是BRDF项与$L(p,w\_i)$项合在一起的,我们无法在位置无限的前提下进行与计算,好在Epic Games提出了`分割求和近似法`,将这两项可以分割开近似计算 $L\_o(p,\\omega\_o) = \\int\\limits\_{\\Omega} L\_i(p,\\omega\_i) d\\omega\_i \* \\int\\limits\_{\\Omega} f\_r(p, \\omega\_i, \\omega\_o) n \\cdot \\omega\_i d\\omega\_i$ 第一部分是预计算的环境卷积CubeMap(?) 第二部分就是`BRDF`,这部分假设入射辐射度都是白色的,就可以在给定粗糙度,光线$w\_i$,法线`n`,夹角$n·w\_i$下预计算出BRDF积分的值. 预计算的结果存储在`LUT`中,LUT存储了每个粗糙度和入射角的组合的结果,存储的是菲涅尔相应系数(R通道),和偏差值(G通道),查找纹理时**以$n·w\_i$作为横坐标,以粗糙度作为纵坐标**,就可以取出结果 # Pilot IBL Pilot中使用Vulkan来进行IBL采样的初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void Pilot::PGlobalRenderResource::initializeIBLResource(PVulkanContext& context) { initializeIBLSamplers(context); }

void Pilot::PGlobalRenderResource::initializeIBLSamplers(PVulkanContext& context)
{
VkPhysicalDeviceProperties physical_device_properties {};
vkGetPhysicalDeviceProperties(context._physical_device, &physical_device_properties);

VkSamplerCreateInfo samplerInfo {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;

samplerInfo.anisotropyEnable = VK_TRUE; // close:false
samplerInfo.maxAnisotropy = physical_device_properties.limits.maxSamplerAnisotropy; // close :1.0f

samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;

// global textures for IBL
samplerInfo.maxLod = 0.0f;
if (vkCreateSampler(context._device, &samplerInfo, nullptr, &_ibl_resource._brdfLUT_texture_sampler) != VK_SUCCESS)
{
throw std::runtime_error("vk create sampler");
}

samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 8.0f; // todo: m_irradiance_texture_miplevels
samplerInfo.mipLodBias = 0.0f;
if (vkCreateSampler(context._device, &samplerInfo, nullptr, &_ibl_resource._irradiance_texture_sampler) !=
VK_SUCCESS)
{
throw std::runtime_error("vk create sampler");
}
if (vkCreateSampler(context._device, &samplerInfo, nullptr, &_ibl_resource._specular_texture_sampler) != VK_SUCCESS)
{
throw std::runtime_error("vk create sampler");
}
}
上述代码主要是建立了三个通道的采样`VkSampler` ## 构建IBL `getIBLTextureData`函数中把需要的数据读取到TextureHandle内存中, 另外,还有`irradiance`和`specular`这两个图