Ray Tracing -- Ray query shadows

光线查询阴影(不透明几何体)

目标:在片段着色器中通过光线查询(Ray Query)实现简单的阴影检测。我们将从每个片段的位置向光源投射一条光线,若光线在到达光源前命中任何几何体,则将该片段调暗(实现硬阴影效果)。

恭喜你:你已经为场景创建了有效的顶层 / 底层加速结构(TLAS/BLAS)!现在,让我们利用这些结构来投射光线。

任务 5:实现基于光线查询的阴影效果

在片段着色器中,我们将通过光线查询从片段位置向光源投射一条阴影射线(shadow ray)。如果射线在到达光源前命中任何几何体,我们就将该片段的颜色调暗:

glsl

复制代码
// TASK05: 实现基于光线查询的阴影效果
bool in_shadow(float3 P)
{
    bool hit = false;

    return hit;
}

[shader("fragment")]
float4 fragMain(VSOutput vertIn) : SV_TARGET {
   float4 baseColor = textures[pc.materialIndex].Sample(textureSampler, vertIn.fragTexCoord);

   float3 P = vertIn.worldPos;

   bool inShadow = in_shadow(P);

   // 若处于阴影中则调暗颜色
   if (inShadow) {
       baseColor.rgb *= 0.2;
   }

   return baseColor;
}

要实现这一功能,你需要编写一个辅助函数 in_shadow() 来执行光线查询。首先定义一个射线描述结构体,并使用片段位置和光源方向对其进行初始化:

glsl

复制代码
bool in_shadow(float3 P)
{
    // 构建从世界空间位置指向光源的阴影射线
    RayDesc shadowRayDesc;
    shadowRayDesc.Origin = P;
    shadowRayDesc.Direction = normalize(lightDir);
    shadowRayDesc.TMin = EPSILON;
    shadowRayDesc.TMax = 1e4;

TMinTMax 定义了射线从原点出发的最小 / 最大传播距离。EPSILON 是一个极小值,用于避免自相交(self-intersection) 问题;1e4 是一个足够大的值,确保射线能命中远处的物体。

接下来,初始化一个 RayQuery 对象,用于执行光线遍历。注意我们选择的标志位(flags)是为了提升查询效率:

  • RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES:因为本场景仅包含三角形几何体,无需处理过程化图元。
  • RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH:一旦找到第一个不透明几何体的相交点就终止遍历 ------ 这对于阴影检测已经足够,因为我们只需要判断是否有物体遮挡光线。

glsl

复制代码
    // 初始化用于阴影检测的光线查询对象
    RayQuery<RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES |
             RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH> sq;
    let rayFlags = RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES |
             RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH;

随后启动光线追踪操作,将射线描述、RayQuery 对象和加速结构关联起来:

glsl

复制代码
    sq.TraceRayInline(accelerationStructure, rayFlags, 0xFF, shadowRayDesc);

    sq.Proceed();

Proceed() 函数会将 RayQuery 对象的状态推进到射线传播路径上的下一个相交 "候选点"。每次调用 Proceed() 都会检查是否存在待处理的相交点:若存在,则更新查询的内部状态,让你可以访问当前候选相交点的信息。这一机制允许你自定义相交处理逻辑(例如跳过透明表面 ------ 我们会在后续实验中实现这一功能),或在命中第一个不透明物体时立即停止。该函数通常在循环中调用以遍历所有潜在相交点,但对于阴影检测,我们只需要第一个相交点:

glsl

复制代码
    // 若阴影射线命中不透明三角形,则判定该像素处于阴影中
    bool hit = (sq.CommittedStatus() == COMMITTED_TRIANGLE_HIT);

    return hit;
}

完成!你已经通过光线查询实现了基础的阴影检测。如果射线在到达光源前命中任何几何体,in_shadow() 函数会返回 true,表示该片段处于阴影中。

修改宏定义并重新编译运行程序:

cpp 复制代码
#define LAB_TASK_LEVEL 5

你会发现一个问题:

GIF 太大可以可以看这里

物体在旋转,但阴影却保持静止。这是因为我们尚未根据物体的动画更新 TLAS------ 每当物体移动或动画播放时,都需要重新构建 TLAS。接下来,我们就来实现这一功能。

相关推荐
王老师青少年编程12 分钟前
2024年信奥赛C++提高组csp-s初赛真题及答案解析(阅读程序第3题)
c++·题解·真题·csp·信奥赛·csp-s·提高组
凡人叶枫39 分钟前
C++中输入、输出和文件操作详解(Linux实战版)| 从基础到项目落地,避坑指南
linux·服务器·c语言·开发语言·c++
CSDN_RTKLIB40 分钟前
使用三方库头文件未使用导出符号情景
c++
rainbow68892 小时前
Linux文件描述符与重定向原理
c++
CodeSheep程序羊3 小时前
拼多多春节加班工资曝光,没几个敢给这个数的。
java·c语言·开发语言·c++·python·程序人生·职场和发展
编程小白20263 小时前
从 C++ 基础到效率翻倍:Qt 开发环境搭建与Windows 神级快捷键指南
开发语言·c++·windows·qt·学习
.小墨迹4 小时前
apollo学习之借道超车的速度规划
linux·c++·学习·算法·ubuntu
历程里程碑4 小时前
Linux20 : IO
linux·c语言·开发语言·数据结构·c++·算法
郝学胜-神的一滴4 小时前
深入浅出:使用Linux系统函数构建高性能TCP服务器
linux·服务器·开发语言·网络·c++·tcp/ip·程序人生
天若有情6734 小时前
【自研实战】轻量级ASCII字符串加密算法:从设计到落地(防查岗神器版)
网络·c++·算法·安全·数据安全·加密