当前位置: 首页 > news >正文

Vulkan API 创建并渲染一个辐照度立方体贴图,用于 PBR 光照计算

我将为 generateIrradianceCube 函数提供逐行中文注释和分析。这个函数是用 C++ 编写的,使用 Vulkan API 生成用于物理基础渲染 (PBR) 的辐照度立方体贴图 (irradiance cube map)。该函数创建一个立方体贴图,设置离屏渲染管线,并将环境贴图渲染为具有多级渐进纹理 (mip levels) 的辐照度立方体贴图。以下是对每一行或逻辑块的详细分析和注释,解释其功能和作用。


void generateIrradianceCube()
{// 开始计时,使用高精度时钟记录函数执行的开始时间。auto tStart = std::chrono::high_resolution_clock::now();
  • 目的:记录函数开始时间,用于测量生成辐照度立方体贴图的总耗时。
  • 细节:使用 std::chrono::high_resolution_clock 提供高精度的时间测量。
    const VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT;const int32_t dim = 64;const uint32_t numMips = static_cast<uint32_t>(floor(log2(dim))) + 1;
  • 注释
    • format:定义图像格式为 VK_FORMAT_R32G32B32A32_SFLOAT,表示每个像素使用 32 位浮点数存储 RGBA 四个通道,提供高精度颜色数据,适合辐照度计算。
    • dim:设置立方体贴图的尺寸为 64x64 像素。
    • numMips:计算mipmaps(多级渐进纹理)的数量。使用 log2(dim) 计算最大mip级别(例如,64 -> 6 级),并加 1 包含基础级别(0 级)。
    // 预过滤的立方体贴图// 图像VkImageCreateInfo imageCI = vks::initializers::imageCreateInfo();imageCI.imageType = VK_IMAGE_TYPE_2D;imageCI.format = format;imageCI.extent.width = dim;imageCI.extent.height = dim;imageCI.extent.depth = 1;imageCI.mipLevels = numMips;imageCI.arrayLayers = 6;imageCI.samples = VK_SAMPLE_COUNT_1_BIT;imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;imageCI.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;imageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &textures.irradianceCube.image));
  • 目的:创建用于存储辐照度立方体贴图的 Vulkan 图像对象。
  • 逐行分析
    • VkImageCreateInfo imageCI:初始化图像创建信息结构体。
    • imageType = VK_IMAGE_TYPE_2D:指定图像类型为 2D,尽管是立方体贴图,但 Vulkan 使用 2D 图像数组表示。
    • format:使用之前定义的浮点格式。
    • extent.width/height = dim:设置图像宽高为 64 像素,深度为 1。
    • mipLevels = numMips:设置mip级别数量。
    • arrayLayers = 6:立方体贴图有 6 个面(正负 X、Y、Z 方向)。
    • samples = VK_SAMPLE_COUNT_1_BIT:禁用多重采样,单次采样即可。
    • tiling = VK_IMAGE_TILING_OPTIMAL:使用最佳平铺模式,优化 GPU 访问。
    • usage:图像用于采样(VK_IMAGE_USAGE_SAMPLED_BIT)和作为传输目标(VK_IMAGE_USAGE_TRANSFER_DST_BIT)。
    • flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT:标记图像为立方体贴图兼容。
    • vkCreateImage:调用 Vulkan API 创建图像对象,存储到 textures.irradianceCube.image
    VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo();VkMemoryRequirements memReqs;vkGetImageMemoryRequirements(device, textures.irradianceCube.image, &memReqs);memAlloc.allocationSize = memReqs.size;memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &textures.irradianceCube.deviceMemory));VK_CHECK_RESULT(vkBindImageMemory(device, textures.irradianceCube.image, textures.irradianceCube.deviceMemory, 0));
  • 目的:为立方体贴图图像分配显存并绑定。
  • 逐行分析
    • VkMemoryAllocateInfo memAlloc:初始化显存分配信息。
    • vkGetImageMemoryRequirements:获取图像的显存需求(大小、对齐方式等)。
    • memAlloc.allocationSize = memReqs.size:设置分配的显存大小。
    • memAlloc.memoryTypeIndex:选择支持设备本地内存(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)的显存类型。
    • vkAllocateMemory:分配显存,存储到 textures.irradianceCube.deviceMemory
    • vkBindImageMemory:将分配的显存绑定到图像对象。
    // 图像视图VkImageViewCreateInfo viewCI = vks::initializers::imageViewCreateInfo();viewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE;viewCI.format = format;viewCI.subresourceRange = {};viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;viewCI.subresourceRange.levelCount = numMips;viewCI.subresourceRange.layerCount = 6;viewCI.image = textures.irradianceCube.image;VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &textures.irradianceCube.view));
  • 目的:创建图像视图,用于访问立方体贴图的图像数据。
  • 逐行分析
    • VkImageViewCreateInfo viewCI:初始化图像视图创建信息。
    • viewType = VK_IMAGE_VIEW_TYPE_CUBE:指定视图类型为立方体贴图。
    • format:使用与图像相同的格式。
    • subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT:指定访问颜色数据。
    • subresourceRange.levelCount = numMips:包括所有mip级别。
    • subresourceRange.layerCount = 6:包括立方体贴图的 6 个面。
    • viewCI.image:关联到之前创建的图像。
    • vkCreateImageView:创建图像视图,存储到 textures.irradianceCube.view
    // 采样器VkSamplerCreateInfo samplerCI = vks::initializers::samplerCreateInfo();samplerCI.magFilter = VK_FILTER_LINEAR;samplerCI.minFilter = VK_FILTER_LINEAR;samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;samplerCI.minLod = 0.0f;samplerCI.maxLod = static_cast<float>(numMips);samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;VK_CHECK_RESULT(vkCreateSampler(device, &samplerCI, nullptr, &textures.irradianceCube.sampler));
  • 目的:创建采样器,用于在着色器中采样立方体贴图。
  • 逐行分析
    • VkSamplerCreateInfo samplerCI:初始化采样器创建信息。
    • magFilter/minFilter = VK_FILTER_LINEAR:使用线性过滤进行放大和缩小,提供平滑的纹理采样。
    • mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR:启用线性 mip 过渡。
    • addressModeU/V/W = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:纹理坐标超出范围时,钳位到边缘,避免重复或镜像。
    • minLod/maxLod:设置 mip 级别的范围,从 0 到最大 mip 级别。
    • borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:设置边界颜色为不透明白色(当使用钳位时)。
    • vkCreateSampler:创建采样器,存储到 textures.irradianceCube.sampler
    textures.irradianceCube.descriptor.imageView = textures.irradianceCube.view;textures.irradianceCube.descriptor.sampler = textures.irradianceCube.sampler;textures.irradianceCube.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;textures.irradianceCube.device = vulkanDevice;
  • 目的:设置描述符,用于在着色器中绑定立方体贴图资源。
  • 逐行分析
    • descriptor.imageView:绑定图像视图。
    • descriptor.sampler:绑定采样器。
    • descriptor.imageLayout:设置图像布局为只读最优,适合在着色器中采样。
    • device = vulkanDevice:记录 Vulkan 设备指针。
    // 帧缓冲、附件、渲染通道等VkAttachmentDescription attDesc = {};attDesc.format = format;attDesc.samples = VK_SAMPLE_COUNT_1_BIT;attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
  • 目的:定义渲染通道的颜色附件。
  • 逐行分析
    • VkAttachmentDescription attDesc:初始化附件描述结构体。
    • format:使用与立方体贴图相同的格式。
    • samples = VK_SAMPLE_COUNT_1_BIT:单次采样。
    • loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR:加载时清除附件内容。
    • storeOp = VK_ATTACHMENT_STORE_OP_STORE:存储渲染结果。
    • stencilLoadOp/stencilStoreOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE:不使用模板缓冲。
    • initialLayout = VK_IMAGE_LAYOUT_UNDEFINED:初始布局未定义。
    • finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:最终布局为颜色附件最优。
    • VkAttachmentReference colorReference:定义颜色附件引用,索引为 0,布局为颜色附件最优。
    VkSubpassDescription subpassDescription = {};subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;subpassDescription.colorAttachmentCount = 1;subpassDescription.pColorAttachments = &colorReference;
  • 目的:定义子通道,指定渲染管线类型和颜色附件。
  • 逐行分析
    • VkSubpassDescription subpassDescription:初始化子通道描述。
    • pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS:指定图形管线。
    • colorAttachmentCount = 1:一个颜色附件。
    • pColorAttachments = &colorReference:绑定颜色附件引用。
    // 使用子通道依赖来管理布局转换std::array<VkSubpassDependency, 2> dependencies;dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;dependencies[0].dstSubpass = 0;dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;dependencies[1].srcSubpass = 0;dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
  • 目的:定义子通道依赖,确保正确的图像布局转换和同步。
  • 逐行分析
    • std::array<VkSubpassDependency, 2>:创建两个子通道依赖。
    • 第一个依赖(外部 -> 子通道 0):
      • srcSubpass = VK_SUBPASS_EXTERNAL:来源是外部子通道。
      • dstSubpass = 0:目标是第一个子通道。
      • srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT:等待管线底部完成。
      • dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT:目标阶段为颜色附件输出。
      • srcAccessMask = VK_ACCESS_MEMORY_READ_BIT:来源访问为内存读取。
      • dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT:目标访问为颜色附件读写。
      • dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT:按区域依赖,优化性能。
    • 第二个依赖(子通道 0 -> 外部):
      • 与第一个依赖相反,确保子通道完成后,数据可以被外部访问。
    // 渲染通道VkRenderPassCreateInfo renderPassCI = vks::initializers::renderPassCreateInfo();renderPassCI.attachmentCount = 1;renderPassCI.pAttachments = &attDesc;renderPassCI.subpassCount = 1;renderPassCI.pSubpasses = &subpassDescription;renderPassCI.dependencyCount = 2;renderPassCI.pDependencies = dependencies.data();VkRenderPass renderpass;VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderpass));
  • 目的:创建渲染通道,用于离屏渲染。
  • 逐行分析
    • VkRenderPassCreateInfo renderPassCI:初始化渲染通道创建信息。
    • attachmentCount = 1:一个附件(颜色附件)。
    • pAttachments = &attDesc:绑定附件描述。
    • subpassCount = 1:一个子通道。
    • pSubpasses = &subpassDescription:绑定子通道描述。
    • dependencyCount = 2:两个依赖。
    • pDependencies = dependencies.data():绑定依赖数组。
    • vkCreateRenderPass:创建渲染通道,存储到 renderpass
    struct {VkImage image;VkImageView view;VkDeviceMemory memory;VkFramebuffer framebuffer;} offscreen;
  • 目的:定义离屏渲染的帧缓冲结构,包含图像、视图、显存和帧缓冲。
  • 细节offscreen 结构体用于临时存储离屏渲染的资源。
    // 离屏帧缓冲{VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;imageCreateInfo.format = format;imageCreateInfo.extent.width = dim;imageCreateInfo.extent.height = dim;imageCreateInfo.extent.depth = 1;imageCreateInfo.mipLevels = 1;imageCreateInfo.arrayLayers = 1;imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;imageCreateInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &offscreen.image));
  • 目的:创建离屏渲染的颜色附件图像。
  • 逐行分析
    • imageCreateInfo:初始化图像创建信息。
    • imageType = VK_IMAGE_TYPE_2D:2D 图像。
    • format:使用相同的浮点格式。
    • extent.width/height = dim:尺寸为 64x64。
    • mipLevels = 1:离屏渲染不需要 mip 级别。
    • arrayLayers = 1:单层图像。
    • samples = VK_SAMPLE_COUNT_1_BIT:单次采样。
    • tiling = VK_IMAGE_TILING_OPTIMAL:优化 GPU 访问。
    • initialLayout = VK_IMAGE_LAYOUT_UNDEFINED:初始布局未定义。
    • usage:用作颜色附件(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)和传输源(VK_IMAGE_USAGE_TRANSFER_SRC_BIT)。
    • sharingMode = VK_SHARING_MODE_EXCLUSIVE:独占模式。
    • vkCreateImage:创建图像,存储到 offscreen.image
        VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo();VkMemoryRequirements memReqs;vkGetImageMemoryRequirements(device, offscreen.image, &memReqs);memAlloc.allocationSize = memReqs.size;memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offscreen.memory));VK_CHECK_RESULT(vkBindImageMemory(device, offscreen.image, offscreen.memory, 0));
  • 目的:为离屏图像分配显存并绑定。
  • 逐行分析:与之前立方体贴图的显存分配类似,分配设备本地显存并绑定到 offscreen.image
        VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo();colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D;colorImageView.format = format;colorImageView.flags = 0;colorImageView.subresourceRange = {};colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;colorImageView.subresourceRange.baseMipLevel = 0;colorImageView.subresourceRange.levelCount = 1;colorImageView.subresourceRange.baseArrayLayer = 0;colorImageView.subresourceRange.layerCount = 1;colorImageView.image = offscreen.image;VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &offscreen.view));
  • 目的:创建离屏图像的视图。
  • 逐行分析
    • viewType = VK_IMAGE_VIEW_TYPE_2D:2D 视图。
    • format:与图像格式一致。
    • subresourceRange:访问颜色数据,单 mip 级别,单层。
    • image = offscreen.image:关联到离屏图像。
    • vkCreateImageView:创建视图,存储到 offscreen.view
        VkFramebufferCreateInfo fbufCreateInfo = vks::initializers::framebufferCreateInfo();fbufCreateInfo.renderPass = renderpass;fbufCreateInfo.attachmentCount = 1;fbufCreateInfo.pAttachments = &offscreen.view;fbufCreateInfo.width = dim;fbufCreateInfo.height = dim;fbufCreateInfo.layers = 1;VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offscreen.framebuffer));
  • 目的:创建离屏帧缓冲。
  • 逐行分析
    • renderPass = renderpass:关联到之前创建的渲染通道。
    • attachmentCount = 1:一个附件(颜色附件)。
    • pAttachments = &offscreen.view:绑定离屏图像视图。
    • width/height = dim:帧缓冲尺寸为 64x64。
    • layers = 1:单层。
    • vkCreateFramebuffer:创建帧缓冲,存储到 offscreen.framebuffer
        VkCommandBuffer layoutCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);vks::tools::setImageLayout(layoutCmd,offscreen.image,VK_IMAGE_ASPECT_COLOR_BIT,VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);vulkanDevice->flushCommandBuffer(layoutCmd, queue, true);
  • 目的:将离屏图像的布局转换为颜色附件最优。
  • 逐行分析
    • createCommandBuffer:创建一个主命令缓冲区,开启录制。
    • setImageLayout:将 offscreen.image 的布局从未定义转换为颜色附件最优。
    • flushCommandBuffer:提交并执行命令缓冲区,完成布局转换。
    // 描述符VkDescriptorSetLayout irrdescriptorsetlayout;std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0),};VkDescriptorSetLayoutCreateInfo irrdescriptorsetlayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &irrdescriptorsetlayoutCI, nullptr, &irrdescriptorsetlayout));
  • 目的:创建描述符集布局,用于在着色器中绑定环境贴图。
  • 逐行分析
    • setLayoutBindings:定义一个绑定,类型为组合图像采样器(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER),用于片段着色器,绑定点为 0。
    • irrdescriptorsetlayoutCI:初始化描述符集布局创建信息。
    • vkCreateDescriptorSetLayout:创建描述符集布局,存储到 irrdescriptorsetlayout
    // 描述符池std::vector<VkDescriptorPoolSize> poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) };VkDescriptorPoolCreateInfo irrdescriptorPoolCI = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);VkDescriptorPool irrdescriptorpool;VK_CHECK_RESULT(vkCreateDescriptorPool(device, &irrdescriptorPoolCI, nullptr, &irrdescriptorpool));
  • 目的:创建描述符池,用于分配描述符集。
  • 逐行分析
    • poolSizes:指定池支持一个组合图像采样器。
    • irrdescriptorPoolCI:初始化描述符池创建信息,最大支持 2 个描述符集。
    • vkCreateDescriptorPool:创建描述符池,存储到 irrdescriptorpool
    // 描述符集VkDescriptorSet irrdescriptorset;VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(irrdescriptorpool, &irrdescriptorsetlayout, 1);VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &irrdescriptorset));VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(irrdescriptorset, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &textures.environmentCube.descriptor);vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr);
  • 目的:分配并更新描述符集,绑定环境立方体贴图。
  • 逐行分析
    • allocInfo:初始化描述符集分配信息,关联到描述符池和布局。
    • vkAllocateDescriptorSets:分配描述符集,存储到 irrdescriptorset
    • writeDescriptorSet:定义描述符写入操作,绑定环境立方体贴图的描述符(textures.environmentCube.descriptor)。
    • vkUpdateDescriptorSets:更新描述符集,完成绑定。
    // 管线布局struct PushBlock {glm::mat4 mvp;float deltaPhi = (2.0f * float(M_PI)) / 180.0f;float deltaTheta = (0.5f * float(M_PI)) / 64.0f;} pushBlock;
  • 目的:定义推送常量块,包含 MVP 矩阵和采样角度增量。
  • 逐行分析
    • PushBlock:结构体包含:
      • mvp:模型-视图-投影矩阵,用于变换。
      • deltaPhi:水平采样角度增量 (2π/180,约 2 度)。
      • deltaTheta:垂直采样角度增量 (π/2 / 64,约 1.4 度)。
    VkPipelineLayout irrpipelinelayout;std::vector<VkPushConstantRange> pushConstantRanges = {vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(PushBlock), 0),};VkPipelineLayoutCreateInfo irrpipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&irrdescriptorsetlayout, 1);irrpipelineLayoutCI.pushConstantRangeCount = 1;irrpipelineLayoutCI.pPushConstantRanges = pushConstantRanges.data();VK_CHECK_RESULT(vkCreatePipelineLayout(device, &irrpipelineLayoutCI, nullptr, &irrpipelinelayout));
  • 目的:创建管线布局,包含描述符集布局和推送常量。
  • 逐行分析
    • pushConstantRanges:定义推送常量范围,适用于顶点和片段着色器,大小为 PushBlock
    • irrpipelineLayoutCI:初始化管线布局创建信息,绑定描述符集布局。
    • pushConstantRangeCount = 1:一个推送常量范围。
    • pPushConstantRanges:绑定推送常量范围。
    • vkCreatePipelineLayout:创建管线布局,存储到 irrpipelinelayout
    // 管线VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE);VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL);VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1);VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT);std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
  • 目的:定义图形管线的各个状态。
  • 逐行分析
    • inputAssemblyState:使用三角形列表拓扑,无原始重启。
    • rasterizationState:填充多边形,无背面剔除,逆时针为正面。
    • blendAttachmentState:禁用颜色混合。
    • colorBlendState:绑定一个颜色混合附件状态。
    • depthStencilState:禁用深度和模板测试。
    • viewportState:支持一个视口和裁剪矩形。
    • multisampleState:禁用多重采样。
    • dynamicStateEnables:启用动态视口和裁剪矩形。
    • shaderStages:定义两个着色器阶段(顶点和片段)。
    VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(irrpipelinelayout, renderpass);pipelineCI.pInputAssemblyState = &inputAssemblyState;pipelineCI.pRasterizationState = &rasterizationState;pipelineCI.pColorBlendState = &colorBlendState;pipelineCI.pMultisampleState = &multisampleState;pipelineCI.pViewportState = &viewportState;pipelineCI.pDepthStencilState = &depthStencilState;pipelineCI.pDynamicState = &dynamicState;pipelineCI.stageCount = 2;pipelineCI.pStages = shaderStages.data();pipelineCI.renderPass = renderpass;pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::UV });
  • 目的:初始化图形管线创建信息。
  • 逐行分析
    • pipelineCI:绑定管线布局和渲染通道。
    • 绑定各个管线状态(输入装配、光栅化、颜色混合等)。
    • stageCount = 2:两个着色器阶段。
    • pStages = shaderStages.data():绑定着色器阶段数组。
    • pVertexInputState:定义顶点输入状态,包含位置、法线和 UV 坐标。
    shaderStages[0] = loadShader(getShadersPath() + "lightprobesh/filtercube.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);shaderStages[1] = loadShader(getShadersPath() + "lightprobesh/irradiancecube.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);VkPipeline pipeline;VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
  • 目的:加载着色器并创建图形管线。
  • 逐行分析
    • shaderStages[0]:加载顶点着色器 (filtercube.vert.spv)。
    • shaderStages[1]:加载片段着色器 (irradiancecube.frag.spv),用于辐照度计算。
    • vkCreateGraphicsPipelines:创建图形管线,存储到 pipeline
    // 渲染VkClearValue clearValues[1];clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } };
  • 目的:定义清除颜色,用于渲染开始时清除帧缓冲。
  • 细节:清除颜色为深蓝色 (RGB: 0, 0, 0.2, Alpha: 0)。
    VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();renderPassBeginInfo.renderPass = renderpass;renderPassBeginInfo.framebuffer = offscreen.framebuffer;renderPassBeginInfo.renderArea.extent.width = dim;renderPassBeginInfo.renderArea.extent.height = dim;renderPassBeginInfo.clearValueCount = 1;renderPassBeginInfo.pClearValues = clearValues;
  • 目的:初始化渲染通道开始信息。
  • 逐行分析
    • renderPass = renderpass:绑定离屏渲染通道。
    • framebuffer = offscreen.framebuffer:绑定离屏帧缓冲。
    • renderArea.extent:渲染区域为 64x64。
    • clearValueCount = 1:一个清除值。
    • pClearValues = clearValues:绑定清除颜色。
    std::vector<glm::mat4> matrices = {glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)),glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)),glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f)),};
  • 目的:定义立方体贴图六个面的视图矩阵。
  • 细节:每个矩阵对应一个面(+X, -X, +Y, -Y, +Z, -Z),通过旋转矩阵调整视角方向。
    VkCommandBuffer cmdBuf = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);VkViewport viewport = vks::initializers::viewport((float)dim, (float)dim, 0.0f, 1.0f);VkRect2D scissor = vks::initializers::rect2D(dim, dim, 0, 0);vkCmdSetViewport(cmdBuf, 0, 1, &viewport);vkCmdSetScissor(cmdBuf, 0, 1, &scissor);
  • 目的:创建命令缓冲区并设置初始视口和裁剪矩形。
  • 逐行分析
    • createCommandBuffer:创建主命令缓冲区并开始录制。
    • viewport:设置视口为 64x64。
    • scissor:设置裁剪矩形为 64x64。
    • vkCmdSetViewport/Scissor:应用视口和裁剪设置。
    VkImageSubresourceRange subresourceRange = {};subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;subresourceRange.baseMipLevel = 0;subresourceRange.levelCount = numMips;subresourceRange.layerCount = 6;vks::tools::setImageLayout(cmdBuf,textures.irradianceCube.image,VK_IMAGE_LAYOUT_UNDEFINED,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,subresourceRange);
  • 目的:将立方体贴图的布局转换为传输目标。
  • 逐行分析
    • subresourceRange:指定颜色数据,所有 mip 级别和 6 个面。
    • setImageLayout:将图像布局从未定义转换为传输目标最优,准备接收渲染数据。
    for (uint32_t m = 0; m < numMips; m++) {for (uint32_t f = 0; f < 6; f++) {viewport.width = static_cast<float>(dim * std::pow(0.5f, m));viewport.height = static_cast<float>(dim * std::pow(0.5f, m));vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
  • 目的:为每个 mip 级别和每个面进行渲染,动态调整视口大小。
  • 逐行分析
    • 双重循环遍历所有 mip 级别和 6 个面。
    • viewport.width/height:根据 mip 级别缩小尺寸(每级减半)。
    • vkCmdSetViewport:更新视口大小。
            vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);pushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f];vkCmdPushConstants(cmdBuf, irrpipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushBlock), &pushBlock);vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, irrpipelinelayout, 0, 1, &irrdescriptorset, 0, NULL);models.skybox.draw(cmdBuf);vkCmdEndRenderPass(cmdBuf);
  • 目的:执行渲染,生成每个面的辐照度贴图。
  • 逐行分析
    • vkCmdBeginRenderPass:开始渲染通道,内联执行子通道内容。
    • pushBlock.mvp:计算投影矩阵(90 度 FOV,1:1 宽高比,近裁剪 0.1,远裁剪 512)与视图矩阵的乘积。
    • vkCmdPushConstants:推送常量数据到着色器。
    • vkCmdBindPipeline:绑定图形管线。
    • vkCmdBindDescriptorSets:绑定描述符集,包含环境贴图。
    • models.skybox.draw:绘制天空盒,执行辐照度计算。
    • vkCmdEndRenderPass:结束渲染通道。
            vks::tools::setImageLayout(cmdBuf,offscreen.image,VK_IMAGE_ASPECT_COLOR_BIT,VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
  • 目的:将离屏图像布局转换为传输源,准备复制数据。
            VkImageCopy copyRegion = {};copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;copyRegion.srcSubresource.baseArrayLayer = 0;copyRegion.srcSubresource.mipLevel = 0;copyRegion.srcSubresource.layerCount = 1;copyRegion.srcOffset = { 0, 0, 0 };copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;copyRegion.dstSubresource.baseArrayLayer = f;copyRegion.dstSubresource.mipLevel = m;copyRegion.dstSubresource.layerCount = 1;copyRegion.dstOffset = { 0, 0, 0 };copyRegion.extent.width = static_cast<uint32_t>(viewport.width);copyRegion.extent.height = static_cast<uint32_t>(viewport.height);copyRegion.extent.depth = 1;vkCmdCopyImage(cmdBuf,offscreen.image,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,textures.irradianceCube.image,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,1,&copyRegion);
  • 目的:将离屏渲染结果复制到立方体贴图的对应面和 mip 级别。
  • 逐行分析
    • copyRegion:定义图像复制区域。
    • srcSubresource:源为离屏图像,单 mip 级别,单层。
    • dstSubresource:目标为立方体贴图的第 f 面,第 m 个 mip 级别。
    • extent:复制区域大小与当前视口一致。
    • vkCmdCopyImage:执行图像复制操作。
            vks::tools::setImageLayout(cmdBuf,offscreen.image,VK_IMAGE_ASPECT_COLOR_BIT,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
  • 目的:将离屏图像布局转换回颜色附件最优,为下一次渲染做准备。
    vks::tools::setImageLayout(cmdBuf,textures.irradianceCube.image,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,subresourceRange);
  • 目的:将立方体贴图的布局转换为着色器只读最优,完成渲染。
    vulkanDevice->flushCommandBuffer(cmdBuf, queue);
  • 目的:提交并执行命令缓冲区,完成所有渲染和复制操作。
    vkDestroyRenderPass(device, renderpass, nullptr);vkDestroyFramebuffer(device, offscreen.framebuffer, nullptr);vkFreeMemory(device, offscreen.memory, nullptr);vkDestroyImageView(device, offscreen.view, nullptr);vkDestroyImage(device, offscreen.image, nullptr);vkDestroyDescriptorPool(device, irrdescriptorpool, nullptr);vkDestroyDescriptorSetLayout(device, irrdescriptorsetlayout, nullptr);vkDestroyPipeline(device, pipeline, nullptr);vkDestroyPipelineLayout(device, irrpipelinelayout, nullptr);
  • 目的:清理所有临时 Vulkan 资源。
  • 逐行分析:销毁渲染通道、帧缓冲、显存、图像视图、图像、描述符池、描述符集布局、管线和管线布局,释放资源。
    auto tEnd = std::chrono::high_resolution_clock::now();auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();std::cout << "Generating irradiance cube with " << numMips << " mip levels took " << tDiff << " ms" << std::endl;
}
  • 目的:记录结束时间,计算并输出生成辐照度立方体贴图的耗时。
  • 逐行分析
    • tEnd:记录结束时间。
    • tDiff:计算时间差(毫秒)。
    • std::cout:输出 mip 级别数量和总耗时。

总结

generateIrradianceCube 函数通过 Vulkan API 创建并渲染一个辐照度立方体贴图,用于 PBR 光照计算。主要步骤包括:

  1. 创建立方体贴图图像、视图和采样器。
  2. 设置离屏渲染的帧缓冲和渲染通道。
  3. 配置描述符和管线,加载着色器。
  4. 为每个 mip 级别和每个面渲染天空盒,生成辐照度数据。
  5. 将渲染结果复制到立方体贴图。
  6. 清理临时资源并输出执行时间。

每个步骤都通过 Vulkan 的资源管理和渲染管线精心组织,确保高效生成高质量的辐照度贴图。

http://www.wxhsa.cn/company.asp?id=3855

相关文章:

  • 使用Putty远程连接树莓派5提示No supported authentication methods available
  • [USACO24FEB] Maximizing Productivity
  • 记录一个纯CSS实现滚动驱动动画的效果
  • 第一周个人作业——我
  • Apache IoTDB V1.3.5 发布|优化加密算法,优化内核稳定性,修复社区反馈问题 - 详解
  • Acrobat Pro DC 2025破解版安装下载教程,附永久免费免中文破解版(稳定版安装包)
  • 20250914
  • 25秋周总结2
  • 华擎、微星、华硕BIOS阵脚线序及杜邦现自制刷机线
  • Ubuntu 安装 VLC
  • AT_abc422_f [ABC422F] Eat and Ride 题解
  • 模拟赛 R14
  • Java并发编程(2)
  • 完整教程:WebApp 的价值与实现:从浏览器架构到用户体验优化
  • Ubuntu 安装百度网盘
  • 八字喜用神起名大师 API 接口
  • 在CentOS 7上集成cJSON库的方法
  • 作业1
  • 网站截图与 HTML 快照 API 接口
  • 深入解析:精确位置定位,AR交互助力高效作业流程​
  • sdjaivkdshwqeofhsoejbc dfb vnhgtbv
  • 开篇自我介绍随笔
  • 第八周
  • Tita 项目一体化管理:驱动项目全周期高效运营的引擎
  • 飞行 NED坐标系(北东地坐标系):
  • windows与linux环境下网络编程
  • 在飞牛系统中通过docker形式部署Nginx proxy manager
  • Es索引同步异步Canal解耦方案
  • 在Ubuntu上配置phpMyAdmin和WordPress环境
  • “四人过河”经典问题