Vulkan_Ray Tracing 11_Intersection Shader(相交着色器)

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

本部分主要展示如何使用相交着色器及不同的材质渲染不同的图元。
首先我们需要明白什么时候使用这个可选的着色器,具体可参照光追渲染管线部分的说明。

一、使用场景

本部分设计使用

  • 在 BLAS 中添加 2000000 个轴对齐的边界框
  • 场景中添加2种材质
  • 每隔一个相交对象创建一个球体或一个立方体,并将使用上述两种材料之一。

因此,我们需要:

  • 添加相交着色器 (.rint)
  • 添加新的最近命中着色器 (.chit)
  • 从VkAccelerationStructureGeometryAabbsDataKHR创建VkAccelerationStructureGeometryKHR。

二、创建数据

在 host_device.h 中,添加我们需要的结构体。首先是定义球体的结构体。并且它也可以用于定义AABB框。此信息将在相交着色器中检索并返回相交点信息。

struct Sphere
{
  vec3  center;
  float radius;
};

然后我们需要保存所有球体的 AABB 结构体,也用于创建 BLAS ( VK_GEOMETRY_TYPE_AABBS_KHR)。

struct Aabb
{
  vec3 minimum;
  vec3 maximum;
};

之后添加以下宏定义以区分球体和正方体

#define KIND_SPHERE 0
#define KIND_CUBE 1

以上所有信息都需要保存在缓冲区中,并且可供着色器访问。

  std::vector<Sphere> m_spheres;                //所有球体
  nvvkBuffer m_spheresBuffer;          //保存球体的缓冲区
  nvvkBuffer m_spheresAabbBuffer;      //所有 Aabb 的缓冲区
  nvvkBuffer m_spheresMatColorBuffer;  //多种材质
  nvvkBuffer m_spheresMatIndexBuffer;  //定义哪个球体使用哪种材料

最后,定义两个函数:一个创建球体,一个创建 BLAS 的中间数据,类似之前的函数objectToVkGeometryKHR()。

  void  createSpheres ();
  auto  sphereToVkGeometryKHR ();

之后将以随机位置和半径创建 2000000 个球体。并且根据球体定义创建 Aabb包围盒,并且两种材料将交替赋值给每个对象。且以上所有创建的信息都将移动到 Vulkan 缓冲区,以便求交和最近命中着色器可访问。


void HelloVulkan::createSpheres(uint32_t nbSpheres)
{
  std::random_device                    rd{};
  std::mt19937                          gen{rd()};
  std::normal_distribution<float>       xzd{0.f, 5.f};
  std::normal_distribution<float>       yd{6.f, 3.f};
  std::uniform_real_distribution<float> radd{.05f, .2f};

  // 球数据
  m_spheres.resize(nbSpheres);
  for(uint32_t i = 0; i < nbSpheres; i++)
  {
    Sphere s;
    s.center     = nvmath::vec3f(xzd(gen), yd(gen), xzd(gen));
    s.radius     = radd(gen);
    m_spheres[i] = std::move(s);
  }

  //  每个球体的轴对齐包围盒
  std::vector<Aabb> aabbs;
  aabbs.reserve(nbSpheres);
  for(const auto& s : m_spheres)
  {
    Aabb aabb;
    aabb.minimum = s.center - nvmath::vec3f(s.radius);
    aabb.maximum = s.center + nvmath::vec3f(s.radius);
    aabbs.emplace_back(aabb);
  }

  // 两种材质
  MaterialObj mat;
  mat.diffuse = nvmath::vec3f(0.2, 1, 0.2);
  std::vector<MaterialObj> materials;
  std::vector<int>         matIdx(nbSpheres);
  materials.emplace_back(mat);
  mat.diffuse = nvmath::vec3f(1, 0.5, 0);
  materials.emplace_back(mat);

  // 指定给每个球体的材质(shader中会根据i%2判断类型)
  for(size_t i = 0; i < m_spheres.size(); i++)
  {
    matIdx[i] = i % 2;
  }

  // 创建所有缓冲区
  using vkBU = VkBufferUsageFlagBits;
  nvvk::CommandPool genCmdBuf(m_device, m_graphicsQueueIndex);
  auto              cmdBuf = genCmdBuf.createCommandBuffer();
  m_spheresBuffer          = m_alloc.createBuffer(cmdBuf, m_spheres, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
  m_spheresAabbBuffer      = m_alloc.createBuffer(cmdBuf, aabbs,
                                             VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
                                                 | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR);
  m_spheresMatIndexBuffer =
      m_alloc.createBuffer(cmdBuf, matIdx, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
  m_spheresMatColorBuffer =
      m_alloc.createBuffer(cmdBuf, materials, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
  genCmdBuf.submitAndWait(cmdBuf);

  // Nsight调试数据
  m_debug.setObjectName(m_spheresBuffer.buffer, "spheres");
  m_debug.setObjectName(m_spheresAabbBuffer.buffer, "spheresAabb");
  m_debug.setObjectName(m_spheresMatColorBuffer.buffer, "spheresMat");
  m_debug.setObjectName(m_spheresMatIndexBuffer.buffer, "spheresMatIdx");

  // 添加一个额外的实例来访问材质缓冲区  
  ObjDesc objDesc{};
  objDesc.materialAddress      = nvvk::getBufferDeviceAddress(m_device, m_spheresMatColorBuffer.buffer);
  objDesc.materialIndexAddress = nvvk::getBufferDeviceAddress(m_device, m_spheresMatIndexBuffer.buffer);
  m_objDesc.emplace_back(objDesc);

  ObjInstance instance{};
  instance.objIndex = static_cast<uint32_t>(m_objModel.size());
  m_instances.emplace_back(instance);
}

之后不要忘记在destroyResources()中销毁缓冲区

  m_alloc.destroy(m_spheresBuffer); 
  m_alloc.destroy(m_spheresAabbBuffer); 
  m_alloc.destroy(m_spheresMatColorBuffer); 
  m_alloc.destroy(m_spheresMatIndexBuffer);

我们需要一个新的底层加速结构 (BLAS) 来存储上述球体或正方体数据。为了提高效率并且由于所有的图元都是静态的,因此我们将他们都添加到单个 BLAS 中。

与原有管线中默认三角形图元相比,我们在求交着色器中改变的是基本图元的 Aabb 数据(参见 Aabb 结构)和几何类型 ( VK_GEOMETRY_TYPE_AABBS_KHR)。

//--------------------------------------------------------------------------------------------------
// 返回用于创建的BLAS光线追踪几何数据,包含所有球体
//
auto HelloVulkan::sphereToVkGeometryKHR()
{
  VkDeviceAddress dataAddress = nvvk::getBufferDeviceAddress(m_device, m_spheresAabbBuffer.buffer);  

  VkAccelerationStructureGeometryAabbsDataKHR aabbs{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR};
  aabbs.data.deviceAddress = dataAddress;
  aabbs.stride             = sizeof(Aabb);

  // 设置加速结构需要用到的构建信息
  VkAccelerationStructureGeometryKHR asGeom{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
  asGeom.geometryType   = VK_GEOMETRY_TYPE_AABBS_KHR;
  asGeom.flags          = VK_GEOMETRY_OPAQUE_BIT_KHR;
  asGeom.geometry.aabbs = aabbs;

  VkAccelerationStructureBuildRangeInfoKHR offset{};
  offset.firstVertex     = 0;
  offset.primitiveCount  = (uint32_t)m_spheres.size();  
  offset.primitiveOffset = 0;
  offset.transformOffset = 0;

  nvvk::RaytracingBuilderKHR::BlasInput input;
  input.asGeometry.emplace_back(asGeom);
  input.asBuildOffsetInfo.emplace_back(offset);
  return input;
}

最后在main.cpp我们加载 OBJ 模型,我们可以用

  //创建示例
  helloVk.loadModel(nvh::findFile( " media/scenes/plane.obj " , defaultSearchPaths, true )); 
  helloVk.createSpheres( 2000000 );

⚠️注意:可能有更多的 OBJ 模型,但由于我们构建 TLAS 的方式,需要在所有这些模型之后添加球体。

场景变大,因此需要设置相机

  CameraManip.setLookat (nvmath :: vec3f ( 20 , 20 , 20 ), nvmath :: vec3f ( 0 , 1 , 0 ), nvmath :: vec3f ( 0 , 1 , 0 ));

三、创建加速结构

3.1 BLAS

函数createBottomLevelAS()为每个 OBJ 创建一个 BLAS,以下修改将添加一个新的 BLAS,其中包含所有球体的 Aabb包围盒信息。

void HelloVulkan::createBottomLevelAS()
{
  // BLAS - 将每个图元存储在几何体中
  std::vector<nvvk::RaytracingBuilderKHR::BlasInput> allBlas;
  allBlas.reserve(m_objModel.size());
  for(const auto& obj : m_objModel)
  {
    auto blas = objectToVkGeometryKHR(obj);

    // 我们可以在每个 BLAS 中添加多个几何体,目前我们只添加一个
    allBlas.emplace_back(blas);
  }

  // Spheres
  {
    auto blas = sphereToVkGeometryKHR();
    allBlas.emplace_back(blas);
  }

  m_rtBuilder.buildBlas(allBlas, VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR);
}

3.2 TLAS

同样在 createTopLevelAS() 中,顶部加速结构需要添加对球体 BLAS 的引用。我们将 instanceCustomId 和 blasId 设置为最后一个元素,这就是为什么必须在其他所有元素之后添加球体 BLAS。

之后将 hitGroupId设置为 1 。我们需要为这些基元添加一个新的命中组,因为我们需要像其他几何体一样计算图元属性,且这些自定义图元不像默认三角形图元那样管线会自动提供。

因为我们在创建自定义对象时添加了一个额外的实例,所以循环的元素少了一个。因此,循环现在应该像这样:

  auto nbObj = static_cast<uint32_t>(m_instances.size()) - 1;
  tlas.reserve(nbObj);
  for(uint32_t i = 0; i < nbObj; i++)
  {
      const auto& inst = m_instances[i];
      ...
  }

在循环之后和构建 TLAS 之前,我们需要添加以下内容。

  //在BLAS中添加所有自定义的对象
  {
    VkAccelerationStructureInstanceKHR rayInst{};
    rayInst.transform           = nvvk::toTransformMatrixKHR(nvmath::mat4f(1));  // Position of the instance (identity)
    rayInst.instanceCustomIndex = nbObj;                                         // nbObj == last object == implicit
    rayInst.accelerationStructureReference = m_rtBuilder.getBlasDeviceAddress(static_cast<uint32_t>(m_objModel.size()));
    rayInst.instanceShaderBindingTableRecordOffset = 1;  // We will use the same hit group for all objects
    rayInst.flags                                  = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
    rayInst.mask                                   = 0xFF;  //  Only be hit if rayMask & instance.mask != 0
    tlas.emplace_back(rayInst);
  }

该instanceCustomIndex给我们的最后一个元素m_instances,并在着色器中将能够访问分配给自定义对象的材质。

四、描述符

要访问新创建的包含所有球体的缓冲区,需要对描述符进行一些更改。

添加一个新的枚举到 Binding中

  eImplicit = 3 ,   //所有自定义对象

描述符需要添加一个绑定到自定义对象的缓冲区。

  //存储球体 (binding = 3) 
  m_descSetLayoutBind.addBinding(eImplicit, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1 ,
                                 VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_INTERSECTION_BIT_KHR);

updateDescriptorSet()写入缓冲区值的函数也需要修改。然后在纹理数组之后写入球体的缓冲区

  VkDescriptorBufferInfo dbiSpheres{m_spheresBuffer. 缓冲区,0,VK_WHOLE_SIZE};
  writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, eImplicit, &dbiSpheres));

五、新增着色器

相交着色器将被添加到命中组 VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR中。在示例中,我们已经有了三角形的命中组和关联的最近命中着色器。我们将添加一个新的命中组,并将成为 Hit Group ID (1)。

这是一个两个命中组的创建:

  enum StageIndices
  {
    eRaygen,
    eMiss,
    eMiss2,
    eClosestHit,
    eClosestHit2,
    eIntersection,
    eShaderGroupCount
  };

  // Closest hit
  stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace2.rchit.spv", true, defaultSearchPaths, true));
  stage.stage          = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
  stages[eClosestHit2] = stage;
  // Intersection
  stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rint.spv", true, defaultSearchPaths, true));
  stage.stage           = VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
  stages[eIntersection] = stage;

  //最近命中着色器 + 相交点(命中组 2) 
  group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR; 
  group.closestHitShader = eClosestHit2; 
  group.intersectionShader = eIntersection; 
  m_rtShaderGroups.push_back(group);

5.1 相交着色器

每次光线命中场景的 Aabb 时都会调用此着色器。不过需要注意,在相交着色器中无法检索到 Aabb 信息。也不可能获得光线追踪器在 GPU 上计算出的命中点值。

我们唯一可以获取的信息是 :使用gl_PrimitiveID获取 Aabb 中被击中的图元是哪一个。然后,利用缓冲区中存储的信息,我们可以检索球体的几何信息。

在着色器中我们首先声明扩展名并包含公共文件。

#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require

#include "raycommon.glsl"
#include "wavefront.glsl"

以下是所有球体的拓扑结构,我们可以使用gl_PrimitiveID获取具体哪个。

layout(binding = 3, set = eImplicit, scalar) buffer allSpheres_
{
  Sphere allSpheres[];
};

我们将针对入射光线实施两种求交方法。

struct Ray
{
  vec3 origin;
  vec3 direction;
};

球体交点

// 射线与球相交算法
// http://viclw17.github.io/2018/07/16/raytracing-ray-sphere-intersection/
float hitSphere(const Sphere s, const Ray r)
{
  vec3  oc           = r.origin - s.center;
  float a            = dot(r.direction, r.direction);
  float b            = 2.0 * dot(oc, r.direction);
  float c            = dot(oc, oc) - s.radius * s.radius;
  float discriminant = b * b - 4 * a * c;
  if(discriminant < 0)
  {
    return -1.0;
  }
  else
  {
    return (-b - sqrt(discriminant)) / (2.0 * a);
  }
}

与轴对齐的包围盒相交

// Ray-AABB 交叉点
float hitAabb ( const Aabb aabb, const Ray r)
{
vec3 invDir = 1.0 / r. 方向;
vec3 tbot = invDir * (aabb. minimum - r. origin );
VEC3 TTOP = invDir *(AABB最大。 - R的原点);
vec3 tmin = min (ttop, tbot);
vec3 tmax =最大值(ttop,tbot);
float t0 = max (tmin. x , max (tmin. y , tmin.Ž));
float t1 = min (tmax. x , min (tmax. y , tmax. z ));
返回t1 > max (t0, 0.0 ) ? t0 : - 1.0 ;
}

// 光线-AABB包围盒 求交点
float hitAabb(const Aabb aabb, const Ray r)
{
  vec3  invDir = 1.0 / r.direction;
  vec3  tbot   = invDir * (aabb.minimum - r.origin);
  vec3  ttop   = invDir * (aabb.maximum - r.origin);
  vec3  tmin   = min(ttop, tbot);
  vec3  tmax   = max(ttop, tbot);
  float t0     = max(tmin.x, max(tmin.y, tmin.z));
  float t1     = min(tmax.x, min(tmax.y, tmax.z));
  return t1 > max(t0, 0.0) ? t0 : -1.0;
}

如果没有命中,两者都返回-1,否则返回射线到原点的距离。

void main()
{
  Ray ray;
  ray.origin    = gl_WorldRayOriginEXT;
  ray.direction = gl_WorldRayDirectionEXT;

并且可以像这样获取有关包含在 Aabb 中的几何体的信息。

  //球体数据
  Sphere sphere = allSpheres.i[gl_PrimitiveID];

现在我们只需要知道光线击中球体还是立方体。

  float tHit    = -1;
  int   hitKind = gl_PrimitiveID % 2 == 0 ? KIND_SPHERE : KIND_CUBE;
  if(hitKind == KIND_SPHERE)
  {
    // Sphere intersection
    tHit = hitSphere(sphere, ray);
  }
  else
  {
    // AABB intersection
    Aabb aabb;
    aabb.minimum = sphere.center - vec3(sphere.radius);
    aabb.maximum = sphere.center + vec3(sphere.radius);
    tHit         = hitAabb(aabb, ray);
  }

使用reportIntersectionEXT函数可以获取相交着色器中获取到的相交信息,:交点与原点的距离和可用于区分原始类型的第二个参数 (hitKind)。


  // Report hit point
  if(tHit > 0)
    reportIntersectionEXT(tHit, hitKind);
}

着色器整体代码如下:

#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "raycommon.glsl"
#include "wavefront.glsl"


layout(set = 1, binding = eImplicit, scalar) buffer allSpheres_
{
  Sphere allSpheres[];
};


struct Ray
{
  vec3 origin;
  vec3 direction;
};

// Ray-Sphere intersection
// http://viclw17.github.io/2018/07/16/raytracing-ray-sphere-intersection/
float hitSphere(const Sphere s, const Ray r)
{
  vec3  oc           = r.origin - s.center;
  float a            = dot(r.direction, r.direction);
  float b            = 2.0 * dot(oc, r.direction);
  float c            = dot(oc, oc) - s.radius * s.radius;
  float discriminant = b * b - 4 * a * c;
  if(discriminant < 0)
  {
    return -1.0;
  }
  else
  {
    return (-b - sqrt(discriminant)) / (2.0 * a);
  }
}

// Ray-AABB intersection
float hitAabb(const Aabb aabb, const Ray r)
{
  vec3  invDir = 1.0 / r.direction;
  vec3  tbot   = invDir * (aabb.minimum - r.origin);
  vec3  ttop   = invDir * (aabb.maximum - r.origin);
  vec3  tmin   = min(ttop, tbot);
  vec3  tmax   = max(ttop, tbot);
  float t0     = max(tmin.x, max(tmin.y, tmin.z));
  float t1     = min(tmax.x, min(tmax.y, tmax.z));
  return t1 > max(t0, 0.0) ? t0 : -1.0;
}

void main()
{
  Ray ray;
  ray.origin    = gl_WorldRayOriginEXT;
  ray.direction = gl_WorldRayDirectionEXT;

  // Sphere data
  Sphere sphere = allSpheres[gl_PrimitiveID];

  float tHit    = -1;
  int   hitKind = gl_PrimitiveID % 2 == 0 ? KIND_SPHERE : KIND_CUBE;
  if(hitKind == KIND_SPHERE)
  {
    // Sphere intersection
    tHit = hitSphere(sphere, ray);
  }
  else
  {
    // AABB intersection
    Aabb aabb;
    aabb.minimum = sphere.center - vec3(sphere.radius);
    aabb.maximum = sphere.center + vec3(sphere.radius);
    tHit         = hitAabb(aabb, ray);
  }

  // Report hit point
  if(tHit > 0)
    reportIntersectionEXT(tHit, hitKind);
}

5.2 最近命中着色器

新增最近命中着色器如下:

#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable

#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require

#include "raycommon.glsl"
#include "wavefront.glsl"

hitAttributeEXT vec2 attribs;

// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(location = 1) rayPayloadEXT bool isShadowed;

layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle

layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(set = 1, binding = eTextures) uniform sampler2D textureSamplers[];
layout(set = 1, binding = eImplicit, scalar) buffer allSpheres_ {Sphere i[];} allSpheres;

layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on


void main()
{
  // Object data
  ObjDesc    objResource = objDesc.i[gl_InstanceCustomIndexEXT];
  MatIndices matIndices  = MatIndices(objResource.materialIndexAddress);
  Materials  materials   = Materials(objResource.materialAddress);

  vec3 worldPos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;

  Sphere instance = allSpheres.i[gl_PrimitiveID];

  // Computing the normal at hit position
  vec3 worldNrm = normalize(worldPos - instance.center);

  // Computing the normal for a cube
  if(gl_HitKindEXT == KIND_CUBE)  // Aabb
  {
    vec3  absN = abs(worldNrm);
    float maxC = max(max(absN.x, absN.y), absN.z);
    worldNrm   = (maxC == absN.x) ? vec3(sign(worldNrm.x), 0, 0) :
                                  (maxC == absN.y) ? vec3(0, sign(worldNrm.y), 0) : vec3(0, 0, sign(worldNrm.z));
  }

  // Vector toward the light
  vec3  L;
  float lightIntensity = pcRay.lightIntensity;
  float lightDistance  = 100000.0;
  // Point light
  if(pcRay.lightType == 0)
  {
    vec3 lDir      = pcRay.lightPosition - worldPos;
    lightDistance  = length(lDir);
    lightIntensity = pcRay.lightIntensity / (lightDistance * lightDistance);
    L              = normalize(lDir);
  }
  else  // Directional light
  {
    L = normalize(pcRay.lightPosition);
  }

  // Material of the object
  int               matIdx = matIndices.i[gl_PrimitiveID];
  WaveFrontMaterial mat    = materials.m[matIdx];

  // Diffuse
  vec3  diffuse     = computeDiffuse(mat, L, worldNrm);
  vec3  specular    = vec3(0);
  float attenuation = 0.3;

  // Tracing shadow ray only if the light is visible from the surface
  if(dot(worldNrm, L) > 0)
  {
    float tMin   = 0.001;
    float tMax   = lightDistance;
    vec3  origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
    vec3  rayDir = L;
    uint  flags  = gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT;
    isShadowed   = true;
    traceRayEXT(topLevelAS,  // acceleration structure
                flags,       // rayFlags
                0xFF,        // cullMask
                0,           // sbtRecordOffset
                0,           // sbtRecordStride
                1,           // missIndex
                origin,      // ray origin
                tMin,        // ray min range
                rayDir,      // ray direction
                tMax,        // ray max range
                1            // payload (location = 1)
    );

    if(isShadowed)
    {
      attenuation = 0.3;
    }
    else
    {
      attenuation = 1;
      // Specular
      specular = computeSpecular(mat, gl_WorldRayDirectionEXT, L, worldNrm);
    }
  }

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

新增命中组中的raytrace2.rchit这个着色器几乎与之前的raytrace.rchit着色器相同,但由于图元是自定义的,因此我们只需要计算被击中的图元的法线。

我们从光线中检索世界位置,并且使用的gl_HitTEXT会在相交着色器中设置。

  vec3 worldPos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;

球体信息的检索方式与raytrace.rint着色器中的检索方式相同。

  Sphere instance = allSpheres.i[gl_PrimitiveID];

然后我们像球体一样计算法线。

  //计算命中位置的法线
  vec3 normal = normalize(worldPos - instance.center);

要判断我们是否与立方体相交而不是球体相交,我们需要使用 gl_HitKindEXT数据(通过reportIntersectionEXT函数的第二个参数中设置)。

所以当这是一个立方体时,我们将法线设置为主轴。

  // 如果命中交点返回值为 1,则计算立方体的法线
  if(gl_HitKindEXT == KIND_CUBE)  // Aabb
  {
    vec3  absN = abs(normal);
    float maxC = max(max(absN.x, absN.y), absN.z);
    normal     = (maxC == absN.x) ?
                 vec3(sign(normal.x), 0, 0) :
                 (maxC == absN.y) ? vec3(0, sign(normal.y), 0) : vec3(0, 0, sign(normal.z));
  }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值