我正在尝试出于研究目的而实现路径跟踪器,但是到目前为止,效果还不是很好,我将向您解释原因。在获取代码之前的总体思路是:

我正在处理在采样之前生成的路径。我的意思是,算法的第一步在于计算特定像素(x,y)的路径。此路径将在场景内执行一些反弹操作,如果终止于灯光,则将被视为有效,这意味着我可以计算其对像素(x,y)的贡献,否则它将为黑色。

但首先,我的目标是通过少量的SPP(约16-32 spp)获得“可接受的”图像。这是因为我遵循的是易于实现的目标框架的代码,该框架只需几个步骤即可达到如此好的效果(稍后说明)。以下是由目标框架生成的目标图像:



它使用16SPP渲染。它背后的核心代码非常简单。它只是实现了渲染方程式,下面对此进行了显示和注释:该PDF是cosTheta / PI,我们必须对其进行划分:因为我只想考虑DIFFUSE对象,所以我的BRDF确实是漫反射/ PI的漫反射BRDF的渲染方程和PI。 br />我试图模仿这段代码,不同之处在于,在我要计算像素颜色的那一刻我不计算路径(就像上面的目标框架一样),但是我事先计算了路径:
我的generatePath()方法确实跟踪到场景的路径并检查它命中的所有顶点。您将看到使用了checkIfRayIntersectSomething(t)方法,它只是在我的框架中实现的伪方法,我省略了其长度的原因。我用它来检查我的射线是否击中了场景中的某物,如果发生了,它会用到该物体的距离来更新“ t”。注意:灯光本身不被视为物体。因此,我还有一个checkRayLightIntersection(hitLightPoint)来检查与灯光的交点,如果有的话,hitLightPoint会更新为我所点击的灯光上的点。
灯光是区域的2D曲面2x2与目标框架位于相同的位置(-1,20,9)。

首先定义几个用于存储某些信息的结构的定义:

Vec Sample(vec3 O, vec3 D, int depth)//oriding, direction, depth of the recursive step
{
  vec3 color(0, 0, 0);
  float t;
  if (checkIfIntersectSomething(D, t))
  {

    vec3 I = O + t * D; //get to the intersection point on the object
    vec3 diffuse = getMaterial(I);

    vec3 L = vec3(-1 + Rand(2.0f), 20, 9 + Rand(2.0f)) - I; //(-1,20,9) is Hard-code of the light position, and I add Rand(2.0f) on X and Z axis
    //so that I have an area light instead of a point light
    L = normalize(L);
    float ndotl = dot(I.getNormal(), L); //the closer the dotProdutc is to 1.0, the more the light and surface face each other
    if (ndotl > 0)
    {
        if (!checkRayLightIntersection(L)) {
            float dist = distFromLight(I);
            color += diffuse * ndotl * vec3(1000.0f, 1000.0f, 850.0f) * (1.0f / (dist * dist));
        }
    }

    // russian roulette
    float Psurvival = CLAMP((diffuse.r + diffuse.g + diffuse.b) * 0.33333f, 0.2f, 0.8f);

    // continue random walk 
    float rand = Rand(1.0f);
    if (depth < 10 && rand < Psurvival)
    {
        //Besides russian roulette, I also do another weight, because rays that go towards the horizon will bring back very little energy
        //so I make a random distribution that favours those rays who are close to the normal of the hit point, this is DiffuseReflectionCosineWeighted(). It creates a Random bounce but proportional to N dot R
        vec3 R = DiffuseReflectionCosineWeighted(I.getNormal());//there is a weight

        float prob = 1.0;
        float cosTheta = fabs(dot(I.getNormal(), R));
        if (cosTheta > 1e-6) prob = cosTheta / M_PI;

        color += diffuse * Sample(I + R * EPSILON, R, depth + 1) * (1.0f / Psurvival); //the cosTheta() of the attenuation of the rendering equation gets simplified with the cosTheta of the "prob"
        //the PI of the prob gets simplified with the BRDF where we are using the ideal BRDF = diffuse/PI
    }
  }
return color;
}


现在,主要代码: 。最后剩下的是Sample方法,它使用上面计算出的路径来计算像素的最终颜色:

color += diffuse * Sample(I + R * EPSILON, R, depth + 1) * (1.0f / Psurvival);


问题是图像真的很暗,您可以看到光有多小,但是在目标框架中以某种方式可以正常工作:



这是因为没有多少光线可以到达光线路径无效。相反,如果我使光线较宽(20x20)效果更好,但我却失去了阴影:



我确信光线会较少,因为我在两种情况下都一直在计算它们:小面积光和大面积光。我还尝试通过增加路径到达时的光贡献来补偿暗图像,但是它会带来白色像素(对于到达光的路径),而黑色像素(导致无效路径)保持黑色,因此好方法。看来(据我所知)目标框架应用了直接照明,这就是为什么按照理论直接照明这样好的图像,但是直接照明只会减少方差,减少噪音。如此多地改变生成的图像。有没有一种方法可以解决您的问题?提前谢谢。

评论

在您的Sample函数中,有一条语句float ndotl = dot(I.getNormal(),L);。不确定,但I.getNormal()看起来有点像它表示标准化的I向量,而不是位置I处的表面法线。

我知道这是一个老问题,但是您找到解决方案了吗?

@HenryLeBerre我对亨利表示歉意,但不幸的是我无法帮助您。我接触该项目已经有好几年了,我不再从事该领域的工作,所以我什至不记得是否最终解决了这个问题。祝你好运!

@Tarta根本不是问题!谢谢

#1 楼

首先要注意的是
从屏幕截图的外观来看,我怀疑您的代码中可能仍然存在错误。噪声只有16 spp是可以预期的,但是您的照片对我来说仍然看起来很暗。为了进行比较,这是我的SmallPT实现的结果,它的速度为16 spp,15跳动,并且没有下一个事件预测:

(在ShaderToy上)像你的照片一样暗。这是一个非常不同的场景,但是考虑到形状相似(一个盒子,一侧开着,只有一个光源),我希望得到类似的发光度/噪声外观。
其余的假设您的代码正确无误。

路径跟踪和减少噪声
光源尺寸,噪声和SPP之间的平衡是一个经典问题,没有神奇的解决方案。从最简单到最困难,您都可以:


使用更适合您的算法的场景:
渲染算法有局限性,在某些情况下可能会发光,而在其他情况下处理效果很差。了解您的局限性之后,您可以选择将其使用限制在最适合的情况下使用:大型光源,天空圆顶,许多光源,遮挡较少的场景等。 >
实施下一个事件预测:
当您知道光源在哪里时,每次反射时,您都可以尝试向其直接发射光线。如果射线没有首先照射到其他几何体,则您将获得一条有助于路径的路径。由于这样做会使随机性产生偏差,因此您需要使用适当的因素来考虑它。


实现双向路径跟踪:
跟踪摄像机发出的光线时,如果光源很小,则许多路径将永远无法到达。这是您遇到的问题。相反,当追踪光线时,许多光线将永远无法到达相机。 BDPT包括首先从相机和光源拍摄光线,然后尝试将它们连接起来。可以连接的每一对都是一条贡献路径。 BDPT将显着降低噪声。


实施大都市光传输:
如果摄像机和光源之间存在路径,则之间可能存在相似的路径。他们。 MLT包括在已知通过变异(添加,删除或更改一个或多个反弹)而起作用的路径附近搜索那些起作用的路径。这已经不那么简单了,因为保持其公正性的数学方法显得有些毛茸茸。 。此时,您将需要阅读最新的动态。

评论


$ \ begingroup $
感谢您回答朱利安!我确实同意您的观点,很可能代码中有一些错误。我不确定我是在做错误的计算还是算法本身存在错误。这就是为什么我发布我的代码的原因。不幸的是,我尝试实现一些可变性降低算法,例如下一个事件预测。确实,这些算法应该会给人“更好”的印象,但是从根本上讲是有问题的
$ \ endgroup $
–塔塔
16年4月21日在7:53