From 05ffa6048e723cd68986cff4524738ea0b7cf4b4 Mon Sep 17 00:00:00 2001 From: zovjsra <58198508+Zovjsra@users.noreply.github.com> Date: Mon, 27 Mar 2023 17:12:01 +0800 Subject: [PATCH] First commit --- .gitignore | 2 + CMakeLists.txt | 8 ++ COPYING.txt | 121 ++++++++++++++++++++++++++++++ geo.h | 101 +++++++++++++++++++++++++ main.cpp | 195 +++++++++++++++++++++++++++++++++++++++++++++++++ ray.h | 22 ++++++ vec3.h | 98 +++++++++++++++++++++++++ 7 files changed, 547 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 COPYING.txt create mode 100644 geo.h create mode 100644 main.cpp create mode 100644 ray.h create mode 100644 vec3.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f31401 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ed35df5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 2.8) + +project(ray) + +set(CMAKE_CXX_FLAGS "-g -Wall") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +add_executable(ray main.cpp ) \ No newline at end of file diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000..354f1e0 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/geo.h b/geo.h new file mode 100644 index 0000000..17ebd2a --- /dev/null +++ b/geo.h @@ -0,0 +1,101 @@ +#ifndef GEOH +#define GEOH +#include + +#include "ray.h" +using namespace std; +struct hit_record { + float t; + vec3 p; + vec3 nv; + vec3 kd; + float wr; + float wt; +}; + +class hitable { //geometry parent class + public: + virtual bool hit(const ray &r, float tmin, float tmax, hit_record *rec) const = 0; +}; + +class sphere : public hitable { + public: + sphere() {} + sphere(vec3 c, float r, vec3 _kd = vec3(1.0, 1.0, 1.0), float w_ri = 0.0f, float w_ti = 0.0f) : center(c), radius(r), kd(_kd), w_r(w_ri), w_t(w_ti){}; + bool hit(const ray &r, float tmin, float tmax, hit_record *rec) const; + vec3 center; + float radius; + vec3 kd; + float w_r; //reflected + float w_t; //transmitted +}; + +bool sphere::hit(const ray &r, float tmin, float tmax, hit_record *rec) const { + float a, b, c, t; + a = dot(r.D, r.D); + b = 2 * (dot(r.D, (r.O - this->center))); + c = dot(r.O - this->center, r.O - this->center) - this->radius * this->radius; + if (((b * b) - (4 * a * c)) > 0) { + if ((((-b) - sqrt((b * b) - (4 * a * c))) / (2 * a)) > tmin && (((-b) + sqrt((b * b) - (4 * a * c))) / (2 * a)) > tmin) { + t = min((((-b) - sqrt((b * b) - (4 * a * c))) / (2 * a)), (((-b) + sqrt((b * b) - (4 * a * c))) / (2 * a))); + } else if ((((-b) - sqrt((b * b) - (4 * a * c))) / (2 * a)) > tmin) { + t = (((-b) - sqrt((b * b) - (4 * a * c))) / (2 * a)); + } else if ((((-b) + sqrt((b * b) - (4 * a * c))) / (2 * a)) > tmin) { + t = (((-b) + sqrt((b * b) - (4 * a * c))) / (2 * a)); + } else { + return false; + } + } else { + return false; + } + if (t > tmax || t < tmin) { + return false; + } + if (rec->t > t) { + rec->t = t; + rec->p = r.point_at_parameter(t); + rec->nv = unit_vector(r.point_at_parameter(t) - this->center); + rec->kd = this->kd; + rec->wr = this->w_r; + rec->wt = this->w_t; + //cout << t << " " << rec->p << " " << rec->nv << endl; + } + return true; +} + +vec3 reflect(const vec3 &d, const vec3 &nv) { + return d - (2 * dot(d, nv) * nv); + + return vec3(0, 0, 0); +} + +template +float clamp(T bot, T top, T targ) { + if (targ > top) { + return top; + } + if (targ < bot) { + return bot; + } + return targ; +} + +vec3 refract(const vec3 &i, const vec3 &n, float eta) { + vec3 I = unit_vector(i); + vec3 N = unit_vector(n); + //cout << I << " " << N << " " << dot(N, I) << endl; + eta = 1 / eta; + if (dot(N, I) > 0) { + N = -1 * N; + eta = 1 / eta; + } + float k = 1.0f - eta * eta * (1.0 - dot(N, I) * dot(N, I)); + //cout << k << endl; + if (k < 0.0) { + return i - (2 * dot(i, n) * n); + } + + //cout << eta * I - (eta * dot(N, I) + sqrt(k)) * N << endl; + return eta * I - (eta * dot(N, I) + sqrt(k)) * N; +} +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..e75b09a --- /dev/null +++ b/main.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "geo.h" +#include "ray.h" +#include "vec3.h" + +float MAX(float a, float b) { return (a > b) ? a : b; } +using namespace std; +int max_step = 5; + +vec3 shading(vec3 &lightsource, vec3 &intensity, hit_record ht, vec3 kd, const vector &list) { + /* + define L, N by yourself + */ + vec3 L = lightsource - ht.p; + vec3 N = ht.nv; + ray shadowRay(ht.p, L); + + int intersect = -1; + hit_record rec; + float closest = FLT_MAX; + /* + To-do: + To find whether the shadowRay hit other object, + you should run the function "hit" of all the hitable you created + */ + for (int i = 0; i < list.size(); i++) { + if (list[i].hit(shadowRay, 0.001, 10000, &rec)) { + intersect += 1; + } + } + + if (intersect == -1) { + return kd * intensity * MAX(0, dot(N, unit_vector(L))); + } else { + return vec3(0, 0, 0); + } +} + +vec3 skybox(const ray &r) { + vec3 uni_direction = unit_vector(r.direction()); + float t = 0.5 * (uni_direction.y() + 1); + return (1.0 - t) * vec3(1, 1, 1) + t * vec3(0.5, 0.7, 1.0); +} + +vec3 trace(const ray &r, const vector &list, int depth) { + if (r.direction() == vec3(0, 0, 0)) { + return vec3(0, 0, 0); + } + //cout << r.direction() << endl; + if (depth >= max_step) + return skybox(r); //or return vec3(0,0,0); + + int intersect = -1; + vec3 lightsource = vec3(-10, 10, 0); + vec3 intensity = vec3(1, 1, 1); + hit_record rec; + rec.t = 10000; + rec.p = vec3(0, 0, 0); + rec.nv = vec3(0, 0, 0); + float closest = FLT_MAX; + /* + To-do: + To find the nearest object from the origin of the ray, + you should run the function "hit" of all the hitable you created + */ + //cout << "Hit" << endl; + for (int i = 0; i < list.size(); i++) { + if (list[i].hit(r, 0.0001, 1000, &rec)) { + //cout << intersect << endl; + intersect += 1; + } + } + + if (intersect != -1) { + /* + To-do: + 1.compute the local color by shading function + 2.compute the relected color by + 2.1 compute the reflected direction + 2.2 define a reflected ray by rec.p and the direction in 2.1 + 2.3 run trace(reflected_ray,list,depth+1); + 3.compute the transmitted color by + 3.1 compute the transmitted direction by Snell's law + 3.2 define a transmitted ray by rec.p and the direction in 3.1 + 3.3 run trace( transmitted_ray, list, depth+1 ); + 4.return the color by the parameter w_r, w_t and the 3 color you computed. + */ + + //1. + vec3 L = unit_vector(lightsource - rec.p); + vec3 colour = (dot(rec.nv, L) >= 0 ? dot(rec.nv, L) : 0) * rec.kd; + + //cout << rec.kd << " " << rec.wr << endl; + + //return colour; + + vec3 shadow = shading(lightsource, intensity, rec, rec.kd, list); + colour = 0.2 * colour + 0.8 * shadow; + + if (depth >= 5) { + //return 0.5 * vec3(rec.nv.x() + 1, rec.nv.y() + 1, rec.nv.z() + 1); + return colour; + } + if (rec.wr <= 0 && rec.wt <= 0) { + return colour; + } + { + vec3 reflection = vec3(0, 0, 0); + if ((dot(r.direction(), rec.nv) / r.direction().length() * rec.nv.length()) < 0) { + if (rec.wr > 0) { + //cout << "reflection " << depth << endl; + reflection = trace(ray(rec.p, reflect(r.direction(), rec.nv)), list, (depth + 1)); + } + if (rec.wt <= 0) { + //cout << "reflection " << depth << endl; + colour = reflection * rec.wr + colour * max(0.0f, (1 - rec.wr)); + return colour; + } + //cout << "refraction " << depth << endl; + vec3 refraction = trace(ray(rec.p, refract(r.direction(), rec.nv, 1.5)), list, (depth + 1)); + colour = reflection * rec.wr + refraction * rec.wt + colour * max(0.0f, (1 - rec.wr - rec.wt)); + } else { + //cout << "refraction " << depth << endl; + vec3 refraction = trace(ray(rec.p, refract(r.direction(), rec.nv, 1.5)), list, (depth + 1)); + //cout << "refraction out" << endl; + colour = refraction; + } + + //cout << L << endl; + //cout << colour << endl; + //return vec3(0, 0, 0); + return colour; + } + } else { + return skybox(r); + } +} + +int main() { + int width = 3840; + int height = 1920; + srand(time(NULL)); + + //camera and projection plane + vec3 lower_left_corner(-2, -1, -1); + vec3 origin(0, 0, 0); + vec3 horizontal(4, 0, 0); + vec3 vertical(0, 2, 0); + + vec3 colorlist[8] = {vec3(0.8, 0.3, 0.3), vec3(0.3, 0.8, 0.3), vec3(0.3, 0.3, 0.8), + vec3(0.8, 0.8, 0.3), vec3(0.3, 0.8, 0.8), vec3(0.8, 0.3, 0.8), + vec3(0.8, 0.8, 0.8), vec3(0.3, 0.3, 0.3)}; + + //test scene with spheres + vector hitable_list; + hitable_list.push_back(sphere(vec3(0, -100.5, -2), 100, vec3(1.0f, 1.0f, 1.0f), 0.0f, 0.0f)); //ground + hitable_list.push_back(sphere(vec3(0, 0, -2), 0.5, vec3(1.0f, 1.0f, 1.0f), 0.0f, 0.9f)); + hitable_list.push_back(sphere(vec3(1, 0, -1.75), 0.5, vec3(1.0f, 1.0f, 1.0f), 0.5f, 0.0f)); + hitable_list.push_back(sphere(vec3(-1, 0, -2.25), 0.5, vec3(1.0f, 0.7f, 0.3f), 0.0f, 0.0f)); + for (int i = 0; i < 48; i++) { + float xr = ((float)rand() / (float)(RAND_MAX)) * 6.0f - 3.0f; + float zr = ((float)rand() / (float)(RAND_MAX)) * 3.0f - 1.5f; + int cindex = rand() % 8; + float rand_reflec = ((float)rand() / (float)(RAND_MAX)); + //float rand_refrac = ((float)rand() / (float)(RAND_MAX)); + hitable_list.push_back(sphere(vec3(xr, -0.4, zr - 2), 0.1, colorlist[cindex], rand_reflec, 0.0f)); + } + + fstream file; + file.open("ray.ppm", ios::out); + file << "P3\n" + << width << " " << height << "\n255\n"; + for (int j = height - 1; j >= 0; j--) { + cout << j << endl; + for (int i = 0; i < width; i++) { + //cout << j << " " << i << endl; + float u = float(i) / float(width); + float v = float(j) / float(height); + ray r(origin, lower_left_corner + u * horizontal + v * vertical); + vec3 c = trace(r, hitable_list, 0); + + file << min(255, (int)(c.r() * 255)) << " " + << min(255, (int)(c.g() * 255)) << " " + << min(255, (int)(c.b() * 255)) << endl; + } + } + return 0; +} \ No newline at end of file diff --git a/ray.h b/ray.h new file mode 100644 index 0000000..a9c23eb --- /dev/null +++ b/ray.h @@ -0,0 +1,22 @@ +#ifndef RAYH +#define RAYH +#include "vec3.h" + +class ray { + public: + ray() {} + ray(const vec3& a, const vec3& b) { + O = a; + D = b; + } + vec3 origin() const { return O; } + vec3 direction() const { return D; } + inline vec3 point_at_parameter(float t) const { + return vec3(D * t + O); + } + + vec3 O; //center(origin) point + vec3 D; //direction vector +}; + +#endif diff --git a/vec3.h b/vec3.h new file mode 100644 index 0000000..973c58a --- /dev/null +++ b/vec3.h @@ -0,0 +1,98 @@ +//================================================================================================= +// Written in 2016 by Peter Shirley +// +// To the extent possible under law, the author(s) have dedicated all copyright and related and +// neighboring rights to this software to the public domain worldwide. This software is distributed +// without any warranty. +// +// You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along +// with this software. If not, see . +//================================================================================================== + +#ifndef VEC3H +#define VEC3H + +#include + +#include +#include +#include + +class vec3 { + public: + vec3() {} + vec3(float e0, float e1, float e2) { + e[0] = e0; + e[1] = e1; + e[2] = e2; + } + vec3(const vec3 &v2){ + this->e[0] = v2.e[0]; + this->e[1] = v2.e[1]; + this->e[2] = v2.e[2]; + } + float x() const { return e[0]; } + float y() const { return e[1]; } + float z() const { return e[2]; } + float r() const { return e[0]; } + float g() const { return e[1]; } + float b() const { return e[2]; } + + inline vec3 operator+(const vec3 &v2) const { return vec3(x() + v2.x(), y() + v2.y(), z() + v2.z()); } + inline void operator+=(const vec3 &v2) { e[0] = x() + v2.x(), e[1] = y() + v2.y(), e[2] = z() + v2.z(); } + inline vec3 operator-(const vec3 &v2) const { return vec3(x() - v2.x(), y() - v2.y(), z() - v2.z()); } + inline void operator-=(const vec3 &v2) { e[0] = x() - v2.x(), e[1] = y() - v2.y(), e[2] = z() - v2.z(); } + + inline vec3 operator*(const float t) const { return vec3(x() * t, y() * t, z() * t); } + inline void operator*=(const float t) { e[0] = x() * t, e[1] = y() * t, e[2] = z() * t; } + inline vec3 operator/(const float t) const { return vec3(x() / t, y() / t, z() / t); } + inline void operator/=(const float t) { e[0] = x() / t, e[1] = y() / t, e[2] = z() / t; } + + inline bool operator==(const vec3 &v2) const { + if(this->x() == v2.x() && this->y() == v2.y() && this->z() == v2.z()) return true; + return false; + } + + inline float length() const { return sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]); } + inline float squared_length() const { return e[0] * e[0] + e[1] * e[1] + e[2] * e[2]; } + inline void make_unit_vector() { + float len = length(); + e[0] = e[0] / len; + e[1] = e[1] / len; + e[2] = e[2] / len; + } + + float e[3]; +}; + +inline vec3 operator*(const float t, const vec3 &v) { + return vec3(v.x() * t, v.y() * t, v.z() * t); +} + +/* +for color calculation +*/ +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 std::ostream &operator<<(std::ostream &os, const vec3 &t) { + os << "( " << t.x() << " , " << t.y() << " , " << t.z() << " )"; + return os; +} + +inline float dot(const vec3 &v1, const vec3 &v2) { + return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z(); +} + +inline vec3 cross(const vec3 &v1, const vec3 &v2) { + return vec3(v1.y() * v2.z() - v1.z() * v2.y(), + v1.x() * v2.z() - v1.z() * v2.x(), + v1.x() * v2.y() - v1.y() * v2.x()); +} + +inline vec3 unit_vector(vec3 v) { + return v / v.length(); +} + +#endif \ No newline at end of file