因此,我刚刚完成了一个简单的路径跟踪器,它使用显式直接光采样。问题是路径追踪器没有收敛。它就像一个射线追踪器,每一次都显示一个图像。关于我是如何做到这一点的更多信息提供如下。我正在使用OpenCL 1.1。
__kernel void render(__write_only image2d_t outputImage, __read_only image2d_t inputImage, int reset)
{
float total_color; // initalized to black
for(int i = 0; i < samples_x; i++)
{
for(int j = 0; j < samples_y; j++)
{
float4 direct_color, indirect_color; initialized black.
Shoot ray through pixel.
HitInfo new_info, old_info;
If( !intersects(ray, old_info) )
{
total_color += Background_color
continue;
}
else
{
direct_color = computeDirectLighting(old_info);
float4 color_arr[BOUNCES];
float cosine_falloff[BOUNCES];
for(int i = 0; i < BOUNCES; i++)
{
new_ray = sampleHemisphere();
if( !intersects(new_ray, new_info) )
{
color_arr[i] = sky_color;
cosine_falloff[i] = max(dot(old_info.normal, new_ray.dir), 0.0f);
break;
}
else if ( intersects light source)
{
i--;
continue;
(Sample again)
}
color_arr[i] = computeDirectLighting(new_info);
cosine_falloff[i] = max(dot(old_info.normal, new_ray.dir), 0.0f);
old_info = new_info;
}
for(i = color_arr.length() - 1; i >= 0; i--)
{
indirect_color += color_arr[i];
indirect_color *= cosine_falloff[i];
}
}
total_color += direct_color + indirect_color;
}
}
}
color /= (samples_x * samples_y);
if ( reset == 1 )
{
write_imagef(outputImage, pixel, color);
}
else
{
float4 prev_color = read_imagef(outputImage, sampler, pixel);
color += prev_color;
color /= 2.0f;
write_imagef(outputImage, pixel, color);
}
}这是主要的轮廓。当内核第一次运行时,reset = 1。因此,它直接将颜色写入输出图像。在每次迭代时,交换outputImage和inputImage的缓冲区。在迭代1之后,reset = 0。
这意味着在迭代2中,内核从它写入的图像中读取,在迭代1中,它将新颜色和以前的颜色平均化。
我认为抽取64份样本2次,平均采样数相当于一次抽取128份样本。如果这是真的,那么我的图像应该会聚在一起,但它不是,它只是不断地显示这个图像。
编辑:-我想我应该清楚我是如何计算GI的。在第一个十字路口之后。我直接发送1射线到光源(显式直接光采样)。然后均匀采样半球的w.r.t区域,并在这个取样方向发出另一条射线。如果新的射线击中光源,我们再次取样。这是在bounces times上。每一次弹跳,由于在每个命中点显式直接光采样,程序存储直接颜色。间接颜色是所有这些直接颜色的总和。在代码片段中添加了一个简单的代码片段。
cosine_falloff阵列保存了所有的弱化因子,这些因素应该乘以第二次GI射线所带来的相应的间接颜色。

发布于 2018-08-26 16:51:40
我可以找到两个可能的原因,图像不收敛。
对于每一个样本,你都会产生随机射线。当你通过一个像素拍摄光线时(为了抗混叠和DoF)和当你对半球取样时(为了一个新的间接反射),你就会这样做。问题是,如果对每一个样本,它会产生相同的方向,那么每个样本都是完全相同的颜色,你就不会做任何会聚。
大多数情况下,当在GPU上生成随机数时,我们会提前生成它们,并将它们保存到纹理中。当我们需要一个数字时,我们只需对纹理进行采样。当然,对于每个像素,您需要一个不同的随机值,然后使用类似线程id或像素坐标的东西来采样纹理。然后,每个框架也必须是不同的,所以您也将使用框架计数器来偏移线程id。但是,请注意,没有什么可以确保每个样本也有一个新的随机数。如果对每个样本产生相同的随机方向,那么每个样本都是完全相同的颜色,并且不会进行任何收敛。
通过跟踪您已经生成的随机数,并使用它来抵消每次查找,可以很容易地解决这个问题。基本上,有一个用来保存纹理查找的u和v值的结构。您可以使用线程id或像素坐标以及在CPU上生成的帧数或随机数初始化它,并将其作为对随机值的全局偏移传递给内核。确保在内核的最开始(初始化total_color的地方)初始化了结构。然后,当你需要一个随机数时,你把对这个结构的引用传递给生成一个随机数的方法,然后它在纹理中查找随机数,然后它抵消了结构中的u和v值,这样下次你就会得到一个不同的数字。
当然,我还没有看到你是如何产生光线的(无论是通过像素拍摄光线,还是对半球进行采样),所以我不能确定这是否是问题所在,但它看起来确实很像。
我认为抽取64份样本2次,平均采样数相当于一次抽取128份样本。
这其实是真的。平均样本才能得到图像。$$IMG1 = \frac{sample1 + sample2 + sample3 + samples4}{4}$$,然后再对第二个图像执行此操作。$$IMG2 = \frac{sample5 + sample6 + sample7 + samples8}{4}$$,然后组合。$$RESULT = \frac{IMG1 +IMG2}{2}=$ $$\frac{sample1 + sample2 + sample3 + samples4}{4}+ \frac{sample5 + sample6 + sample7 +samples8}{2}{2}=$$$\frac{{sample1+ sample2 + sample3 + samples4 + sample2 +sample1+sample2+sample1+en19+# be 20{4}{2}=$${+#en25+#en28+{8}#{8}#30#,这确实是采样的两倍。
但是,您的代码不能正确地组合两个图像。
float4 prev_color = read_imagef(outputImage, sampler, pixel);
color += prev_color;
color /= 2.0f;
write_imagef(outputImage, pixel, color); 当我们看到你在做什么。$$outColor = \frac{newColor + oldColor}{2}$$,其中$outColor$是保存在图像中的颜色。$newColor$是你刚才计算出来的颜色。$oldColor$是上一帧的颜色。
我们看到它做它应该做的事情,它平均两个框架。
当然,当第三帧出现时,$outColor$变成了$oldColor$,因为您交换了outputImage和inputImage的缓冲区。因此,我们可以用它来代替。$$outColor = \frac{newColor + \frac{prevColor +oldColor}{2}{2}$$,其中$prevColor$是计算前一帧的颜色。
不过,这似乎不太好。$newColor$贡献了50%,而$prevColor$和$oldColor$只贡献了25%。这是一个问题。你希望每一个帧都能做出同样的贡献,因为那时你只是在把样本加起来。这意味着你需要对此进行补偿。
有一个非常简单的解决办法。将$oldColor$乘以不包括当前帧的帧数量,然后除以包括当前帧的帧数量。然后你就会得到这个。$$outColor = \frac{newColor + 2*\frac{prevColor +oldColor}{2+1}{2+ 1}$$,这将取消除法并给出您。$$outColor = \frac{newColor + prevColor + oldColor}{3}$$ ( $+1$是因为我们在顶部乘以2,基本上总共是3部分,因此需要除以3)。
如果你在计算第五帧,你就会这样做。
float4 prev_color = read_imagef(outputImage, sampler, pixel);
color += prev_color * 4.0f;
color /= 5.0f;
write_imagef(outputImage, pixel, color); 如果你计算第十帧,你就会这样做。
float4 prev_color = read_imagef(outputImage, sampler, pixel);
color += prev_color * 9.0f;
color /= 10.0f;
write_imagef(outputImage, pixel, color); 这确实意味着您需要通过一个计数器,它告诉您您已经计算了多少帧。大多数渐进式或交互式路径跟踪器实际上每个帧只执行一个样本(确保通过像素的光线是随机抖动的),因为计算一个帧所需的时间较少,因此您可以更早地看到变化,而且如果您只使用一个样本编辑场景,因为您仍然可以获得足够的信息。
https://computergraphics.stackexchange.com/questions/7966
复制相似问题