Raytracing demonstration for Pokitto
Dependencies: PokittoLib
main.cpp
00001 // [header] 00002 // A very basic raytracer example. 00003 // [/header] 00004 // [compile] 00005 // c++ -o raytracer -O3 -Wall raytracer.cpp 00006 // [/compile] 00007 // [ignore] 00008 // Copyright (C) 2012 www.scratchapixel.com 00009 // 00010 // This program is free software: you can redistribute it and/or modify 00011 // it under the terms of the GNU General Public License as published by 00012 // the Free Software Foundation, either version 3 of the License, or 00013 // (at your option) any later version. 00014 // 00015 // This program is distributed in the hope that it will be useful, 00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 // GNU General Public License for more details. 00019 // 00020 // You should have received a copy of the GNU General Public License 00021 // along with this program. If not, see <http://www.gnu.org/licenses/>. 00022 // [/ignore] 00023 #include "Pokitto.h" 00024 #include <cstdlib> 00025 #include <cstdio> 00026 #include <cmath> 00027 //#include <fstream> 00028 #include <vector> 00029 //#include <iostream> 00030 //#include <cassert> 00031 00032 Pokitto::Core g; 00033 00034 #if defined __linux__ || defined __APPLE__ 00035 // "Compiled for Linux 00036 #else 00037 // Windows doesn't define these values by default, Linux does 00038 #define M_PI 3.141592653589793 00039 #define INFINITY 1e8 00040 #endif 00041 00042 template<typename T> 00043 class Vec3 00044 { 00045 public: 00046 T x, y, z; 00047 Vec3() : x(T(0)), y(T(0)), z(T(0)) {} 00048 Vec3(T xx) : x(xx), y(xx), z(xx) {} 00049 Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {} 00050 Vec3& normalize() 00051 { 00052 T nor2 = length2(); 00053 if (nor2 > 0) { 00054 T invNor = 1 / sqrt(nor2); 00055 x *= invNor, y *= invNor, z *= invNor; 00056 } 00057 return *this; 00058 } 00059 Vec3<T> operator * (const T &f) const { return Vec3<T>(x * f, y * f, z * f); } 00060 Vec3<T> operator * (const Vec3<T> &v) const { return Vec3<T>(x * v.x, y * v.y, z * v.z); } 00061 T dot(const Vec3<T> &v) const { return x * v.x + y * v.y + z * v.z; } 00062 Vec3<T> operator - (const Vec3<T> &v) const { return Vec3<T>(x - v.x, y - v.y, z - v.z); } 00063 Vec3<T> operator + (const Vec3<T> &v) const { return Vec3<T>(x + v.x, y + v.y, z + v.z); } 00064 Vec3<T>& operator += (const Vec3<T> &v) { x += v.x, y += v.y, z += v.z; return *this; } 00065 Vec3<T>& operator *= (const Vec3<T> &v) { x *= v.x, y *= v.y, z *= v.z; return *this; } 00066 Vec3<T> operator - () const { return Vec3<T>(-x, -y, -z); } 00067 T length2() const { return x * x + y * y + z * z; } 00068 T length() const { return sqrt(length2()); } 00069 00070 }; 00071 00072 typedef Vec3<float> Vec3f; 00073 00074 class Sphere 00075 { 00076 public: 00077 Vec3f center; /// position of the sphere 00078 float radius, radius2; /// sphere radius and radius^2 00079 Vec3f surfaceColor, emissionColor; /// surface color and emission (light) 00080 float transparency, reflection; /// surface transparency and reflectivity 00081 Sphere( 00082 const Vec3f &c, 00083 const float &r, 00084 const Vec3f &sc, 00085 const float &refl = 0, 00086 const float &transp = 0, 00087 const Vec3f &ec = 0) : 00088 center(c), radius(r), radius2(r * r), surfaceColor(sc), emissionColor(ec), 00089 transparency(transp), reflection(refl) 00090 { /* empty */ } 00091 //[comment] 00092 // Compute a ray-sphere intersection using the geometric solution 00093 //[/comment] 00094 bool intersect(const Vec3f &rayorig, const Vec3f &raydir, float &t0, float &t1) const 00095 { 00096 Vec3f l = center - rayorig; 00097 float tca = l.dot(raydir); 00098 if (tca < 0) return false; 00099 float d2 = l.dot(l) - tca * tca; 00100 if (d2 > radius2) return false; 00101 float thc = sqrt(radius2 - d2); 00102 t0 = tca - thc; 00103 t1 = tca + thc; 00104 00105 return true; 00106 } 00107 }; 00108 00109 //[comment] 00110 // This variable controls the maximum recursion depth 00111 //[/comment] 00112 #define MAX_RAY_DEPTH 5 00113 00114 float mix(const float &a, const float &b, const float &mix) 00115 { 00116 return b * mix + a * (1 - mix); 00117 } 00118 00119 //[comment] 00120 // This is the main trace function. It takes a ray as argument (defined by its origin 00121 // and direction). We test if this ray intersects any of the geometry in the scene. 00122 // If the ray intersects an object, we compute the intersection point, the normal 00123 // at the intersection point, and shade this point using this information. 00124 // Shading depends on the surface property (is it transparent, reflective, diffuse). 00125 // The function returns a color for the ray. If the ray intersects an object that 00126 // is the color of the object at the intersection point, otherwise it returns 00127 // the background color. 00128 //[/comment] 00129 Vec3f trace( 00130 const Vec3f &rayorig, 00131 const Vec3f &raydir, 00132 const std::vector<Sphere> &spheres, 00133 const int &depth) 00134 { 00135 //if (raydir.length() != 1) std::cerr << "Error " << raydir << std::endl; 00136 float tnear = INFINITY; 00137 const Sphere* sphere = NULL; 00138 // find intersection of this ray with the sphere in the scene 00139 for (unsigned i = 0; i < spheres.size(); ++i) { 00140 float t0 = INFINITY, t1 = INFINITY; 00141 if (spheres[i].intersect(rayorig, raydir, t0, t1)) { 00142 if (t0 < 0) t0 = t1; 00143 if (t0 < tnear) { 00144 tnear = t0; 00145 sphere = &spheres[i]; 00146 } 00147 } 00148 } 00149 // if there's no intersection return black or background color 00150 if (!sphere) return Vec3f(2); 00151 Vec3f surfaceColor = 0; // color of the ray/surfaceof the object intersected by the ray 00152 Vec3f phit = rayorig + raydir * tnear; // point of intersection 00153 Vec3f nhit = phit - sphere->center; // normal at the intersection point 00154 nhit.normalize(); // normalize normal direction 00155 // If the normal and the view direction are not opposite to each other 00156 // reverse the normal direction. That also means we are inside the sphere so set 00157 // the inside bool to true. Finally reverse the sign of IdotN which we want 00158 // positive. 00159 float bias = 1e-4; // add some bias to the point from which we will be tracing 00160 bool inside = false; 00161 if (raydir.dot(nhit) > 0) nhit = -nhit, inside = true; 00162 if ((sphere->transparency > 0 || sphere->reflection > 0) && depth < MAX_RAY_DEPTH) { 00163 float facingratio = -raydir.dot(nhit); 00164 // change the mix value to tweak the effect 00165 float fresneleffect = mix(pow(1 - facingratio, 3), 1, 0.1); 00166 // compute reflection direction (not need to normalize because all vectors 00167 // are already normalized) 00168 Vec3f refldir = raydir - nhit * 2 * raydir.dot(nhit); 00169 refldir.normalize(); 00170 Vec3f reflection = trace(phit + nhit * bias, refldir, spheres, depth + 1); 00171 Vec3f refraction = 0; 00172 // if the sphere is also transparent compute refraction ray (transmission) 00173 if (sphere->transparency) { 00174 float ior = 1.1, eta = (inside) ? ior : 1 / ior; // are we inside or outside the surface? 00175 float cosi = -nhit.dot(raydir); 00176 float k = 1 - eta * eta * (1 - cosi * cosi); 00177 Vec3f refrdir = raydir * eta + nhit * (eta * cosi - sqrt(k)); 00178 refrdir.normalize(); 00179 refraction = trace(phit - nhit * bias, refrdir, spheres, depth + 1); 00180 } 00181 // the result is a mix of reflection and refraction (if the sphere is transparent) 00182 surfaceColor = ( 00183 reflection * fresneleffect + 00184 refraction * (1 - fresneleffect) * sphere->transparency) * sphere->surfaceColor; 00185 } 00186 else { 00187 // it's a diffuse object, no need to raytrace any further 00188 for (unsigned i = 0; i < spheres.size(); ++i) { 00189 if (spheres[i].emissionColor.x > 0) { 00190 // this is a light 00191 Vec3f transmission = 1; 00192 Vec3f lightDirection = spheres[i].center - phit; 00193 lightDirection.normalize(); 00194 for (unsigned j = 0; j < spheres.size(); ++j) { 00195 if (i != j) { 00196 float t0, t1; 00197 if (spheres[j].intersect(phit + nhit * bias, lightDirection, t0, t1)) { 00198 transmission = 0; 00199 break; 00200 } 00201 } 00202 } 00203 surfaceColor += sphere->surfaceColor * transmission * 00204 std::max(float(0), nhit.dot(lightDirection)) * spheres[i].emissionColor; 00205 } 00206 } 00207 } 00208 00209 return surfaceColor + sphere->emissionColor; 00210 } 00211 00212 //[comment] 00213 // Main rendering function. We compute a camera ray for each pixel of the image 00214 // trace it and return a color. If the ray hits a sphere, we return the color of the 00215 // sphere at the intersection point, else we return the background color. 00216 //[/comment] 00217 void render(const std::vector<Sphere> &spheres, int rscale) 00218 { 00219 unsigned width = 220, height = 176; 00220 //Vec3f *image = new Vec3f[width * height], *pixel = image; 00221 Vec3f pixel; //jonne 00222 float invWidth = 1 / float(width), invHeight = 1 / float(height); 00223 float fov = 30, aspectratio = width / float(height); 00224 float angle = tan(M_PI * 0.5 * fov / 180.); 00225 // Trace rays 00226 for (unsigned y = 1; y < height+1; y+=rscale) { 00227 for (unsigned x = 1; x < width+1; x+=rscale) { //, ++pixel) { 00228 float xx = (2 * ((x + 0.5*rscale) * invWidth) - 1) * angle * aspectratio; 00229 float yy = (1 - 2 * ((y + 0.5*rscale) * invHeight)) * angle; 00230 Vec3f raydir(xx, yy, -1); 00231 raydir.normalize(); 00232 //*pixel = trace(Vec3f(0), raydir, spheres, 0); 00233 pixel = trace(Vec3f(0), raydir, spheres, 0); 00234 //g.display.directPixel(x,y,g.display.RGBto565(pixel.x*255,pixel.y*255,pixel.z*255)); 00235 g.display.directRectangle(x,y,x+rscale,y+rscale,g.display.RGBto565(pixel.x*255,pixel.y*255,pixel.z*255)); 00236 //g.wait(100); 00237 } 00238 } 00239 // Save result to a PPM image (keep these flags if you compile under Windows) 00240 /*std::ofstream ofs("./untitled.ppm", std::ios::out | std::ios::binary); 00241 ofs << "P6\n" << width << " " << height << "\n255\n"; 00242 for (unsigned i = 0; i < width * height; ++i) { 00243 ofs << (unsigned char)(std::min(float(1), image[i].x) * 255) << 00244 (unsigned char)(std::min(float(1), image[i].y) * 255) << 00245 (unsigned char)(std::min(float(1), image[i].z) * 255); 00246 } 00247 ofs.close();*/ 00248 //delete [] image; 00249 } 00250 00251 //[comment] 00252 // In the main function, we will create the scene which is composed of 5 spheres 00253 // and 1 light (which is also a sphere). Then, once the scene description is complete 00254 // we render that scene, by calling the render() function. 00255 //[/comment] 00256 int main() 00257 { 00258 g.begin(); 00259 //srand48(13); 00260 std::vector<Sphere> spheres; 00261 // position, radius, surface color, reflectivity, transparency, emission color 00262 spheres.push_back(Sphere(Vec3f( 0.0, -10004, -20), 10000, Vec3f(0.20, 0.20, 0.20), 0, 0.0)); 00263 spheres.push_back(Sphere(Vec3f( 0.0, 0, -20), 4, Vec3f(0.32, 1.00, 0.16), 1, 0.5)); //green middle sphere 00264 spheres.push_back(Sphere(Vec3f( 5.0, -1, -15), 2, Vec3f(1.00, 0.16, 1.00), 1, 0.5)); //magenta 00265 spheres.push_back(Sphere(Vec3f( 5.0, 0, -25), 3, Vec3f(0.36, 0.16, 0.97), 1, 0.5)); //blue 00266 spheres.push_back(Sphere(Vec3f(-5.5, 0, -15), 3, Vec3f(1.00, 0.65, 0.30), 1, 0.5)); //orange 00267 // light 00268 spheres.push_back(Sphere(Vec3f( 0.0, 20, -30), 3, Vec3f(0.00, 0.00, 0.00), 0, 0.0, Vec3f(3))); 00269 render(spheres,32); 00270 render(spheres,16); 00271 render(spheres,8); 00272 render(spheres,4); 00273 render(spheres,2); 00274 render(spheres,1); 00275 while (g.isRunning()) { 00276 if(g.update(true)) { 00277 00278 } 00279 } 00280 return 0; 00281 }
Generated on Fri Jul 15 2022 19:44:28 by 1.7.2