射线追踪中如何实现反锯齿?


13

在在线阅读了几篇文章之后,我可以自信地说我对使用射线追踪时抗锯齿的工作原理一无所知。

我所了解的是,单个像素/射线被分成4个子像素和4条射线,而不是1条

有人可以解释一下这是怎么做的(最好是用代码)吗?


2
我可以建议您看看“ supersampling” en.wikipedia.org/wiki/Supersampling,也许还有 en.wikipedia.org/wiki/Distributed_ray_tracing吗?
西蒙F

2
我还建议阅读PBRT的这一章pbrt.org/chapters/pbrt_chapter7.pdf并阅读本文lgdv.cs.fau.de/get/785(这说明了与pbrt中实现的技术不同的技术)。
汤姆·范·布鲁塞尔

1
foreach pixel : p{acc = 0; foreach subsample : s { acc+=sample_scene(s);} store(p, acc);}
棘轮怪胎

Answers:


12

我认为可以肯定地说,有两种方法可以在光线追踪中进行AA:

1:如果您拥有最终图像和深度图像,则可以应用游戏中使用的几乎所有现有技术(FXAA等),这些技术直接作用于最终图像,并且与光线追踪无关

2:第二种方法是考虑每个像素的多条光线,然后平均结果。对于一个非常简单的版本,可以这样考虑:

  • 您首先渲染尺寸为1024x1024的图像,每个像素一束(例如)
  • 渲染后,将图像缩放到512x512(每个4像素平均为一个),您会注意到边缘更加平滑。这样,您有效地为512x512大小的最终图像中的每个像素使用了4条射线。

此方法还有其他变体。例如,您可以调整恰好位于几何图形边缘的像素的样本数量,这意味着对于某些像素,您将只有4个样本,而对于其他像素,则只有16个样本。

检查上面评论中的链接。


所以基本上我将图像渲染为大尺寸,然后将其保存为图像时,将其缩小为较小的尺寸?这似乎很简单:)!这是超级采样方法吗?
Arjan Singh

1
@Arjan Singh是的,它是en.wikipedia.org/wiki/Supersampling,但这是所有方法中最慢的,光线跟踪使您可以轻松地进行自适应超级采样,从而可以表现得更好
Raxvan 2016年

14

Raxvan完全正确地说,“传统”抗锯齿技术将在光线跟踪中起作用,包括那些使用诸如深度之类的信息进行抗锯齿的技术。例如,您甚至可以在光线跟踪中进行时间抗锯齿。

朱利安(Julien)在Raxvan的第二项内容上进行了扩展,这是对超级采样的解释,并展示了您实际上是如何做的,还提到您可以随机化像素内采样的位置,但是随后您进入了信号处理国家,更深,这绝对是!

NN

如果这样做,您仍然可以使用别名。最好不要执行此操作,因为您可以提高采样率,因此能够处理更高频率的数据(又称较小的细节),但仍可能导致混叠。

N

当您仅使用“常规”随机数(例如从rand()或std :: uniform_int_distribution获得)时,称为“白噪声”,因为它包含所有频率,例如白光如何由其他所有颜色(频率)组成)的光。

使用白噪声将像素内的样本随机化会带来问题,有时您的样本会聚集在一起。例如,如果您平均一个像素中有100个样本,但是它们最终都位于该像素的左上角,那么您将不会获得有关该像素其他部分的任何信息,因此最终得到的像素颜色将缺少有关其应使用的颜色的信息。

更好的方法是使用仅包含高频成分的蓝噪声(例如蓝光是高频光)。

蓝色噪点的好处是,您可以获得均匀的像素覆盖范围,就像使用均匀的采样网格一样,但是,您仍然会得到一些随机性,这将混叠化为噪点并为您提供更好的图像。

不幸的是,蓝噪声的计算成本非常高,最好的方法似乎都已申请了专利(到底是什么?!),但是一种方法是由pixar发明的(我认为也已获得专利,但并非100%肯定)就是制作一个均匀的采样点网格,然后将每个采样点随机偏移少量​​-就像采样网格宽度和高度的正负一半之间的随机量一样。这样,您就可以以相当便宜的价格获得某种蓝噪声采样。

请注意,这是分层采样的一种形式,泊松磁盘采样也是这种形式的一种,这也是产生蓝噪声的一种方式:https : //www.jasondavies.com/poisson-disc/

如果您有兴趣进一步研究,可能还需要检查一下这个问题并回答!

在像素内使用多个随机样本进行抗锯齿的基本原因是什么?

最后,这些东西开始误入蒙特卡洛路径跟踪领域,这是进行逼真的光线跟踪的常用方法。如果您有兴趣了解更多有关此内容,请阅读本书!

http://blog.demofox.org/2016/09/21/path-tracing-getting-started-with-diffuse-and-emissive/


一看到您的名字,我就知道您会在蓝色的噪音中加入一些东西:)
哈勃

7

让我们假设一个相当典型的光线跟踪主循环:

struct Ray
{
    vec3 origin;
    vec3 direction;
};

RGBColor* image = CreateImageBuffer(width, height);

for (int j=0; j < height; ++i)
{
    for (int i=0; i < width; ++i)
    {
        float x = 2.0 * (float)i / (float)max(width, height) - 1.0;
        float y = 2.0 * (float)j / (float)max(width, height) - 1.0;

        vec3 dir = normalize(vec3(x, y, -tanHalfFov));
        Ray r = { cameraPosition, dir };

        image[width * j + i] = ComputeColor(r);
    }
}

可以做4个MSAA样本的一种可能修改是:

float jitterMatrix[4 * 2] = {
    -1.0/4.0,  3.0/4.0,
     3.0/4.0,  1.0/3.0,
    -3.0/4.0, -1.0/4.0,
     1.0/4.0, -3.0/4.0,
};

for (int j=0; j < height; ++i)
{
    for (int i=0; i < width; ++i)
    {
        // Init the pixel to 100% black (no light).
        image[width * j + i] = RGBColor(0.0);

        // Accumulate light for N samples.
        for (int sample = 0; sample < 4; ++sample)
        {
            float x = 2.0 * (i + jitterMatrix[2*sample]) / (float)max(width, height) - 1.0;
            float y = 2.0 * (i + jitterMatrix[2*sample+1]) / (float)max(width, height) - 1.0;

            vec3 dir = normalize(vec3(x, y, -tanHalfFov) + jitter);
            Ray r = { cameraPosition, dir };

            image[width * j + i] += ComputeColor(r);
        }

        // Get the average.
        image[width * j + i] /= 4.0;
    }
}

另一种可能性是做一个随机抖动(而不是上面的基于矩阵的抖动),但是您很快就会进入信号处理领域,需要大量阅读才能知道如何选择一个好的噪声函数。

但是这个想法仍然是一样的:考虑像素代表一个很小的正方形区域,而不是只发射一条穿过像素中心的光线,而是发射覆盖整个像素区域的许多光线。射线分布越密集,得到的信号越好。

PS:我是即时编写上面的代码,所以我希望其中有一些错误。这仅是为了展示基本思想。


好答案!与@Raxvan使用的方法相比,使用此方法有什么好处?通过渲染成较大的尺寸然后缩小为较小的尺寸,是否可以获得相同的结果?
Arjan Singh's

从根本上讲,使用光线追踪,您无需渲染更大的图像然后将其缩小。这意味着您具有更大的灵活性:您可以拥有很多样本,可以根据区域来改变样本数量,并且简单地,您不必添加重新缩放步骤。
Julien Guertault

2
关于抖动的话题,事实证明这是一个相当复杂的话题。这是一篇很棒的论文,分析了几年前的图形
。pixar.com/ library / MultiJitteredSampling / paper.pdf

上面的代码示例使用了4个样本MSAA,如果我想做8倍MSAA,矩阵将是什么样?我需要改变上面显示的抖动矩阵吗?
Arjan Singh

您说“ 4个MSAA样本将是:”大概是指SSAA。在栅格化中作为成本/质量折衷使用的MSAA首先确定每个样本命中哪个对象(读取三角形)。如果它们全部击中了同一对象,则仅执行一次着色操作。如果找到了2个不同的对象,则将执行2次着色操作,并对结果进行加权和求和,然后再对3个对象进行采样,以此类推。您所描述的是标准超级采样AA,即SSAA。
Simon F

0

只是添加到上面的答案:

分布式射线追踪(库克,波特和木匠)。允许您同时进行空间AA,时间AA(即运动模糊)和景深/景深。最好阅读论文,但基本上每个像素发射的N射线也可以分配伪随机时间(用于运动模糊)和透镜上的位置(以获得聚焦效果)。

自适应超级采样:您最初会在每个像素上发射一定数量的光线。但是,如果本地附近的光线返回的结果明显不同,则可以在本地增加采样率(例如2倍)以改善结果。您可以选择重复此过程。由于对象仍然可以“落入样本之间”,因此它不是完美的,但是它可能比以更高的速率进行均匀采样便宜。

带有圆锥体(Amanatides)的光线追踪:不是使用无限细的光线,而是通过圆锥体来近似每条光线,比如说它的直径覆盖了所有像素(和一些邻居),可以评估部分覆盖率。它还对纹理采样/抗锯齿,柔和阴影以及潜在的模型LOD有好处。我做了一个实现许多年前-它肯定更困难的事,但它避免了大量的附加射线。IIRC也有一个称为铅笔追踪的方案(但是我最初的搜索没有找到论文的链接)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.