想看原书可以看本系列的第一篇《Ray Tracing in a Weekend》学习笔记01
8.3 Using Gamma Correction for Accurate Color Intensity
这张照片很暗,这个球体在现实生活中应该是浅灰色。我们的球体在每次反弹仅吸收一半的能量,因此是50%反射器。看上去暗的原因是几乎所有的图像查看器都是假定图像经过“伽马校正”的,这意味着0到1的值存储为字节之前会有一些变换。我们可以使用“伽马2”,也就是将颜色提升为1/gamma的幂,或者通常就是平方根。
//[color.h] write_color(), with gamma correction
void write_color(std::ostream &out, color pixel_color, int samples_per_pixel) {
auto r = pixel_color.x();
auto g = pixel_color.y();
auto b = pixel_color.z();
// Divide the color by the number of samples and gamma-correct for gamma=2.0.
auto scale = 1.0 / samples_per_pixel;
r = sqrt(scale * r);
g = sqrt(scale * g);
b = sqrt(scale * b);
// Write the translated [0,255] value of each color component.
out << static_cast<int>(256 * clamp(r, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(g, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(b, 0.0, 0.999)) << '\n';
}
8.4 Fixing Shadow Acne
还有一个细微的错误。 一些反射的射线不是恰好在t = 0时撞击到它们所反射的对象,而是在t = -0.0000001或t = 0.00000001或球体相交给我们的任何浮点近似值处射出。 因此,我们需要忽略非常接近零的点击:
//[main.cc] Calculating reflected ray origins with tolerance
if (world.hit(r, 0.001, infinity, rec)) {
8.5 True Lambertian Reflection
这里介绍的方法是在单位球沿曲面法线偏移的位置产生随机点。这意味着接近法线的位置概率更高,偏离法线的位置概率较低。此分布是按cos^3(ϕ)缩放,ϕ是偏离方向与法线的夹角。这是合理的,因为以浅角度到达的光,会散布到更大的区域上,因此他们对最终颜色贡献较小。
我们对具有cos(ϕ)分布的Lambertian分布感兴趣。 True Lambertian射线接近法线的可能性更高,但是分布更均匀。 这是通过在单位球面上拾取沿曲面法线偏移的点来实现的。 可以通过在单位球中拾取点,然后对其进行归一化来获得球体上的拾取点。
//[vec3.h] The random_unit_vector() function
//单位球体表面上一点满足x^2+y^2+z^2=1;
vec3 random_unit_vector() {
auto a = random_double(0, 2*pi);
auto z = random_double(-1, 1);
auto r = sqrt(1 - z*z);
return vec3(r*cos(a), r*sin(a), z);
}
该random_unit_vector()替代了现有random_in_unit_sphere()函数。
//[main.c] ray_color() with replacement diffuse
color ray_color(const ray& r, const hittable& world, int depth) {
hit_record rec;
// If we've exceeded the ray bounce limit, no more light is gathered.
if (depth <= 0)
return color(0,0,0);
if (world.hit(r, 0.001, infinity, rec)) {
point3 target = rec.p + rec.normal + random_unit_vector();
return 0.5 * ray_color(ray(rec.p, target - rec.p), world, depth-1);
}
vec3 unit_direction = unit_vector(r.direction());
auto t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}
这两个变化都是由于光线的散射更加均匀,朝法线散射的光线更少。 这意味着对于散射的物体,它们会显得更亮,因为更多的光会朝着相机反弹。 对于阴影,较少的光直接向上反射,因此较大球体的正下方较小球体的部分更亮。
8.6 An Alternative Diffuse Formulation
一种更直观的方法是,对于远离击点的所有角度都具有统一的散射方向,而不依赖于与法线的角度。 许多第一批射线追踪论文都使用这种扩散方法(在采用Lambertian散射之前)。
//[vec3.h] The random_in_hemisphere(normal) function
vec3 random_in_hemisphere(const vec3& normal) {
vec3 in_unit_sphere = random_in_unit_sphere();
if (dot(in_unit_sphere, normal) > 0.0) // In the same hemisphere as the normal
return in_unit_sphere;
else
return -in_unit_sphere;
}
将新公式插入ray_color()函数:
//[main.c] ray_color() with hemispherical scattering
color ray_color(const ray& r, const hittable& world, int depth) {
hit_record rec;
// If we've exceeded the ray bounce limit, no more light is gathered.
if (depth <= 0)
return color(0,0,0);
if (world.hit(r, 0.001, infinity, rec)) {
point3 target = rec.p + random_in_hemisphere(rec.normal);
return 0.5 * ray_color(ray(rec.p, target - rec.p), world, depth-1);
}
vec3 unit_direction = unit_vector(r.direction());
auto t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}