Vulkan_Ray Tracing 09_反射

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

一、场景构建

首先,我们创建一个具有:两个反射长方体和一个模型的场景。

在main函数中将之前加载模型的地方更改为:

  //创建示例
  helloVk.loadModel(nvh::findFile( " media/scenes/cube.obj " , defaultSearchPaths, true ),
                     nvmath::translation_mat4 (nvmath::vec3f(- 2 , 0 , 0 ))
                        * nvmath::scale_mat4(nvmath::vec3f(. 1f , 5 .f, 5 .f)));
  helloVk.loadModel(nvh::findFile( " media/scenes/cube.obj " , defaultSearchPaths, true ),
                     nvmath::translation_mat4 (nvmath::vec3f( 2 , 0 , 0 ))
                        * nvmath::scale_mat4(nvmath::vec3f(. 1f , 5 .f, 5 .f)));
  helloVk.loadModel(nvh::findFile( " media/scenes/cube_multi.obj " , defaultSearchPaths, true ));
  helloVk.loadModel(nvh::findFile( " media/scenes/plane.obj " , defaultSearchPaths, true ),
                     nvmath::translation_mat4 (nvmath::vec3f( 0 , - 1 , 0 )));

然后将cube.mtl中材质信息修改为具有95%的反射特性的:

newmtl  cube_instance_material
illum 3
d 1  
Ns 32
Ni 0
Ka 0 0 0
Kd .1 .8 .3
Ks 0.95 0.95 0.95

二、递归反射

Vulkan的光线追踪功能允许对 traceRayEXT 进行递归调用,最多由VkPhysicalDeviceRayTracingPropertiesKHR结构体中的maxRecursionDepth确定。

在createRtPipeline() 中,将最大递归深度提高到 10,并确保不超过物理设备的最大递归限制:

  rayPipelineInfo.maxPipelineRayRecursionDepth  = std::max(10u, m_rtProperties.maxRecursionDepth);  // Ray depth

2.1 修改raycommon.glsl

我们需要确定光线追踪的深度和衰减。所以对raycommon.glsl中的hitPayload 结构体我们添加以下内容:

  int  depth;
  vec3 attenuation;

2.2 修改raytrace.rgen

在光线生成着色器中,我们将在调用traceRayEXT

  prd.depth = 0 ;
  prd.hitValue = vec3 ( 0 );
  prd.attenuation = vec3( 1 .f, 1 .f, 1 .f);

2.3 修改raytrace.rchit

在最近命中着色器的末尾,且在设置prd.hitValue之前,如果材质是反射的,我们需要发射光线:

  // 反射
  if(mat.illum == 3 && prd.depth < 10)
  {
    vec3 origin   = worldPos;
    vec3 rayDir   = reflect(gl_WorldRayDirectionEXT, normal);
    prd.attenuation *= mat.specular;

    prd.depth++;
    traceRayEXT(topLevelAS,         // acceleration structure
            gl_RayFlagsNoneEXT,  // rayFlags
            0xFF,               // cullMask
            0,                  // sbtRecordOffset
            0,                  // sbtRecordStride
            0,                  // missIndex
            origin,             // ray origin
            0.1,                // ray min range
            rayDir,             // ray direction
            100000.0,           // ray max range
            0                   // payload (location = 0)
    );
    prd.depth--;
  }

由于反射的原因,每次递归计算出来的hitValue都需要累加,因为payload对于raygen光追的整个执行都是全局的,所以在main()中需要把最后一行改为:

prd.hitValue += vec3(attenuation * lightIntensity * (diffuse + specular)) * prd.attenuation;

2.4 修改raytrace.rmiss

最后,在未命中着色器中光线能量衰减也会产生对应的影响:

  prd.hitValue = clearColor.xyz * 0.8 * prd.attenuation;

三、迭代反射

上述代码可以完成反射功能,但它受限于 GPU 可以执行的递归次数,并且还可能影响性能。而且设置超过递归的限制的数据将最终会产生vulkan运行错误。

如果需要的话,我们将返回有效光路载荷中的信息来发发出新光线,而不是从最近命中着色器发出新光线。

  • 修改raycommon.glsl

在结构体中新增以下信息来为光追做准备

  int  done;
  vec3 rayOrigin;
  vec3 rayDir;
  • 修改raytrace.rgen
    在光线生产着色器中进行初始化有效光路载荷值:
  prd.done        = 1;
  prd.rayOrigin   = origin.xyz;
  prd.rayDir      = direction.xyz;

之后我们不止调用一次 traceRayEXT,而是在循环中调用它,直到完成为止。

我们在raytrace.rgen中这样进行调用:

  vec3 hitValue = vec3(0);
  for(;;)
  {
    traceRayEXT( /*.. */);

    hitValue += prd.hitValue * prd.attenuation;

    prd.depth++;
    if(prd.done == 1 || prd.depth >= 10)
      break;

    origin.xyz    = prd.rayOrigin;
    direction.xyz = prd.rayDir;
    prd.done      = 1; // Will stop if a reflective material isn't hit
  }

并在最后确保写入正确的值:

imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 1.0));
  • 修改raytrace.rchit

我们不再需要从最近命中着色器发射光线,所以我们可以用:

  if(mat.illum == 3)
  {
    vec3 origin = worldPos;
    vec3 rayDir = reflect(gl_WorldRayDirectionEXT, normal);
    prd.attenuation *= mat.specular;
    prd.done      = 0;
    prd.rayOrigin = origin;
    prd.rayDir    = rayDir;
  }

hitValue 值的计算也不再需要累加,且需要考虑衰减:

  prd.hitValue = vec3(attenuation * lightIntensity * (diffuse + specular));
  • 修改raytrace.rmiss
    由于光线生成着色器现在处理了衰减,因此我们不再需要衰减未命中着色器中返回的值:
  prd.hitValue = clearColor.xyz * 0.8 ;
  • 最大递归
    最后,我们不再需要在createRtPipeline中设置深度递归,只需要设置深度为 2:一个用于初始光线生成,另一个用于阴影光线。
  rayPipelineInfo.maxPipelineRayRecursionDepth = 2 ;  //光线深度

在raytrace.rgen中,我们现在可以增大最大光线深度而不会导致vulkan设备运行报错。

  • 控制深度

在PushConstantRay结构体中,我们可以添加一个新maxDepth成员来传递给着色器。

struct PushConstantRay
{
  vec4  clearColor;
  vec3  lightPosition;
  float lightIntensity;
  int   lightType;
  int   maxDepth;
};

我们可以将默认值设置为 10

PushConstantRay m_pcRay{{}, {}, 0, 0, 10};

在raytrace.rgen着色器中,我们添加何时停止的条件:

    if(prd.done == 1 || prd.depth >= pushC.maxDepth)
      break;

之后运行可见如下效果:

递归深度为1:
在这里插入图片描述

递归深度为2:
在这里插入图片描述

递归深度为3:
在这里插入图片描述

递归深度为4:
在这里插入图片描述
递归深度为5:在这里插入图片描述

递归深度为10:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值