首页 > 其他分享 >Ray Tracing In One Weekend

Ray Tracing In One Weekend

时间:2023-04-20 17:13:04浏览次数:54  
标签:const auto color Tracing vec3 double return Weekend Ray

2. Output an Image

  • PPM文件格式
  • 写PPM文件内容
#include <iostream>

int main() {

    // Image

    const int image_width = 256;
    const int image_height = 256;

    // Render

    std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";

    for (int j = image_height - 1; j >= 0; --j) {
        std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
        for (int i = 0; i < image_width; ++i) {
            auto r = double(i) / (image_width - 1);
            auto g = double(j) / (image_height - 1);
            auto b = 0.25;

            int ir = static_cast<int>(255.999 * r);
            int ig = static_cast<int>(255.999 * g);
            int ib = static_cast<int>(255.999 * b);

            std::cout << ir << ' ' << ig << ' ' << ib << '\n';
        }
    }

    std::cerr << "\nDone.\n";
}
  • 生成.ppm文件
.\RayTracing.exe > image.ppm

3. The vec3 Class

  • vec3.h定义了一个三维矢量,包括矢量的值和一系列数值操作,此外,将其重命名为point3与color。
#ifndef VEC3_H
#define VEC3_H

#include <cmath>
#include <iostream>

using std::sqrt;

class vec3 {
    public:
        vec3() : e{0,0,0} {}
        vec3(double e0, double e1, double e2) : e{e0, e1, e2} {}

        double x() const { return e[0]; }
        double y() const { return e[1]; }
        double z() const { return e[2]; }

        vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }
        double operator[](int i) const { return e[i]; }
        double& operator[](int i) { return e[i]; }

        vec3& operator+=(const vec3 &v) {
            e[0] += v.e[0];
            e[1] += v.e[1];
            e[2] += v.e[2];
            return *this;
        }

        vec3& operator*=(const double t) {
            e[0] *= t;
            e[1] *= t;
            e[2] *= t;
            return *this;
        }

        vec3& operator/=(const double t) {
            return *this *= 1/t;
        }

        double length() const {
            return sqrt(length_squared());
        }

        double length_squared() const {
            return e[0]*e[0] + e[1]*e[1] + e[2]*e[2];
        }

    public:
        double e[3];
};

// Type aliases for vec3
using point3 = vec3;   // 3D point
using color = vec3;    // RGB color

#endif
  • vec3 utility functions也被定义在头文件vec3.h中,包括一系列的运算。
// vec3 Utility Functions

inline std::ostream& operator<<(std::ostream &out, const vec3 &v) {
    return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2];
}

inline vec3 operator+(const vec3 &u, const vec3 &v) {
    return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]);
}

inline vec3 operator-(const vec3 &u, const vec3 &v) {
    return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]);
}

inline vec3 operator*(const vec3 &u, const vec3 &v) {
    return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]);
}

inline vec3 operator*(double t, const vec3 &v) {
    return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
}

inline vec3 operator*(const vec3 &v, double t) {
    return t * v;
}

inline vec3 operator/(vec3 v, double t) {
    return (1/t) * v;
}

inline double dot(const vec3 &u, const vec3 &v) {
    return u.e[0] * v.e[0]
         + u.e[1] * v.e[1]
         + u.e[2] * v.e[2];
}

inline vec3 cross(const vec3 &u, const vec3 &v) {
    return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1],
                u.e[2] * v.e[0] - u.e[0] * v.e[2],
                u.e[0] * v.e[1] - u.e[1] * v.e[0]);
}

inline vec3 unit_vector(vec3 v) {
    return v / v.length();
}
  • Color Utility Functions被定义在color.h中,用于输出一个像素的RGB值
#ifndef COLOR_H
#define COLOR_H

#include "vec3.h"

#include <iostream>

void write_color(std::ostream &out, color pixel_color) {
    // Write the translated [0,255] value of each color component.
    out << static_cast<int>(255.999 * pixel_color.x()) << ' '
        << static_cast<int>(255.999 * pixel_color.y()) << ' '
        << static_cast<int>(255.999 * pixel_color.z()) << '\n';
}

#endif
  • 使用头文件定义的类,改写cpp文件
#include "color.h"
#include "vec3.h"

#include <iostream>

int main() {

    // Image

    const int image_width = 256;
    const int image_height = 256;

    // Render

    std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";

    for (int j = image_height-1; j >= 0; --j) {
        std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
        for (int i = 0; i < image_width; ++i) {
            color pixel_color(double(i)/(image_width-1), double(j)/(image_height-1), 0.25);
            write_color(std::cout, pixel_color);
        }
    }

    std::cerr << "\nDone.\n";
}

4. Rays, a Simple Camera, and Background

  • 将Ray类定义在ray.h文件中,主要内容是起点和方向俩个vec3类成员,以及查询t时刻的光线位置的成员函数at
#ifndef RAY_H
#define RAY_H

#include "vec3.h"

class ray {
    public:
        ray() {}
        ray(const point3& origin, const vec3& direction)
            : orig(origin), dir(direction)
        {}

        point3 origin() const  { return orig; }
        vec3 direction() const { return dir; }

        point3 at(double t) const {
            return orig + t*dir;
        }

    public:
        point3 orig;
        vec3 dir;
};

#endif
  • 按照如下方式给出camera
  • 下面是一个简单的着色尝试,按照camera上面像素的光线向量的单位矢量的y值作为蓝色与白色之间的插值进行着色。
#include "color.h"
#include "ray.h"
#include "vec3.h"

#include <iostream>

color ray_color(const ray& r) {
    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);
}

int main() {

    // Image
    const auto aspect_ratio = 16.0 / 9.0;
    const int image_width = 400;
    const int image_height = static_cast<int>(image_width / aspect_ratio);

    // Camera

    auto viewport_height = 2.0;
    auto viewport_width = aspect_ratio * viewport_height;
    auto focal_length = 1.0;

    auto origin = point3(0, 0, 0);
    auto horizontal = vec3(viewport_width, 0, 0);
    auto vertical = vec3(0, viewport_height, 0);
    auto lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length);

    // Render

    std::cout << "P3\n" << image_width << " " << image_height << "\n255\n";

    for (int j = image_height-1; j >= 0; --j) {
        std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
        for (int i = 0; i < image_width; ++i) {
            auto u = double(i) / (image_width-1);
            auto v = double(j) / (image_height-1);
            ray r(origin, lower_left_corner + u*horizontal + v*vertical - origin);
            color pixel_color = ray_color(r);
            write_color(std::cout, pixel_color);
        }
    }

    std::cerr << "\nDone.\n";
}

效果为:

5. Adding a Sphere

  • 光线与球面的相交检测
    对于一个球面,其implicit model为

    可以表示成矢量形式

    利用光线的传播形式计算交点:


    得到一个二阶多项式方程:

    因此,可以判断交点的个数,以及求解出交点位置。在本文中将只有一个焦点视作不相交。
  • 定义一个相交检测函数hit_sphere,并修改着色函数将相交处着色为红色。
bool hit_sphere(const point3& center, double radius, const ray& r) {
    vec3 oc = r.origin() - center;
    auto a = dot(r.direction(), r.direction());
    auto b = 2.0 * dot(oc, r.direction());
    auto c = dot(oc, oc) - radius*radius;
    auto discriminant = b*b - 4*a*c;
    return (discriminant > 0);
}

color ray_color(const ray& r) {
    if (hit_sphere(point3(0,0,-1), 0.5, r))
        return color(1, 0, 0);
    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);
}


然而,这种检测将光线视作直线,所以需要进行合理性判断。

6. Surface Normals and Multiple Objects

  • 在hit_sphere函数内部加入相交点的法线计算部分,进一步的利用法线坐标表示颜色来检测法线计算的效果
double hit_sphere(const point3& center, double radius, const ray& r) {
    vec3 oc = r.origin() - center;
    auto a = dot(r.direction(), r.direction());
    auto b = 2.0 * dot(oc, r.direction());
    auto c = dot(oc, oc) - radius*radius;
    auto discriminant = b*b - 4*a*c;
    if (discriminant < 0) {
        return -1.0;
    } else {
        return (-b - sqrt(discriminant) ) / (2.0*a);
    }
}

color ray_color(const ray& r) {
    auto t = hit_sphere(point3(0,0,-1), 0.5, r);
    if (t > 0.0) {
        vec3 N = unit_vector(r.at(t) - vec3(0,0,-1));
        return 0.5*color(N.x()+1, N.y()+1, N.z()+1);
    }
    vec3 unit_direction = unit_vector(r.direction());
    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);
}

  • 对hit_sphere进行优化,使用half_b代替b则减少了数乘计算;同时使用vec3的成员函数进行内积运算,减少了对dot的使用,即减少了对中间vec3变量的需求。
double hit_sphere(const point3& center, double radius, const ray& r) {
    vec3 oc = r.origin() - center;
    auto a = r.direction().length_squared();
    auto half_b = dot(oc, r.direction());
    auto c = oc.length_squared() - radius*radius;
    auto discriminant = half_b*half_b - a*c;

    if (discriminant < 0) {
        return -1.0;
    } else {
        return (-half_b - sqrt(discriminant) ) / a;
    }
}
  • 在hit.h头文件中定义了相交结果的结构体,此外定义了一个相交检测的抽象类,内部定义了相交检测的纯虚函数。该hit函数参数有光线,最大最小时间限定,与hit_record结构体,时间限定用于进一步判断相交是否有效,用于判断相交位置是否是最近位置,以减少法线计算的需求,hit_record则用于记录最近相交位置的信息。
#ifndef HITTABLE_H
#define HITTABLE_H

#include "ray.h"

struct hit_record {
    point3 p;
    vec3 normal;
    double t;
};

class hittable {
    public:
        virtual bool hit(const ray& r, double t_min, double t_max, hit_record& rec) const = 0;
};

#endif
  • 此外在sphere.h头文件中定义了sphere类,记录了球体的基本数值信息,此外继承了hittable类,并重写了hit函数。
#ifndef SPHERE_H
#define SPHERE_H

#include "hittable.h"
#include "vec3.h"

class sphere : public hittable {
    public:
        sphere() {}
        sphere(point3 cen, double r) : center(cen), radius(r) {};

        virtual bool hit(
            const ray& r, double t_min, double t_max, hit_record& rec) const override;

    public:
        point3 center;
        double radius;
};

bool sphere::hit(const ray& r, double t_min, double t_max, hit_record& rec) const {
    vec3 oc = r.origin() - center;
    auto a = r.direction().length_squared();
    auto half_b = dot(oc, r.direction());
    auto c = oc.length_squared() - radius*radius;

    auto discriminant = half_b*half_b - a*c;
    if (discriminant < 0) return false;
    auto sqrtd = sqrt(discriminant);

    // Find the nearest root that lies in the acceptable range.
    auto root = (-half_b - sqrtd) / a;
    if (root < t_min || t_max < root) {
        root = (-half_b + sqrtd) / a;
        if (root < t_min || t_max < root)
            return false;
    }

    rec.t = root;
    rec.p = r.at(rec.t);
    rec.normal = (rec.p - center) / radius;

    return true;
}

#endif
  • 为了检测光线是从内部还是外部相交,在hit_record中定义一个bool成员,此外定义一个内联成员函数用于判断内外关系,并更新法线和内外关系。相应的,修改sphere::hit。
struct hit_record {
    point3 p;
    vec3 normal;
    double t;
    bool front_face;

    inline void set_face_normal(const ray& r, const vec3& outward_normal) {
        front_face = dot(r.direction(), outward_normal) < 0;
        normal = front_face ? outward_normal :-outward_normal;
    }
};
bool sphere::hit(const ray& r, double t_min, double t_max, hit_record& rec) const {
    ...

    rec.t = root;
    rec.p = r.at(rec.t);
    vec3 outward_normal = (rec.p - center) / radius;
    rec.set_face_normal(r, outward_normal);

    return true;
}

标签:const,auto,color,Tracing,vec3,double,return,Weekend,Ray
From: https://www.cnblogs.com/etherovo/p/17336667.html

相关文章

  • [未解决] Cesium 报错 undefined RangeError: Invalid array length
    Anerroroccurredwhilerendering.Renderinghasstopped.undefinedRangeError:InvalidarraylengthRangeError:InvalidarraylengthatupdateFrustums(webpack-internal:///./src/source/CesiumEngine/Scene/View.js:94:36)atView.createPotentiallyV......
  • cpp:Double Dimensional Array using vector 2
     //StudentArry.h:此文件包含"StudentArry"类。学生数组成绩显示方法C++14//2023年4月9日涂聚文GeovinDuedit.//(1)vec1.size()就是”二维数组”的行数//(2)vec1[0].size()就是”二维数组”的列数//vector<vector<double>>geovindu#pragmaonce#ifndefSTUD......
  • Programming: array
      Cintarr[5];bracketsmustfollowtheidentifier,definitionofvariablewitharraytypeneedsanexplicitsizeofaninitializer  ......
  • 16 Ray Tracing (Monte Carlo Path Tracing)
    关键点MonteCarloIntegrationDistributedRayTracingPathTracingRussianRoulette(RR)SamplingtheLight(puremath)1.MonteCarloIntegration蒙特卡洛积分对于没有解析式的对象,可以使用该方法求其定积分。在积分范围内随机采样一个值,作为高,使用区间长度作为宽,......
  • 15 Ray Tracing (Rendering Equation)
    关键点BRDF(BidirectionalReflectanceDistributionFunction)ReflectionEquationRenderingEquation1.BidirectionalReflectanceDistributionFunction(BRDF)1.1BRDF反射可以理解为光线打到物体表面被吸收,然后按照某些方向再辐射出去一部分。BRDF定义了从某一个......
  • Java:ArrayList初始化赋值
    测试环境$java-versionjavaversion"1.8.0_251"Java(TM)SERuntimeEnvironment(build1.8.0_251-b08)JavaHotSpot(TM)64-BitServerVM(build25.251-b08,mixedmode)方式一:常规方式List<Integer>list=newArrayList<>();list.add(1);list......
  • invalid comparison: java.util.ArrayList and java.lang.String 异常分析及解决方法
    nvalidcomparison:java.util.ArrayListandjava.lang.String异常解决方法异常原因首先我们可以确定是在mybatis的xml中的list操作出现错误然后发现在接收list的时候加了判断list!=’’,导致list(数组集合类型)和空字符串(字符串类型)进行比较,故报错解决办法,对于list类型进......
  • Java 实现Arrays 数组工具类
    ClassArrays是java工具包自带的非常强大的数组工具类,今天手工实现了一部分功能,部分参考实现如下publicclassMyArrays{//最大值/***获取int数组最大值**@paramarr:代遍历的数组*@return数组最大值*/publicintgetMax(......
  • 初始化ArrayList的方式
    本片文章用于归纳java中初始化一个ArrayList的几种方式add方法添加最基础的方式就是我们在new一个ArrayList之后使用add方法添加元素/***第一种方式,通过add方法*@return*/publicList<String>initOne(){List<String>list=newArrayL......
  • 镭速Raysync v6.6.8.0版本发布
    最近镭速发布了v6.6.8.0版本,已经发布上线了。主要更新内容有服务器下发任务支持指定客户端,客户端增加日志清理和日志压缩,自动删除源文件保持源目录结构,支持将文件投递给其他成员等功能,详细的更新内容如下:一、服务器下发任务支持指定客户端在后管创建服务器任务时,可以指定想要......