我跟随射线追踪你的余生来实现射线追踪器,但是对数学的解释(主要是pdf部分)让我感到困惑,所以我按照渲染方程来理解背后的数学。结果,我用普通蒙特卡罗方法(由方程导出)对场景进行了采样,去除了渲染方程中的cos(θ),得到了一些正确的结果,但用cos(θ)得到了一幅不正确的暗图像。更糟糕的是,渲染没有收敛:随着样本数量的增加,渲染变得越来越暗。
从左到右:500个没有cos(θ)的样本,500个带有cos(θ)的样本,100个带有cos(θ)的样本:

代码并不复杂。对于每一次采样,相机向函数ray_trace(Ray ray, int depth)发送一条光线,当光线击中光线或在反射足够的时间(depth <= 0)后找不到光线时,该函数终止。当它碰到场景中的某物时(除了光之外,场景中唯一的物质是兰伯材料),散射射线是由物质的scatter(Ray ray, Intersection intersection, Ray &scattered_ray)函数产生的。在Lambertian的scatter()函数中,散射射线是由半球周围的均匀采样产生的。然后这条散射射线成为函数ray_trace中的新射线,从而depth = depth-1,并开始另一轮射线追踪。
{
// initially, this ray is generated from a camera
if (depth <= 0)
{
// return black when no light found
return Vec3(0.0, 0.0, 0.0);
}
auto intersection = scene.intersect(ray);
if (!intersection.intersected)
{
// return black when hits nothing
return Vec3(0.0, 0.0, 0.0);
}
if (intersection.hit_a_light)
{
// found a light
return intersection.material.emit(...);
}
Ray scattered_ray;
Vec3 attenuation = intersection.material.scatter(ray, intersection, &scattered_ray);
if (with_cosine_theta)
{
Vec3 cosine_theta = dot(intersection.normal.normalize(), scattered_ray.direction.normalize());
return cosine_theta * attenuation * ray_trace(scattered_ray, depth - 1);
}
else
{
// without cosine(theta)
return attenuation * ray_trace(scattered_ray, depth - 1);
}
}
Vec3 Lambertian::scatter(Ray ray, Intersection intersection, Ray &scattered_ray)
{
double phi = random(0.0, 2.0 * PI);
double sin_phi = sine(phi);
double cos_phi = cosine(phi);
double cos_theta = random(-1.0, 1.0);
double sin_theta = sqrt(1 - cos_theta * cos_theta);
Vec3 scattered_direction = Vec3(sin_phi * sin_theta, cos_phi * sin_theta, cos_theta);
// generate random direction in a hemisphere
if (dot(scattered_direction, intersection.normal) < 0)
{
scattered_direction = -scattered_direction;
}
scattered_ray.origin = intersection.hit_point;
scattered_ray.direction = scattered_direction;
return this.albedo;
}有人能指出是什么让我的渲染错误吗?
最新情况:
遵循着电灯泡的想法,我尝试了这两种方法(https://codeshare.io/bvR8ev上的代码):
cos(θ)保持在ray_trace(),均匀样本半球在scatter() (WITH_COSINE=true,UNIFORM_SAMPLE=true)cos(θ)在ray_trace()和样本半球中的cos(θ)重要性(WITH_COSINE=false,UNIFORM_SAMPLE=false)对于第一种方法,我有(100/200/500样本):

关于第二种方法(100/200/500样本):

从左到右:100个样本,200个样本,500个样本
发布于 2022-05-19 08:09:43
没有余弦的渲染可能是错误的,因为您使用的是均匀球面采样,并且余弦必须在那里(除非假设brdfs包含1/cos(θ)项)。也要这样做:
pdf = 1.0/(2.0*PI);
return cosine_theta * attenuation / pdf * ray_trace(scattered_ray, depth - 1);也适用于非余弦部分:
pdf = 1.0/(2.0*PI);
return attenuation/pdf * ray_trace(scattered_ray, depth - 1);如果您想使用非余弦估计,那么您将不得不使用余弦分布抽样。它看起来是这样的:
Vec3 Lambertian::scatter_cosine_dist(Ray ray, Intersection intersection, Ray &scattered_ray)
{
double phi = random(0.0, 2.0 * PI);
double sin_phi = sine(phi);
double cos_phi = cosine(phi);
double cos_theta = random(-1.0, 1.0);
double sin_theta = sqrt(1 - cos_theta * cos_theta);
Vec3 scattered_direction = Vec3(sin_phi * sin_theta, cos_phi * sin_theta, cos_theta).normalize();
scattered_ray.origin = intersection.hit_point;
scattered_ray.direction = (intersection.normal+scattered_direction).normalize();
return this.albedo;
}然后也修改非余弦估计器的pdf:
pdf_wo_cos = 1.0/PI;
return attenuation/pdf_wo_cos * ray_trace(scattered_ray, depth - 1);这里面的pdf实际上是cos(theta)/PI,但是余弦与呈现方程中的余弦相抵消。如果对不带余弦的估计器采用余弦分布抽样,而对具有余弦的估计器采用均匀抽样,则应得到与噪声相等的结果(余弦估计器应产生较少的噪声)。
发布于 2022-06-21 11:03:11
你为intersection.normal和scattered_ray.direction使用了什么坐标系?一种典型的方法是在局部空间中采样散射方向,然后将其转化为世界空间。因此,余弦θ将在采样函数中计算。我不确定是否是坐标系引起了你们的问题。
https://computergraphics.stackexchange.com/questions/12700
复制相似问题