C++控制台写光线追踪渲染器

最近在跟着朋友学光线追踪,看着纯控制台居然能写出光线追踪渲染器,感觉非常神奇。
先上效果吧:

C++控制台写光线追踪渲染器_第1张图片
下面是代码:

vec3.h

#pragma once

#include 
#include 
#include 
class vec3
{
public:
	vec3() {}
	vec3(double e0, double e1, double e2) { e[0] = e0; e[1] = e1; e[2] = e2; }

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

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

	inline vec3& operator+=(const vec3 &v2);
	inline vec3& operator-=(const vec3 &v2);
	inline vec3& operator*=(const vec3 &v2);
	inline vec3& operator/=(const vec3 &v2);
	inline vec3& operator*=(const double t);
	inline vec3& operator/=(const double t);

	inline double length() const { return sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]); }
	inline double squared_length() const { return e[0] * e[0] + e[1] * e[1] + e[2] * e[2]; }
	inline void make_unit_vector();

	double e[3];
};

//文件读取流
inline std::istream& operator >> (std::istream &is, vec3 &t) {
	is >> t.e[0] >> t.e[1] >> t.e[2];
	return is;
}
//文件输出流
inline std::ostream& operator<<(std::ostream &os, const vec3 &t) {
	os << t.e[0] << " " << t.e[1] << " " << t.e[2];
	return os;
}
//规整化
inline void vec3::make_unit_vector() {
	double k = 1.0 / sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
	e[0] *= k; e[1] *= k; e[2] *= k;
}

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

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

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

inline vec3 operator/(const vec3 &v1, const vec3 &v2) {
	return vec3(v1.e[0] / v2.e[0], v1.e[1] / v2.e[1], v1.e[2] / v2.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/(vec3 v, double t) {
	return vec3(v.e[0] / t, v.e[1] / t, v.e[2] / t);
}

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

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

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


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

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

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

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

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

inline vec3& vec3::operator/=(const double t) {
	double k = 1.0 / t;

	e[0] *= k;
	e[1] *= k;
	e[2] *= k;
	return *this;
}

inline vec3 unit_vector(vec3 v) {
	return v / v.length();
}

Ray.h
#pragma once

#include "vec3.h"

class ray
{
public:

	ray() = default;
	ray(const vec3& a, const vec3& b, double ti = 0) { A = a; B = b; _time = ti; }

	vec3 origin() const { return A; }
	vec3 direction() const { return B; }
	double time() const { return _time; }
	vec3 point_at_parameter(double t) const { return A + t * B; }

	vec3 A;
	vec3 B;
	double _time;
};

HitShap.h
#pragma once

#include "Ray.h"

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

class hitable
{
public:

	virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const = 0;

};

class sphere : public hitable
{
public:

	sphere() = default;
	sphere(vec3 cen, float r) : center(cen), radius(r){}
	virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const override
	{
		vec3 oc = r.origin() - center;
		double a = dot(r.direction(), r.direction());
		double b = dot(oc, r.direction());
		double c = dot(oc, oc) - radius * radius;
		double discripminant = b * b - a * c;

		if (discripminant > 0)
		{
			double temp = (-b - sqrt(b * b - a * c)) / a;
			if (temp < t_max && temp > t_min)
			{
				rec.t = temp;
				rec.p = r.point_at_parameter(rec.t);
				rec.normal = (rec.p - center) / radius;
				return true;
			}
		}
		return false;

	}
	vec3 center;
	float radius;

};

class hitable_list : public hitable
{
public:

	hitable_list() = default;
	hitable_list(hitable **l, int n)
	{
		list = l;
		list_size = n;
	}

	virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const override
	{
		hit_record temp_rec;
		bool hit_anything = false;
		double closest_so_far = t_max;
		for (int i = 0; ihit(r,t_min,closest_so_far,temp_rec))
			{
				hit_anything = true;
				closest_so_far = temp_rec.t;
				rec = temp_rec;
			}
		}
		return hit_anything;
	}

	hitable **list;
	int list_size;
};

Camera.h
#pragma once

#include "Ray.h"

class camera
{
public:

	camera()
	{
		lower_left_corner = vec3(-2.0, -1.0, -1.0);
		horizontal = vec3(4.0, 0.0, 0.0);
		vertical = vec3(0.0, 2.0, 0.0);
		origin = vec3(0.0, 0.0, 0.0);
	}
	ray get_ray(float u, float v)
	{
		return ray(origin, lower_left_corner + u * horizontal + v * vertical - origin);
	}

	vec3 origin;
	vec3 lower_left_corner;
	vec3 horizontal;
	vec3 vertical;
};

Render.cpp

#include 
#include 
#include "Ray.h"
#include "float.h"
#include "HitShap.h"
#include "Camera.h"

#include 
#include 
std::default_random_engine reng(time(nullptr));
std::uniform_real_distribution uni_dist(0.0f, 1.0f);

using namespace std;

vec3 radom_unit_sphere()
{
	vec3 p;

	do 
	{
		p = 2.0 * vec3(uni_dist(reng), uni_dist(reng), uni_dist(reng)) - vec3(1.0f, 1.0f, 1.0f);

	} while (p.squared_length() >= 1.0);

	return p;
}

vec3 color(const ray& r, hitable* world)
{
	hit_record rec;
	if (world->hit(r, 0.0f, DBL_MAX, rec))
	{
		vec3 target = rec.p + rec.normal + radom_unit_sphere();
		return 0.5f * color(ray(rec.p, target - rec.p), world);
	}
	else
	{
		vec3 unit_direction = unit_vector(r.direction());
		double t = 0.5f * (unit_direction.y() + 1.0);
		return (1.0f - t) * vec3(1.0f, 1.0f, 1.0f) + t * vec3(0.5f, 0.7f, 1.0f);
	}
	
}


int main()
{
	int nx = 600;
	int ny = 300;
	int ns = 64;

	ofstream fout("image.ppm");
	fout << "P3\n" << nx << " " << ny << "\n255\n";
	cout << "渲染中。。。。" << endl;

	hitable *list[2];
	list[0] = new sphere(vec3(0, 0, -1), 0.5);
	list[1] = new sphere(vec3(0, -100.5, -1), 100);
	hitable* world = new hitable_list(list, 2);

	camera cam;

	for (int j = ny - 1; j >= 0; j--)
	{
		for (int i = 0; i < nx; i++)
		{
			vec3 col(0, 0, 0);
			for (int s = 0; s < ns; s++)
			{
				float u = float(i) / float(nx);
				float v = float(j) / float(ny);
				ray r = cam.get_ray(u, v);
				vec3 p = r.point_at_parameter(2.0);
				col += color(r, world);
			}

			col /= float(ns);
			col = vec3(sqrt(col[0]), sqrt(col[1]), sqrt(col[2]));
			int ir = int(255.99f * col[0]);
			int ig = int(255.99f * col[1]);
			int ib = int(255.99f * col[2]);

			fout << ir << " " << ig << " " << ib << "\n";
		}
	}
	cout << "渲染完毕" << endl;
	fout.close();
	//system("Pause");
}

我的项目结构:
C++控制台写光线追踪渲染器_第2张图片

直接把代码拷贝进新工程就可以了,什么环境都不需要配置,默认空控制台就好。
光线相交部分的公式我简化了一下,这里解释一下这个数学原理

C++控制台写光线追踪渲染器_第3张图片

C++控制台写光线追踪渲染器_第4张图片

你可能感兴趣的:(Shader效果,光线追踪)