首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >路径追踪器没有收敛

路径追踪器没有收敛
EN

Computer Graphics用户
提问于 2018-08-26 14:19:43
回答 1查看 460关注 0票数 1

因此,我刚刚完成了一个简单的路径跟踪器,它使用显式直接光采样。问题是路径追踪器没有收敛。它就像一个射线追踪器,每一次都显示一个图像。关于我是如何做到这一点的更多信息提供如下。我正在使用OpenCL 1.1。

代码语言:javascript
复制
  __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。因此,它直接将颜色写入输出图像。在每次迭代时,交换outputImageinputImage的缓冲区。在迭代1之后,reset = 0

这意味着在迭代2中,内核从它写入的图像中读取,在迭代1中,它将新颜色和以前的颜色平均化。

我认为抽取64份样本2次,平均采样数相当于一次抽取128份样本。如果这是真的,那么我的图像应该会聚在一起,但它不是,它只是不断地显示这个图像。

编辑:-我想我应该清楚我是如何计算GI的。在第一个十字路口之后。我直接发送1射线到光源(显式直接光采样)。然后均匀采样半球的w.r.t区域,并在这个取样方向发出另一条射线。如果新的射线击中光源,我们再次取样。这是在bounces times上。每一次弹跳,由于在每个命中点显式直接光采样,程序存储直接颜色。间接颜色是所有这些直接颜色的总和。在代码片段中添加了一个简单的代码片段。

cosine_falloff阵列保存了所有的弱化因子,这些因素应该乘以第二次GI射线所带来的相应的间接颜色。

EN

回答 1

Computer Graphics用户

回答已采纳

发布于 2018-08-26 16:51:40

我可以找到两个可能的原因,图像不收敛。

#1.每个示例都是相同的

对于每一个样本,你都会产生随机射线。当你通过一个像素拍摄光线时(为了抗混叠和DoF)和当你对半球取样时(为了一个新的间接反射),你就会这样做。问题是,如果对每一个样本,它会产生相同的方向,那么每个样本都是完全相同的颜色,你就不会做任何会聚。

大多数情况下,当在GPU上生成随机数时,我们会提前生成它们,并将它们保存到纹理中。当我们需要一个数字时,我们只需对纹理进行采样。当然,对于每个像素,您需要一个不同的随机值,然后使用类似线程id或像素坐标的东西来采样纹理。然后,每个框架也必须是不同的,所以您也将使用框架计数器来偏移线程id。但是,请注意,没有什么可以确保每个样本也有一个新的随机数。如果对每个样本产生相同的随机方向,那么每个样本都是完全相同的颜色,并且不会进行任何收敛。

通过跟踪您已经生成的随机数,并使用它来抵消每次查找,可以很容易地解决这个问题。基本上,有一个用来保存纹理查找的u和v值的结构。您可以使用线程id或像素坐标以及在CPU上生成的帧数或随机数初始化它,并将其作为对随机值的全局偏移传递给内核。确保在内核的最开始(初始化total_color的地方)初始化了结构。然后,当你需要一个随机数时,你把对这个结构的引用传递给生成一个随机数的方法,然后它在纹理中查找随机数,然后它抵消了结构中的u和v值,这样下次你就会得到一个不同的数字。

当然,我还没有看到你是如何产生光线的(无论是通过像素拍摄光线,还是对半球进行采样),所以我不能确定这是否是问题所在,但它看起来确实很像。

#2.不正确地平均多帧

我认为抽取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#,这确实是采样的两倍。

但是,您的代码不能正确地组合两个图像。

代码语言:javascript
复制
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)。

如果你在计算第五帧,你就会这样做。

代码语言:javascript
复制
float4 prev_color = read_imagef(outputImage, sampler, pixel);
color += prev_color * 4.0f;
color /= 5.0f;
write_imagef(outputImage, pixel, color);  

如果你计算第十帧,你就会这样做。

代码语言:javascript
复制
float4 prev_color = read_imagef(outputImage, sampler, pixel);
color += prev_color * 9.0f;
color /= 10.0f;
write_imagef(outputImage, pixel, color);  

这确实意味着您需要通过一个计数器,它告诉您您已经计算了多少帧。大多数渐进式或交互式路径跟踪器实际上每个帧只执行一个样本(确保通过像素的光线是随机抖动的),因为计算一个帧所需的时间较少,因此您可以更早地看到变化,而且如果您只使用一个样本编辑场景,因为您仍然可以获得足够的信息。

票数 3
EN
页面原文内容由Computer Graphics提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://computergraphics.stackexchange.com/questions/7966

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档