我正在以其他方式编辑和发布此问题;从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个样本,但是与上面的图像不同:
有人能找出我错了的地方吗?
谢谢,
#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
评论
如果不阅读代码,仅阅读图片,我的直觉将是仔细检查集成。看起来沿射线吸收了太多的光。我将首先检查在第一个实现中,更改步数时颜色保持不变(只是确保它是一个很好的参考图像),然后我将再次检查MT实现中使用的吸收方程。谢谢你的评论。仍然无法正常工作,我也不明白为什么。我沿射线均匀地采样一个点,而不是将其划分为段,而pdf仅为1 / dist。但是由于某些原因,它不能收敛到相同的结果。
该评论针对愿意提出答案的人们。但是,谢谢,这也使问题变得更加有趣。 :)
我会怀疑这行> Vector samplePositionLight = samplePosition + sunDirection *(tCurrentLight + segmentLengthLight * rand.NextDouble()); >>这应该使样本到光照均匀移动,为什么要进行随机长度的移动?
我之前已经实现了该算法,还发现没有必要为每个“像素”生成多条光线。从视角方向行进的单个光线,对于每个步骤,都朝着光行进,足以获得高质量的结果。您是否尝试过每个像素仅一次调用ComputeIncidentLight? (另请参阅我的上一篇文章,您以可能破坏了样本的方式修改了示例光线行进)