更新

我正在以其他方式编辑和发布此问题;从Nishita纸的角度来看,这一次。



阳光在P处散射并衰减,直到到达Pv为止。因此,可以通过将衰减乘以P处的强度来获得来自P的强度达到Pv:



因此可以通过积分空气中的光来获得达到Pv的强度沿射线段PaPb的分子:



在哪里



蒙特卡罗方法,没有射线行进方法,什么是估计器?pdf和抽样方法的最佳选择是什么?

。谢谢。


我是试图在瑞利散射上实施分布式射线追踪的概念,以使天空更加逼真。我使用的代码是从该网站借来的。它沿着光线需要16个样本,而对于阳光则需要8个样本。
这很好,我只能使用每个像素1个样本来生成此图像(不确定为什么太阳这么小,有什么想法吗?):



然后我尝试使用Monte Carlo来执行此操作。沿射线均匀选择1个样品,对阳光均匀选择1个样品。我所做的唯一更改是在四行上:

line 7:  uint32_t numSamples = 1; //16
line 8:  uint32_t numSamplesLight = 1; //8 

//used drand48() instead of 0.5
line 18: Vec3f samplePosition = orig + (tCurrent+
                                segmentLength * drand48()) * dir; 
line 32: Vec3f samplePositionLight = samplePosition + (tCurrentLight + 
                                segmentLengthLight * drand48) * sunDirection; 


这是我的用c#代码实现:

public override void Render(int pxlX, int pxlY, Random rand)
{
    Vector estimatedColor = new Vector();

    Ray ray = Scene.Camera.GenerateRay(pxlX + rand.NextDouble(), pxlY + rand.NextDouble(), rand.NextDouble(), rand.NextDouble());

    ISect isect = Scene.Trace(ray);
    if (ISect.IsNull(isect))
        estimatedColor = Atmosph.ComputeIncidentLight(ray, rand);
    else
        estimatedColor = isect.Thing.Material.Emission +
                ComputeRadiance(isect, rand, 0);

    ImageFilm.AddToPixel(pxlX, pxlY, estimatedColor/numPixelSamples);
}

public Vector ComputeIncidentLight(Ray r, Random rand)
{
    r.Start.Z += earthRadius;

    ISect isect = earth.Intersect(r);
    if (ISect.IsNull(isect)) return Vector.ZERO;

    int numSamples = 1;
    int numSamplesLight = 1;
    double segmentLength = isect.Dist / numSamples;
    double tCurrent = 0;
    Vector sumR = Vector.ZERO;
    Vector sumM = Vector.ZERO;// mie and rayleigh contribution 
    double opticalDepthR = 0, opticalDepthM = 0;
    double mu = r.Dir.Dot(sunDirection); // mu in the paper which is the cosine of the angle between the sun direction and the ray direction 
    double phaseR = 3d / (16d * Math.PI) * (1 + mu * mu);
    double g = 0.76f;
    double phaseM = 3d / (8d * Math.PI) * ((1d - g * g) * (1d + mu * mu)) / ((2d + g * g) * Math.Pow(1d + g * g - 2d * g * mu, 1.5d));
    for (int i = 0; i < numSamples; ++i)
    {
        Vector samplePosition = r.Start + r.Dir * (tCurrent + segmentLength * rand.NextDouble());
        double height = samplePosition.Length() - earthRadius;
        // compute optical depth for light
        double hr = Math.Exp(-height / Hr) * segmentLength;
        double hm = Math.Exp(-height / Hm) * segmentLength;
        opticalDepthR += hr;
        opticalDepthM += hm;
        // light optical depth
        ISect isectSun = earth.Intersect(new Ray(samplePosition, sunDirection));
        double segmentLengthLight = isectSun.Dist/ numSamplesLight, tCurrentLight = 0;
        double opticalDepthLightR = 0, opticalDepthLightM = 0;
        int j;
        for (j = 0; j < numSamplesLight; ++j)
        {
            Vector samplePositionLight = samplePosition + sunDirection * (tCurrentLight + segmentLengthLight * rand.NextDouble());
            double heightLight = samplePositionLight.Length() - earthRadius;
            if (heightLight < 0) break;
            opticalDepthLightR += Math.Exp(-heightLight / Hr) * segmentLengthLight;
            opticalDepthLightM += Math.Exp(-heightLight / Hm) * segmentLengthLight;
            tCurrentLight += segmentLengthLight;
        }
        if (j == numSamplesLight)
        {
            Vector tau = betaR * (opticalDepthR + opticalDepthLightR) + betaM * 1.1f * (opticalDepthM + opticalDepthLightM);
            Vector attenuation = new Vector(Math.Exp(-tau.X), Math.Exp(-tau.Y), Math.Exp(-tau.Z));
            sumR += attenuation * hr;
            sumM += attenuation * hm;
        }
        tCurrent += segmentLength;
    }

    // We use a magic number here for the intensity of the sun (20). We will make it more
    // scientific in a future revision of this lesson/code
    return (sumR * betaR * phaseR + sumM * betaM * phaseM) * sunIrradiance;
}


下面的图像每像素渲染400个样本,但是与上面的图像不同:



有人能找出我错了的地方吗?
谢谢,

评论

如果不阅读代码,仅阅读图片,我的直觉将是仔细检查集成。看起来沿射线吸收了太多的光。我将首先检查在第一个实现中,更改步数时颜色保持不变(只是确保它是一个很好的参考图像),然后我将再次检查MT实现中使用的吸收方程。

谢谢你的评论。仍然无法正常工作,我也不明白为什么。我沿射线均匀地采样一个点,而不是将其划分为段,而pdf仅为1 / dist。但是由于某些原因,它不能收敛到相同的结果。

该评论针对愿意提出答案的人们。但是,谢谢,这也使问题变得更加有趣。 :)

我会怀疑这行> Vector samplePositionLight = samplePosition + sunDirection *(tCurrentLight + segmentLengthLight * rand.NextDouble()); >>这应该使样本到光照均匀移动,为什么要进行随机长度的移动?

我之前已经实现了该算法,还发现没有必要为每个“像素”生成多条光线。从视角方向行进的单个光线,对于每个步骤,都朝着光行进,足以获得高质量的结果。您是否尝试过每个像素仅一次调用ComputeIncidentLight? (另请参阅我的上一篇文章,您以可能破坏了样本的方式修改了示例光线行进)

#1 楼

要采样均匀的体积,请使用光子的平均自由程:
float dt = -logf(1.0f - Xi) / uT;
其中:

Xi是[0,1]中的随机变量
是消光系数(散射和吸收的总和)

在均匀体积中,射线时间(t)的pdf由下式给出:
float pdf = uT * expf(-t * uT);
您没有统一的体积,而是密度随与球体表面距离的增加而呈指数递减的方法。
解决此问题的方法通常包括射线法,该方法在每个步骤都对密度进行采样并将该步骤视为均匀的体积。这是一个近似值,并引入了一些偏差,但是有减小偏差的方法。
我建议阅读基于物理体绘制的蒙特卡洛方法:

https://jannovak.info/publications /VolumeCourse/index.html

另一个好的资源是pbrtv3,尤其是第11和15章:

http://www.pbr-book.org/3ed-2018/ Volume_Scattering.html
http://www.pbr-book.org/3ed-2018/Light_Transport_II_Volume_Rendering.html