Optix (02)

Kiraray

Optix Shader

MegaKernel

  • 一根光线的所有过程都写在 raygen 中
  • divergence:不同像素的光线的期望长度可能不一致

RadianceRay

  • raygen
  • NEE:击中场景中点的时候,对这个点做 NEE
    • NEE 实现的时候,可以采样环境光,因此也需要和环境光做 MIS
  • 不 NEE 的情况
    • 材质为镜面(specular)
    • 直接光照(第一跳,depth=0)
  • 采样光源
    • 简单实现:先采样一个光源(\(\dfrac{1}{N}\)),再在光源上均匀采样
    • 如果采样的光源位置被另外一个光源遮挡了,那么 NEE 部分的贡献为 0
      • 实现简单,不需要考虑某一个方向能够被多个光源采样到的情况(远处的直接被剔除了)
    • 面积采样要转化为立体角采样,推导
      • 代码:src/core/shape.h:sample()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
_ raygen() {
for(spp) {
tracePath();
}
}

_ tracePath() {
for(depth) {
optixTrace();
if(miss){
// 正常光追部分(环境光)
// 如果有 NEE, 则需要对这个部分进行加权
// 这里相当于是环境光与正常光追打到环境光部分的 MIS
// 注意视点不 NEE(depth=0, 相机光线直接打空)
handleMiss();
break;
}

if(light) {
// 正常光追部分(光源)
// 如果有 NEE, 则需要对这个部分进行加权
// 这里相当于是常规光源与正常光追打到常规光源部分的 MIS
// 此时我们已经知道打中哪一个光源了, 可以计算 NEE 的 MIS 权重
handleHit();
}

if(shouldStop) {
// 终止条件: max depth, RR
break;
}

throughput /= RR;

if(NEE) {
// NEE 部分
// 已经知道 BRDF, 可以计算 BRDF 的 MIS 权重
}

// 光源不交互可以体现在这里
if(genScatterRay()) { break; }
}
}
  • chit、ahit、miss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
_ ahit() {
// alpha test
// 如果有透明度贴图, 则按照概率通过
// 通过则当作透明体, 将其剔除 optixIgnoreIntersection()
}

_ chit() {
// 保存更新击中点的信息
}

_ miss() {
// 返回没有击中 (payload2)
// payload01 被用于指针传递
}

ShadowRay

  • shadow ray 在 NEE 的时候使用,判定光线能否打到光源
1
2
3
4
// trace 设置
trace_flags = OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT
| OPTIX_RAY_FLAG_DISABLE_CLOSESTHIT;
// 最大距离设置为光源位置
  • chit、ahit、miss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
_ ahit() {
// alpha test
// 如果有透明度贴图, 则按照概率通过
// 通过则当作透明体, 将其剔除 optixIgnoreIntersection()
}

_ chit() {
// nothing (disabled)
}

_ miss() {
// 返回没有击中 (payload0)
// 也就是说没有遮挡, 能够击中光源
}

WaveFront

  • 注意:PT 部分 item 都是 soa
    • 场景构建中:soa 部分,将 array of struct 转化为 struct of array
      • https://github.com/crosetto/SoAvsAoS
    • 提供多线程访问的时候的缓存一致性
      • 但是在保存(save)的时候,会有更大的缓存开销
  • 简写
1
Color thp; // throughput
  • 任务队列
1
2
3
4
5
6
7
// work queues
RayQueue* rayQueue[2]{ }; // switching bewteen current and next queue, 保存 RayWorkItem(光线信息、着色点信息)
MissRayQueue* missRayQueue{ };
HitLightRayQueue* hitLightRayQueue{ };
ShadowRayQueue* shadowRayQueue{ };
ScatterRayQueue* scatterRayQueue{ };
PixelStateBuffer* pixelState; // 维护每一个像素的信息: 最终 L、随机数生成器
  • 流程
    • 只有在使用到 optix 函数的时候再使用 optixLaunch(),其他的直接调用 cuda kernel 并行实现
      • 使用 optixLaunch() 的函数:trace_closest()trace_shadow()
    • 优化:如果没有环境光,那么就不需要 miss queue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
for(spp) {
// [STEP#1] generate camera / primary rays
reset_current_ray_queue(); // 重置当前的 ray queue
generate_camera_rays(); // 在当前 ray queue 中生成 camera rays

// [STEP#2] do radiance estimation recursively
for(depth) {
// 重置 queues: ray(next)、miss、hit、shadow、scatter
prepara_for_current_depth();
// [STEP#2.1] find closest intersections, filling in scatterRayQueue and hitLightQueue
trace_closest(); /** OptixLaunch **/

// [STEP#2.2] handle hit and missed rays, contribute to pixels
handle_hit(); // 把光追贡献(常规光源)加到 pixelState 上,注意 NEE 条件(depth > 0 && 非镜面)
handle_miss(); // 把光追贡献(环境光)加到 pixelState 上,注意 NEE 条件(depth > 0 && 非镜面)

// RR 的终止条件放到 STEP#2.3 里面判断, 因为不同 ray 的 RR 结果不一样,不能一次性判断出来
if(depth == MaxDepth) {
break;
}

// [STEP#2.3] scattered rays & shadow rays(if NEE)
// (1) 根据 BRDF 更新 thoughput 以及着色点信息, 生成 scattered rays 并推入 next ray queue
// 如果 throughput=vec3(0) 的话直接丢弃
// (2) 如果 NEE 的话生成 shadow rays
();

// [STEP#2.4] trace shadow rays (NEE)
if (NEE) {
trace_shadow(); /** OptixLaunch **/
}
}

// [STEP#3] gathering
gather(); // 将当前 pixelState 中保存的 L/spp 后写入 framebuffer
}
  • trace_closest()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
_ raygen_closest() {
// 根据 RayQueue 中 RayWorkItem 信息追踪光线
optixTrace();
}

_ chit_closest() {
// 保存更新击中点的信息
prepareShadingData();

// 根据当前击中点的类型推入不同的队列
// (1) HitLightRayQueue
// (2) MissRayQueue
push_into_corresponding_queue();
}

_ ahit_closest() {
// alpha test
}

_ miss_closest() {
// 推入 MissRayQueue
}
  • trace_shadow()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
_ raygen_shadow() {
// 根据 ShadowRayQueue 中 ShadowRayWorkItem 信息追踪光线
// OptixRayFlags = OPTIX_RAY_FLAG_DISABLE_CLOSESTHIT | OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
optixTrace();
if(miss) {
// 直接加贡献值加到当前像素中(贡献可以在 generate_scatter_rays() 中计算,也可以在这里计算)
}
}

_ chit_shadow() {
// skip
}

_ ahit_shadow() {
// alpha test
}

_ miss_shadow() {
// 返回 miss (SetPayload0)
}