Vulkan_Ray Tracing 13_Callable Shader

本文主要参考NVIDIA Vulkan Ray Tracing Tutorial教程,环境配置与程序均可参照此文档执行(个人水平有限,如有错误请参照原文)。

一、可调用着色器

可调用着色器可以访问一个可调用的有效荷载,类似光线有效荷载值的使用来处理光追程序。 并且可调用着色器通过从一个允许的着色器阶段调用OpExecuteCallableKHR来执行。

光线追踪允许在光线生成,最近命中,未命中或其他可调用着色器阶段使用可调用着色器。它类似于间接函数调用,无需将这些着色器与可执行程序链接。

1.1 数据存储

数据只能访问从父阶段(上阶段)传入可调用对象的数据。每次只有一个结构传递,并且应该像有效载荷一样定义。

在父阶段(上阶段),使用callableDataEXT存储限定符数据,它可以声明为以下模样:

layout(location = 0) callableDataEXT rayLight cLight;

布局(位置 = 0) callableDataEXT rayLight cLight;
其中rayLight结构体在公共文件中定义为:

struct rayLight
{
  vec3  inHitPosition;
  float outLightDistance;
  vec3  outLightDir;
  float outIntensity;
};

之后在传入的可调用着色器中,您必须使用callableDataInEXT存储限定符数据。

layout(location = 0) callableDataInEXT rayLight cLight;

1.2 执行

要执行可调用着色器,父阶段(上阶段)需要调用executeCallableEXT函数

executeCallableEXT(pushC.lightType, 0);
  • 第一个参数是 SBT 记录索引
  • 第二个参数对应于“位置”索引

二、向 SBT 添加可调用着色器

2.1 创建着色器模块

在 HelloVulkan::createRtPipeline() 中,在添加最近命中着色器之后,我们将立即为每种类型的光添加 3 个可调用着色器。

首先,创建着色器模块

  enum StageIndices
  {
    eRaygen,
    eMiss,
    eMiss2,
    eClosestHit,
    eCall0,
    eCall1,
    eCall2,
    eShaderGroupCount
  };

  ...
  // Call0
  stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_point.rcall.spv", true, defaultSearchPaths, true));
  stage.stage    = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
  stages[eCall0] = stage;
  // Call1
  stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_spot.rcall.spv", true, defaultSearchPaths, true));
  stage.stage    = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
  stages[eCall1] = stage;
  // Call2
  stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_inf.rcall.spv", true, defaultSearchPaths, true));
  stage.stage    = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
  stages[eCall2] = stage;

然后是 3 组可调用着色器以及与之相关的阶段。

  // 可调用着色器
  group.type             = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
  group.closestHitShader = VK_SHADER_UNUSED_KHR;
  group.generalShader    = eCall0;
  m_rtShaderGroups.push_back(group);
  group.generalShader = eCall1;
  m_rtShaderGroups.push_back(group);
  group.generalShader = eCall2;
  m_rtShaderGroups.push_back(group);

2.2 着色器源码

light_point.rcall

#version 460 core
#extension GL_EXT_ray_tracing : enable
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"

layout(location = 0) callableDataInEXT rayLight cLight;

layout(push_constant) uniform Constants
{
  vec4  clearColor;
  vec3  lightPosition;
  float lightIntensity;
  vec3  lightDirection;
  float lightSpotCutoff;
  float lightSpotOuterCutoff;
  int   lightType;
};

void main()
{
  vec3 lDir               = lightPosition - cLight.inHitPosition;
  cLight.outLightDistance = length(lDir);
  cLight.outIntensity     = lightIntensity / (cLight.outLightDistance * cLight.outLightDistance);
  cLight.outLightDir      = normalize(lDir);
}

light_spot.rcall

#version 460 core
#extension GL_EXT_ray_tracing : enable
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"

layout(location = 0) callableDataInEXT rayLight cLight;

layout(push_constant) uniform Constants
{
  vec4  clearColor;
  vec3  lightPosition;
  float lightIntensity;
  vec3  lightDirection;
  float lightSpotCutoff;
  float lightSpotOuterCutoff;
  int   lightType;
};

void main()
{
  vec3 lDir               = lightPosition - cLight.inHitPosition;
  cLight.outLightDistance = length(lDir);
  cLight.outIntensity     = lightIntensity / (cLight.outLightDistance * cLight.outLightDistance);
  cLight.outLightDir      = normalize(lDir);
  float theta             = dot(cLight.outLightDir, normalize(-lightDirection));
  float epsilon           = lightSpotCutoff - lightSpotOuterCutoff;
  float spotIntensity     = clamp((theta - lightSpotOuterCutoff) / epsilon, 0.0, 1.0);
  cLight.outIntensity *= spotIntensity;
}

light_inf.rcall

#version 460 core
#extension GL_EXT_ray_tracing : enable
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"

layout(location = 0) callableDataInEXT rayLight cLight;

layout(push_constant) uniform Constants
{
  vec4  clearColor;
  vec3  lightPosition;
  float lightIntensity;
  vec3  lightDirection;
  float lightSpotCutoff;
  float lightSpotOuterCutoff;
  int   lightType;
};

void main()
{
  cLight.outLightDistance = 10000000;
  cLight.outIntensity     = 1.0;
  cLight.outLightDir      = normalize(-lightDirection);
}

三、着色器绑定表

在本例中,我们将使用nvvk::SBTWrapper来创建光线追踪管线,并为着色绑定表创建缓冲区。

在头文件hello_vulkan.h中,需要添加其头文件并添加一个新成员。

#include "nvvk/sbtwrapper_vk.hpp"
...
nvvk::SBTWrapper m_sbtWrapper;

在 HelloVulkan::initRayTracing() 中,按以下方式对其进行初始化。

m_sbtWrapper.setup(m_device, m_graphicsQueueIndex, &m_alloc, m_rtProperties);

在HelloVulkan::createRtPipeline()中,在创建对vkCreateRayTracingPipelinesKHR()的管道调用之后,立即使用以下命令创建SBT。

  m_sbtWrapper.create(m_rtPipeline, rayPipelineInfo);

在 HelloVulkan::raytrace() 中,我们必须告诉可调用着色器从哪里开始。由于它们是在命中着色器之后添加的,因此我们在 SBT 中添加以下内容。
在这里插入图片描述

SBT 封装类中可以直接返回我们需要的信息。因此,我们可以直接VkStridedDeviceAddressRegionKHR获取每个组类型的,而不是计算各种偏移量 。

四、调用着色器

在最近命中着色器中,我们现在可以根据光的类型直接调用对应的着色器,而不是使用 if-else 情况。

cLight.inHitPosition = worldPos;
//#define DONT_USE_CALLABLE
#if defined(DONT_USE_CALLABLE)
  // Point light
  if(pushC.lightType == 0)
  {
    vec3  lDir              = pushC.lightPosition - cLight.inHitPosition;
    float lightDistance     = length(lDir);
    cLight.outIntensity     = pushC.lightIntensity / (lightDistance * lightDistance);
    cLight.outLightDir      = normalize(lDir);
    cLight.outLightDistance = lightDistance;
  }
  else if(pushC.lightType == 1)
  {
    vec3 lDir               = pushC.lightPosition - cLight.inHitPosition;
    cLight.outLightDistance = length(lDir);
    cLight.outIntensity =
        pushC.lightIntensity / (cLight.outLightDistance * cLight.outLightDistance);
    cLight.outLightDir  = normalize(lDir);
    float theta         = dot(cLight.outLightDir, normalize(-pushC.lightDirection));
    float epsilon       = pushC.lightSpotCutoff - pushC.lightSpotOuterCutoff;
    float spotIntensity = clamp((theta - pushC.lightSpotOuterCutoff) / epsilon, 0.0, 1.0);
    cLight.outIntensity *= spotIntensity;
  }
  else  // Directional light
  {
    cLight.outLightDir      = normalize(-pushC.lightDirection);
    cLight.outIntensity     = 1.0;
    cLight.outLightDistance = 10000000;
  }
#else
  executeCallableEXT(pushC.lightType, 0);
#endif

对应执行效果如下:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值