camer.h
#pragma once
#ifndef __CAMERA_H__
#define __CAMERA_H__
#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 lower_left_corner;
vec3 horizontal;
vec3 vertical;
vec3 origin;
};
#endif
dielectric.h
#pragma once
#include "hitable.h"
class dielectric :public material {
public:
dielectric(float ri) :ref_idx(ri) {}
virtual bool scatter(const ray&r_in, const hit_record&rec, vec3& attenuation, ray& scattered);
float ref_idx;
};
hitable.h
#pragma once
#ifndef __HITABLE__H__
#define __HITABLE__H__
#include "ray.h"
class material;
struct hit_record {
float t;
vec3 p;
vec3 normal;
material* mat_ptr;
};
class hitable {
public:
virtual bool hit(const ray& r, float t_min, float t_max, hit_record&rec)const = 0;
};
class material {
public:
virtual bool scatter(const ray &r_in, const hit_record& rec, vec3& attenuation, ray& scattered) = 0;
vec3 random_in_unit_sphere() {
vec3 p;
do {
p = 2.0 * vec3(rand() / (RAND_MAX + 1.0), rand() / (RAND_MAX + 1.0),
rand() / (RAND_MAX + 1.0)) - vec3(1, 1, 1);
} while (dot(p, p) >= 1.0);
return p;
}
};
class lambertian :public material {
public:
lambertian(const vec3&a) :albedo(a) {}
virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) {
vec3 target = rec.p + rec.normal + random_in_unit_sphere();
scattered = ray(rec.p, target - rec.p);
attenuation = albedo;
return true;
}
vec3 albedo;
};
class metal :public material {
public:
vec3 reflect(const vec3 &v, const vec3 &n) {
return v - 2 * dot(v, n)*n;
}
metal(const vec3& a, float f) : albedo(a) {
if (f < 1) fuzz = f;
else fuzz = 1;
}
metal(const vec3&a) :albedo(a) {}
virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) {
vec3 reflected = reflect(unitVector(r_in.direction()), rec.normal);
scattered = ray(rec.p, reflected + fuzz * random_in_unit_sphere());
attenuation = albedo;
return (dot(scattered.direction(), rec.normal) > 0);
}
vec3 albedo;
float fuzz;
};
#endif
hitablelist.h
#pragma once
#ifndef __HITABLELIST_H__
#define __HITABLELIST_H__
#include "hitable.h"
class hitable_list :public hitable {
public:
hitable_list() {};
hitable_list(hitable **l, int n) { list = l; list_size = n; }
virtual bool hit(const ray&r, float tmin, float tmax, hit_record& rec)const;
hitable **list;
int list_size;
};
#endif
Ray.h
#ifndef RAY_H
#define RAY_H
#include "Vec3.h"
class ray {
public:
ray() {}
ray(const vec3& a, const vec3& b)
{
data[0] = a; data[1] = b;
data[2] = vec3(1.0f / b.x(), 1.0f / b.y(), 1.0f / b.z());
posneg[0] = (data[1].x() > 0 ? 0 : 1);
posneg[1] = posneg[0] ^ 1;
posneg[2] = (data[1].y() > 0 ? 0 : 1);
posneg[3] = posneg[2] ^ 1;
posneg[4] = (data[1].z() > 0 ? 0 : 1);
posneg[5] = posneg[4] ^ 1;
}
ray(const ray& r) { *this = r; }
vec3 origin() const { return data[0]; }
vec3 direction() const { return data[1]; }
vec3 invDirection() const { return data[2]; }
void setOrigin(const vec3& v) { data[0] = v; }
void setDirection(const vec3& v)
{
data[1] = v;
data[2] = vec3(1.0f / v.x(), 1.0f / v.y(), 1.0f / v.z());
posneg[0] = (data[1].x() > 0 ? 0 : 1);
posneg[1] = posneg[0] ^ 1;
posneg[2] = (data[1].y() > 0 ? 0 : 1);
posneg[3] = posneg[2] ^ 1;
posneg[4] = (data[1].z() > 0 ? 0 : 1);
posneg[5] = posneg[4] ^ 1;
}
vec3 pointAtParameter(float t) const { return data[0] + t * data[1]; }
vec3 data[3];
int posneg[6];
};
#endif
sphere.h
#pragma once
#ifndef __SPHEREH__
#define __SPHEREH__
#include "hitable.h"
class sphere :public hitable {
public:
sphere() {}
sphere(vec3 cen, float r, material* thisMaterial) :center(cen), radius(r), m_material(thisMaterial) {};
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;
vec3 center;
float radius;
material* m_material;
};
#endif
vec3.h
#ifndef VECTOR3_H
#define VECTOR3_H
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <iostream>
#include <iomanip>
class vec3 {
public:
vec3() { e[0] = 0; e[1] = 0; e[2] = 0; }
vec3(float e0, float e1, float e2) { e[0] = e0; e[1] = e1; e[2] = e2; }
float x() const { return e[0]; }
float y() const { return e[1]; }
float z() const { return e[2]; }
void setX(float a) { e[0] = a; }
void setY(float a) { e[1] = a; }
void setZ(float a) { e[2] = a; }
inline vec3(const vec3 &v) {
e[0] = v.e[0]; e[1] = v.e[1]; e[2] = v.e[2];
}
const vec3& operator+() const { return *this; }
vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }
float& operator[](int i) { return e[i]; }
float operator[](int i) const { return e[i]; }
vec3& operator+=(const vec3& v2);
vec3& operator-=(const vec3& v2);
vec3& operator*=(const vec3& v2);
vec3& operator*=(const float t);
vec3& operator/=(const float t);
float length() const { return sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]); }
float squaredLength() const { return e[0] * e[0] + e[1] * e[1] + e[2] * e[2]; }
void makeUnitVector();
float minComponent() const { return e[indexOfMinComponent()]; }
float maxComponent() const { return e[indexOfMaxComponent()]; }
float maxAbsComponent() const { return e[indexOfMaxAbsComponent()]; }
float minAbsComponent() const { return e[indexOfMinAbsComponent()]; }
int indexOfMinComponent() const {
return (e[0] < e[1] && e[0] < e[2]) ? 0 : (e[1] < e[2] ? 1 : 2);
}
int indexOfMinAbsComponent() const {
if (fabs(e[0]) < fabs(e[1]) && fabs(e[0]) < fabs(e[2]))
return 0;
else if (fabs(e[1]) < fabs(e[2]))
return 1;
else
return 2;
}
int indexOfMaxComponent() const {
return (e[0] > e[1] && e[0] > e[2]) ? 0 : (e[1] > e[2] ? 1 : 2);
}
int indexOfMaxAbsComponent() const {
if (fabs(e[0]) > fabs(e[1]) && fabs(e[0]) > fabs(e[2]))
return 0;
else if (fabs(e[1]) > fabs(e[2]))
return 1;
else
return 2;
}
float e[3];
};
inline bool operator==(const vec3 &t1, const vec3 &t2) {
return ((t1[0] == t2[0]) && (t1[1] == t2[1]) && (t1[2] == t2[2]));
}
inline bool operator!=(const vec3 &t1, const vec3 &t2) {
return ((t1[0] != t2[0]) || (t1[1] != t2[1]) || (t1[2] != t2[2]));
}
inline vec3 unitVector(const vec3& v) {
float k = 1.0f / sqrt(v.e[0] * v.e[0] + v.e[1] * v.e[1] + v.e[2] * v.e[2]);
return vec3(v.e[0] * k, v.e[1] * k, v.e[2] * k);
}
inline void vec3::makeUnitVector() {
float k = 1.0f / 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*(float t, const vec3 &v) {
return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
}
inline vec3 operator*(const vec3 &v, float t) {
return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
}
inline vec3 operator/(const vec3 &v, float t) {
return vec3(v.e[0] / t, v.e[1] / t, v.e[2] / t);
}
inline vec3 operator*(const vec3& v, const vec3& v2) {
return vec3(v.e[0] * v2.e[0], v.e[1] * v2.e[1], v.e[2] * v2.e[2]);
}
inline float 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[2] * v2.e[0] - v1.e[0] * v2.e[2]),
(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 float t) {
e[0] *= t;
e[1] *= t;
e[2] *= t;
return *this;
}
inline vec3& vec3::operator/=(const float t) {
e[0] /= t;
e[1] /= t;
e[2] /= t;
return *this;
}
inline
vec3 reflect(const vec3& in, const vec3& normal)
{
return in - normal * (2 * dot(in, normal));
}
#endif
dielectric.cpp
#include "dielectric.h"
inline bool refract(const vec3&v, const vec3 &n, float ni_over_nt, vec3& refracted) {
vec3 uv = unitVector(v);
float dt = dot(uv, n);
float discriminant = 1.0 - ni_over_nt * ni_over_nt*(1 - dt * dt);
if (discriminant > 0) {
refracted = ni_over_nt * (uv - n * dt) - n * sqrt(discriminant);
return true;
}
else
return false;
}
bool dielectric::scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) {
vec3 outward_normal;
vec3 reflected = reflect(r_in.direction(), rec.normal);
float ni_over_nt;
attenuation = vec3(1.0, 1.0, 1.0);
vec3 refracted;
if (dot(r_in.direction(), rec.normal) > 0) {
outward_normal = -rec.normal;
ni_over_nt = ref_idx;
}
else {
outward_normal = rec.normal;
ni_over_nt = 1.0 / ref_idx;
}
if (refract(r_in.direction(), outward_normal, ni_over_nt, refracted)) {
scattered = ray(rec.p, refracted);
}
else {
scattered = ray(rec.p, reflected);
return false;
}
return true;
}
hitablelist.cpp
#include "hitablelist.h"
bool hitable_list::hit(const ray&r, float tmin, float tmax, hit_record& rec)const {
hit_record temp_rec;
bool hit_anything = false;
double closest_so_far = tmax;
for (int i = 0; i < list_size; i++) {
if (list[i]->hit(r, tmin, closest_so_far, temp_rec)) {
hit_anything = true;
closest_so_far = temp_rec.t;
rec = temp_rec;
}
}
return hit_anything;
}
main.cpp
#include <stdlib.h>
#ifdef _WIN64
#define GLUT_NO_LIB_PRAGMA
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glut64.lib")
#endif
#ifdef _WIN32
#include "glut.h"
#include "glext.h"
#define GET_PROC_ADDRESS( str ) wglGetProcAddress( str )
#endif
#include "vec3.h"
#include "ray.h"
#define WIDTH 400
#define HEIGHT 200
unsigned char *Pixels;
#include "hitable.h"
#include "Sphere.h"
#include "hitablelist.h"
#define MAXFLOAT 20.0
#include "camera.h"
#include "time.h"
#include "dielectric.h"
vec3 color(const ray&r, hitable *world, int depth) {
hit_record rec;
if (world->hit(r, 0.001, MAXFLOAT, rec)) {
ray scattered;
vec3 attenuation;
if (depth < 50 && rec.mat_ptr->scatter(r, rec, attenuation, scattered)) {
return attenuation * color(scattered, world, depth + 1);
}
else {
return vec3(0, 0, 0);
}
}
else {
vec3 unit_direction = unitVector(r.direction());
float t = 0.5*(unit_direction.y() + 1.0);
return (1.0 - t)*vec3(1.0, 1.0, 1.0) + t * vec3(0.5, 0.7, 1.0);
}
}
void Draw(void) {
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glDrawPixels(WIDTH, HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, Pixels);
glFlush();
}
int main() {
srand(time(NULL));
Pixels = (unsigned char*)malloc(WIDTH*HEIGHT * sizeof(unsigned char) * 4);
hitable *list[4];
list[0] = new sphere(vec3(0, 0, -1.0), 0.5, new lambertian(vec3(0.8, 0.3, 0.3)));
list[1] = new sphere(vec3(0.0, -100.5, -1.0), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
list[2] = new sphere(vec3(1.0, 0.0, -1.0), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.3));
list[3] = new sphere(vec3(-1.0, 0.0, -1.0), 0.5, new dielectric(1.5));
hitable *world = new hitable_list(list, 4);
camera cam;
int ns = 100;
for (int j = HEIGHT - 1; j >= 0; j--) {
for (int i = 0; i < WIDTH; i++) {
vec3 col(0, 0, 0);
for (int s = 0; s < ns; s++) {
float u = float(i + rand() / (RAND_MAX + 1.0)) / float(WIDTH);
float v = float(j + rand() / (RAND_MAX + 1.0)) / float(HEIGHT);
ray r = cam.get_ray(u, v);
vec3 p = r.pointAtParameter(2.0);
col += color(r, world, 0);
}
col /= float(ns);
col = vec3(sqrt(col[0]), sqrt(col[1]), sqrt(col[2]));
int offset = (WIDTH * j + i) * 4;
Pixels[offset + 0] = (unsigned char)255.99*col[0];
Pixels[offset + 1] = (unsigned char)255.99*col[1];
Pixels[offset + 2] = (unsigned char)255.99*col[2];
Pixels[offset + 3] = 255;
}
}
int argc = 0; char *argv = const_cast<char*>("");
glutInit(&argc, &argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("bitmap");
glutDisplayFunc(Draw);
glutMainLoop();
}
sphere.cpp
#include "sphere.h"
bool sphere::hit(const ray&r, float t_min, float t_max, hit_record&rec)const {
vec3 oc = r.origin() - center;
float a = dot(r.direction(), r.direction());
float b = 2.0 * dot(oc, r.direction());
float c = dot(oc, oc) - radius * radius;
float discriminant = b * b - 4 * a * c;
if (discriminant > 0) {
float temp = (-b - sqrt(discriminant)) / (2.0*a);
if (temp < t_max && temp > t_min) {
rec.t = temp;
rec.p = r.pointAtParameter(rec.t);
rec.normal = (rec.p - center) / radius;
rec.mat_ptr = m_material;
return true;
}
temp = (-b + sqrt(discriminant)) / (2.0*a);
if (temp < t_max && temp > t_min) {
rec.t = temp;
rec.p = r.pointAtParameter(rec.t);
rec.normal = (rec.p - center) / radius;
rec.mat_ptr = m_material;
return true;
}
}
return false;
}