Skip to content

Antialiasing

Utils:Random Class

使用 <cstdlib> 提供的随机数以获得所需的随机数。

random.h
 //
// Created by Klingsor on 2026/6/7.
//

#ifndef RT1WEEK_RANDOM_H
#define RT1WEEK_RANDOM_H

#include <cmath>
#include <cstdlib>
#include <iostream>
#include <limits>
#include <memory>


inline double random_double() {
    // Returns a random real in [0,1).
    return std::rand() / (RAND_MAX + 1.0);
}

inline double random_double(double min, double max) {
    // Returns a random real in [min,max).
    return min + (max-min)*random_double();
}

#endif //RT1WEEK_RANDOM_H

Anti-Aliasing

Note

采样缩放因子:即\(1.0/采样次数\)

CPU计算乘法的速度比计算除法快。

抗锯齿的一种简单方法是对每个像素进行重采样,具体实现为计算指定采样次数的像素中心随机偏移点的颜色,将多次采样的结果累加并乘以缩放因子,得到采样后的颜色。
其中,相较于像素中心的偏移量为\([0,0.5)\)

为此,需要修改camera类,增加sample_squart()get_ray()方法,调整render()方法。

修改后的camera类
//
// Created by Klingsor on 2026/6/5.
//

#ifndef RT1WEEK_CAMERA_H
#define RT1WEEK_CAMERA_H

#include "../shape/hittable.h"
#include "../shape/hittable_list.h"
#include "../shape/sphere.h"
#include "../math/rtweek.h"

class camera {
public:
    //将输出图像宽度和宽高比作为成员变量,此处的初始值仅作为默认,实际渲染时可以在调用render方法前修改为实际值
    double aspect_ratio=1.0;
    int image_width=100;

    //单像素采样次数
    int sample=100;

    //接收可击中对象列表,初始化世界空间和摄像机,逐像素发射光线计算渲染结果
    void render(std::ostream& out ,const hittable& world) {
        init();

        //PPM文件头
        out<<"P3\n"<<image_width<<' '<<image_height<<'\n'<<"255\n";

        //逐像素渲染 i-水平偏移 j-垂直偏移
        for (int j=0;j<image_height;j++) {
            //输出进度,\r表示光标回到当前行行首
            std::clog<<"\r当前进度:"<<(image_height-j)<<' '<<std::flush;
            for (int i=0;i<image_width;i++) {
                //重采样并计算颜色平均值
                color pixel_color=color(0.0,0.0,0.0);
                for (int k=0;k<sample;k++) {
                    ray r=get_ray(i,j);
                    pixel_color+=ray_color(r,world);
                }
                pixel_color*=pixel_sample_scale;
                //将颜色输出到文件
                write_color(out,pixel_color);
            }
        }
        std::clog<<"\r结束                                     \n";

    }
private:
    int image_height=100;
    point3 camera_center;
    double pixel_sample_scale;     //缩放因子

    //像素在水平和垂直方向上的步进增量
    point3 pixel_delta_u;
    point3 pixel_delta_v;

    //视口左上角顶点像素的像素中心
    point3 pixel00_loc;


    void init() {
        pixel_sample_scale=1.0/sample;
        image_height=image_width/aspect_ratio<1?1:image_width/aspect_ratio;

        //Camera
        auto focal_length=1.0;     //焦距
        auto view_height=2.0;      //视口高度
        auto view_width=view_height*((double)image_width/image_height);  //视口宽度
        camera_center=point3(0,0,0);    //相机/视点坐标

        //从视口左边缘到右边缘、上边缘到下边缘的向量
        auto view_u=vec3(view_width,0.0,0.0);
        auto view_v=vec3(0.0,-view_height,0.0);

        //像素在水平和垂直方向上的步进增量
        //这表示每向右/下移动一个像素,在3d空间中需要移动的距离
        //3d空间是指视口所在的3d空间。我们将焦距设为1,这里的步进增量也是相对这个单位1定义的坐标系
        pixel_delta_u=view_u/image_width;
        pixel_delta_v=view_v/image_height;

        //获得视口左上角顶点在3d空间中的坐标
        auto view_upper_left=camera_center-vec3(0,0,focal_length)-view_v/2-view_u/2;
        //获得视口左上角顶点像素的像素中心
        pixel00_loc=view_upper_left+0.5*(pixel_delta_u+pixel_delta_v);

    }

    //根据随机偏移量计算实际光线
    ray get_ray(int i,int j) {
        auto offset =sample_squart();
        //偏移后的实际像素中心
        auto pixel_offset=pixel00_loc+(
            ((double)i+offset.x)*pixel_delta_u+
            ((double)j+offset.y)*pixel_delta_v);

        auto origin=camera_center;
        auto dirction=pixel_offset-origin;
        return ray(origin,dirction);
    }

    color ray_color(const ray& r,const hittable& world) {
        //检查当前射线是否和实体对象相交
        hit_record temp_rec;
        if (world.hit(r,interval(0,infinity),temp_rec)) {
            //返回交点的法线映射的颜色
            return 0.5*(temp_rec.normal+color(1.0,1.0,1.0));
        }

        //如果没有交点,则绘制渐变背景
        //将光线转换为单位向量
        vec3 unit_direction=unit_vector(r.direction());
        //将y坐标的范围从[-1,1]映射到[0,1],便于之后按比例混合颜色
        //由于主函数规定的视口高度为2,以视口中心为0,则高度的范围就是【-1,1】
        auto a=0.5*(unit_direction.y+1.0);
        //返回白色和蓝色混合后的颜色
        return (1.0-a)*color(1.0,1.0,1.0)+a*color(0.5,0.7,1.0);
    }

    //单次采样偏移量
    //将范围从random_double的(0,1)映射到(-0.5,0.5)
    vec3 sample_squart() const {
        return vec3(random_double()-0.5,random_double()-0.5,0.0);
    }
};

#endif //RT1WEEK_CAMERA_H