今天简单的学习一下相关类的定义和作用

Vec3类

图形程序中需要一些用于存储几何向量和颜色的类,这里的话我们设置一个最简单的,只需要三个坐标就够了。我们使用相同的类vec3来表示颜色、位置、方向、偏移。所以我们会为这个类声明另外两个别名point3color,但是要注意,不要将一个point3添加到一个color

我们定义一个类文件:

#ifndef RENDER_C___VEC3_H
#define RENDER_C___VEC3_H

#include <cmath>
#include <iostream>
// 向量类
class vec3 {
public:
    double e[3];

    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 {-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*=(double t){
        e[0] *= t;
        e[1] *= t;
        e[2] *= t;
        return *this;
    }

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

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

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

// 设置别名
using point3 = vec3;

// 设置内联函数
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 {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 {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 {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 {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/(const 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 {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 uint_vector(const vec3& v){
    return v/v.length();
}

#endif //RENDER_C___VEC3_H

其中大量应用了函数的重载,不过对着《c++ primer plus》还是看的差不多了。

这里的小数我们使用了double 数据类型,因为它更加的准确,不过还是以自己使用的机器为主,看你内存空间咯

vec3类在颜色中的应用

基于vec3类,定义一个color.h,并向里面定义一个写入像素的函数


#ifndef RENDER_C___COLOR_H
#define RENDER_C___COLOR_H

#include "vec3.h"

using color = vec3;

void write_color(std::ostream& out,const color& pixel_color){
    auto r = pixel_color.x();
    auto g = pixel_color.y();
    auto b = pixel_color.z();

    int rbyte = int (255.999 * r);
    int gbyte = int (255.999 * g);
    int bbyte = int (255.999 * b);
    
    out << rbyte << ' ' << gbyte << ' ' << bbyte << '\n';
}

#endif //RENDER_C___COLOR_H

现在我们可以用我们定义的类去实现我们昨天手搓出来的图片了

#include "color.h"
#include "vec3.h"

#include <iostream>

int main(){
    int image_width = 256;
    int image_height = 256;

    std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
    for (int j=0;j<image_height;j++){
        std::clog << "\rScanlines remaining: "<< (image_height-j) << '\r' << std::flush;
        for(int i =0;i<image_width;i++){
            auto pixel_color = color(double(i)/(image_width-1),double(j)/(image_height-1),0);
            write_color(std::cout,pixel_color);
        }
    }
    std::clog << "\rDone\n";
}

虽然没有节省多少内容,但是在后续的过程中,这个简便性会慢慢体现出来。

基于vec3实现射线类

我们需要一个光线类,来实现对光线的计算。我们可以用函数P(t)=A+btP(t) = A + bt来实现光线路径的模拟。其中A指的是射线的起点,b指的是射线的方向,t是光线的延伸。我们将这个射线的概念封装为一个类,其中用于计算的函数P(t)P(t)用函数ray::at(t)表示

#ifndef RENDER_C___RAY_H
#define RENDER_C___RAY_H

#include "vec3.h"

class ray{
public:
    ray(){}

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

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

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

#endif //RENDER_C___RAY_H

这里将射线的起点和方向进行了封装,只能通过接口访问以确保程序的完整与安全

现在我们接下来要用到几个类就设置完成啦