Raytracing demonstration for Pokitto

Dependencies:   PokittoLib

Committer:
Pokitto
Date:
Wed May 02 06:52:50 2018 +0000
Revision:
3:c5e3a23c9ff3
Parent:
0:398e251490fd
New pokittolib with improved volume controls & better button handling

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Pokitto 0:398e251490fd 1 // [header]
Pokitto 0:398e251490fd 2 // A very basic raytracer example.
Pokitto 0:398e251490fd 3 // [/header]
Pokitto 0:398e251490fd 4 // [compile]
Pokitto 0:398e251490fd 5 // c++ -o raytracer -O3 -Wall raytracer.cpp
Pokitto 0:398e251490fd 6 // [/compile]
Pokitto 0:398e251490fd 7 // [ignore]
Pokitto 0:398e251490fd 8 // Copyright (C) 2012 www.scratchapixel.com
Pokitto 0:398e251490fd 9 //
Pokitto 0:398e251490fd 10 // This program is free software: you can redistribute it and/or modify
Pokitto 0:398e251490fd 11 // it under the terms of the GNU General Public License as published by
Pokitto 0:398e251490fd 12 // the Free Software Foundation, either version 3 of the License, or
Pokitto 0:398e251490fd 13 // (at your option) any later version.
Pokitto 0:398e251490fd 14 //
Pokitto 0:398e251490fd 15 // This program is distributed in the hope that it will be useful,
Pokitto 0:398e251490fd 16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
Pokitto 0:398e251490fd 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Pokitto 0:398e251490fd 18 // GNU General Public License for more details.
Pokitto 0:398e251490fd 19 //
Pokitto 0:398e251490fd 20 // You should have received a copy of the GNU General Public License
Pokitto 0:398e251490fd 21 // along with this program. If not, see <http://www.gnu.org/licenses/>.
Pokitto 0:398e251490fd 22 // [/ignore]
Pokitto 0:398e251490fd 23 #include "Pokitto.h"
Pokitto 0:398e251490fd 24 #include <cstdlib>
Pokitto 0:398e251490fd 25 #include <cstdio>
Pokitto 0:398e251490fd 26 #include <cmath>
Pokitto 0:398e251490fd 27 //#include <fstream>
Pokitto 0:398e251490fd 28 #include <vector>
Pokitto 0:398e251490fd 29 //#include <iostream>
Pokitto 0:398e251490fd 30 //#include <cassert>
Pokitto 0:398e251490fd 31
Pokitto 0:398e251490fd 32 Pokitto::Core g;
Pokitto 0:398e251490fd 33
Pokitto 0:398e251490fd 34 #if defined __linux__ || defined __APPLE__
Pokitto 0:398e251490fd 35 // "Compiled for Linux
Pokitto 0:398e251490fd 36 #else
Pokitto 0:398e251490fd 37 // Windows doesn't define these values by default, Linux does
Pokitto 0:398e251490fd 38 #define M_PI 3.141592653589793
Pokitto 0:398e251490fd 39 #define INFINITY 1e8
Pokitto 0:398e251490fd 40 #endif
Pokitto 0:398e251490fd 41
Pokitto 0:398e251490fd 42 template<typename T>
Pokitto 0:398e251490fd 43 class Vec3
Pokitto 0:398e251490fd 44 {
Pokitto 0:398e251490fd 45 public:
Pokitto 0:398e251490fd 46 T x, y, z;
Pokitto 0:398e251490fd 47 Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
Pokitto 0:398e251490fd 48 Vec3(T xx) : x(xx), y(xx), z(xx) {}
Pokitto 0:398e251490fd 49 Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
Pokitto 0:398e251490fd 50 Vec3& normalize()
Pokitto 0:398e251490fd 51 {
Pokitto 0:398e251490fd 52 T nor2 = length2();
Pokitto 0:398e251490fd 53 if (nor2 > 0) {
Pokitto 0:398e251490fd 54 T invNor = 1 / sqrt(nor2);
Pokitto 0:398e251490fd 55 x *= invNor, y *= invNor, z *= invNor;
Pokitto 0:398e251490fd 56 }
Pokitto 0:398e251490fd 57 return *this;
Pokitto 0:398e251490fd 58 }
Pokitto 0:398e251490fd 59 Vec3<T> operator * (const T &f) const { return Vec3<T>(x * f, y * f, z * f); }
Pokitto 0:398e251490fd 60 Vec3<T> operator * (const Vec3<T> &v) const { return Vec3<T>(x * v.x, y * v.y, z * v.z); }
Pokitto 0:398e251490fd 61 T dot(const Vec3<T> &v) const { return x * v.x + y * v.y + z * v.z; }
Pokitto 0:398e251490fd 62 Vec3<T> operator - (const Vec3<T> &v) const { return Vec3<T>(x - v.x, y - v.y, z - v.z); }
Pokitto 0:398e251490fd 63 Vec3<T> operator + (const Vec3<T> &v) const { return Vec3<T>(x + v.x, y + v.y, z + v.z); }
Pokitto 0:398e251490fd 64 Vec3<T>& operator += (const Vec3<T> &v) { x += v.x, y += v.y, z += v.z; return *this; }
Pokitto 0:398e251490fd 65 Vec3<T>& operator *= (const Vec3<T> &v) { x *= v.x, y *= v.y, z *= v.z; return *this; }
Pokitto 0:398e251490fd 66 Vec3<T> operator - () const { return Vec3<T>(-x, -y, -z); }
Pokitto 0:398e251490fd 67 T length2() const { return x * x + y * y + z * z; }
Pokitto 0:398e251490fd 68 T length() const { return sqrt(length2()); }
Pokitto 0:398e251490fd 69
Pokitto 0:398e251490fd 70 };
Pokitto 0:398e251490fd 71
Pokitto 0:398e251490fd 72 typedef Vec3<float> Vec3f;
Pokitto 0:398e251490fd 73
Pokitto 0:398e251490fd 74 class Sphere
Pokitto 0:398e251490fd 75 {
Pokitto 0:398e251490fd 76 public:
Pokitto 0:398e251490fd 77 Vec3f center; /// position of the sphere
Pokitto 0:398e251490fd 78 float radius, radius2; /// sphere radius and radius^2
Pokitto 0:398e251490fd 79 Vec3f surfaceColor, emissionColor; /// surface color and emission (light)
Pokitto 0:398e251490fd 80 float transparency, reflection; /// surface transparency and reflectivity
Pokitto 0:398e251490fd 81 Sphere(
Pokitto 0:398e251490fd 82 const Vec3f &c,
Pokitto 0:398e251490fd 83 const float &r,
Pokitto 0:398e251490fd 84 const Vec3f &sc,
Pokitto 0:398e251490fd 85 const float &refl = 0,
Pokitto 0:398e251490fd 86 const float &transp = 0,
Pokitto 0:398e251490fd 87 const Vec3f &ec = 0) :
Pokitto 0:398e251490fd 88 center(c), radius(r), radius2(r * r), surfaceColor(sc), emissionColor(ec),
Pokitto 0:398e251490fd 89 transparency(transp), reflection(refl)
Pokitto 0:398e251490fd 90 { /* empty */ }
Pokitto 0:398e251490fd 91 //[comment]
Pokitto 0:398e251490fd 92 // Compute a ray-sphere intersection using the geometric solution
Pokitto 0:398e251490fd 93 //[/comment]
Pokitto 0:398e251490fd 94 bool intersect(const Vec3f &rayorig, const Vec3f &raydir, float &t0, float &t1) const
Pokitto 0:398e251490fd 95 {
Pokitto 0:398e251490fd 96 Vec3f l = center - rayorig;
Pokitto 0:398e251490fd 97 float tca = l.dot(raydir);
Pokitto 0:398e251490fd 98 if (tca < 0) return false;
Pokitto 0:398e251490fd 99 float d2 = l.dot(l) - tca * tca;
Pokitto 0:398e251490fd 100 if (d2 > radius2) return false;
Pokitto 0:398e251490fd 101 float thc = sqrt(radius2 - d2);
Pokitto 0:398e251490fd 102 t0 = tca - thc;
Pokitto 0:398e251490fd 103 t1 = tca + thc;
Pokitto 0:398e251490fd 104
Pokitto 0:398e251490fd 105 return true;
Pokitto 0:398e251490fd 106 }
Pokitto 0:398e251490fd 107 };
Pokitto 0:398e251490fd 108
Pokitto 0:398e251490fd 109 //[comment]
Pokitto 0:398e251490fd 110 // This variable controls the maximum recursion depth
Pokitto 0:398e251490fd 111 //[/comment]
Pokitto 0:398e251490fd 112 #define MAX_RAY_DEPTH 5
Pokitto 0:398e251490fd 113
Pokitto 0:398e251490fd 114 float mix(const float &a, const float &b, const float &mix)
Pokitto 0:398e251490fd 115 {
Pokitto 0:398e251490fd 116 return b * mix + a * (1 - mix);
Pokitto 0:398e251490fd 117 }
Pokitto 0:398e251490fd 118
Pokitto 0:398e251490fd 119 //[comment]
Pokitto 0:398e251490fd 120 // This is the main trace function. It takes a ray as argument (defined by its origin
Pokitto 0:398e251490fd 121 // and direction). We test if this ray intersects any of the geometry in the scene.
Pokitto 0:398e251490fd 122 // If the ray intersects an object, we compute the intersection point, the normal
Pokitto 0:398e251490fd 123 // at the intersection point, and shade this point using this information.
Pokitto 0:398e251490fd 124 // Shading depends on the surface property (is it transparent, reflective, diffuse).
Pokitto 0:398e251490fd 125 // The function returns a color for the ray. If the ray intersects an object that
Pokitto 0:398e251490fd 126 // is the color of the object at the intersection point, otherwise it returns
Pokitto 0:398e251490fd 127 // the background color.
Pokitto 0:398e251490fd 128 //[/comment]
Pokitto 0:398e251490fd 129 Vec3f trace(
Pokitto 0:398e251490fd 130 const Vec3f &rayorig,
Pokitto 0:398e251490fd 131 const Vec3f &raydir,
Pokitto 0:398e251490fd 132 const std::vector<Sphere> &spheres,
Pokitto 0:398e251490fd 133 const int &depth)
Pokitto 0:398e251490fd 134 {
Pokitto 0:398e251490fd 135 //if (raydir.length() != 1) std::cerr << "Error " << raydir << std::endl;
Pokitto 0:398e251490fd 136 float tnear = INFINITY;
Pokitto 0:398e251490fd 137 const Sphere* sphere = NULL;
Pokitto 0:398e251490fd 138 // find intersection of this ray with the sphere in the scene
Pokitto 0:398e251490fd 139 for (unsigned i = 0; i < spheres.size(); ++i) {
Pokitto 0:398e251490fd 140 float t0 = INFINITY, t1 = INFINITY;
Pokitto 0:398e251490fd 141 if (spheres[i].intersect(rayorig, raydir, t0, t1)) {
Pokitto 0:398e251490fd 142 if (t0 < 0) t0 = t1;
Pokitto 0:398e251490fd 143 if (t0 < tnear) {
Pokitto 0:398e251490fd 144 tnear = t0;
Pokitto 0:398e251490fd 145 sphere = &spheres[i];
Pokitto 0:398e251490fd 146 }
Pokitto 0:398e251490fd 147 }
Pokitto 0:398e251490fd 148 }
Pokitto 0:398e251490fd 149 // if there's no intersection return black or background color
Pokitto 0:398e251490fd 150 if (!sphere) return Vec3f(2);
Pokitto 0:398e251490fd 151 Vec3f surfaceColor = 0; // color of the ray/surfaceof the object intersected by the ray
Pokitto 0:398e251490fd 152 Vec3f phit = rayorig + raydir * tnear; // point of intersection
Pokitto 0:398e251490fd 153 Vec3f nhit = phit - sphere->center; // normal at the intersection point
Pokitto 0:398e251490fd 154 nhit.normalize(); // normalize normal direction
Pokitto 0:398e251490fd 155 // If the normal and the view direction are not opposite to each other
Pokitto 0:398e251490fd 156 // reverse the normal direction. That also means we are inside the sphere so set
Pokitto 0:398e251490fd 157 // the inside bool to true. Finally reverse the sign of IdotN which we want
Pokitto 0:398e251490fd 158 // positive.
Pokitto 0:398e251490fd 159 float bias = 1e-4; // add some bias to the point from which we will be tracing
Pokitto 0:398e251490fd 160 bool inside = false;
Pokitto 0:398e251490fd 161 if (raydir.dot(nhit) > 0) nhit = -nhit, inside = true;
Pokitto 0:398e251490fd 162 if ((sphere->transparency > 0 || sphere->reflection > 0) && depth < MAX_RAY_DEPTH) {
Pokitto 0:398e251490fd 163 float facingratio = -raydir.dot(nhit);
Pokitto 0:398e251490fd 164 // change the mix value to tweak the effect
Pokitto 0:398e251490fd 165 float fresneleffect = mix(pow(1 - facingratio, 3), 1, 0.1);
Pokitto 0:398e251490fd 166 // compute reflection direction (not need to normalize because all vectors
Pokitto 0:398e251490fd 167 // are already normalized)
Pokitto 0:398e251490fd 168 Vec3f refldir = raydir - nhit * 2 * raydir.dot(nhit);
Pokitto 0:398e251490fd 169 refldir.normalize();
Pokitto 0:398e251490fd 170 Vec3f reflection = trace(phit + nhit * bias, refldir, spheres, depth + 1);
Pokitto 0:398e251490fd 171 Vec3f refraction = 0;
Pokitto 0:398e251490fd 172 // if the sphere is also transparent compute refraction ray (transmission)
Pokitto 0:398e251490fd 173 if (sphere->transparency) {
Pokitto 0:398e251490fd 174 float ior = 1.1, eta = (inside) ? ior : 1 / ior; // are we inside or outside the surface?
Pokitto 0:398e251490fd 175 float cosi = -nhit.dot(raydir);
Pokitto 0:398e251490fd 176 float k = 1 - eta * eta * (1 - cosi * cosi);
Pokitto 0:398e251490fd 177 Vec3f refrdir = raydir * eta + nhit * (eta * cosi - sqrt(k));
Pokitto 0:398e251490fd 178 refrdir.normalize();
Pokitto 0:398e251490fd 179 refraction = trace(phit - nhit * bias, refrdir, spheres, depth + 1);
Pokitto 0:398e251490fd 180 }
Pokitto 0:398e251490fd 181 // the result is a mix of reflection and refraction (if the sphere is transparent)
Pokitto 0:398e251490fd 182 surfaceColor = (
Pokitto 0:398e251490fd 183 reflection * fresneleffect +
Pokitto 0:398e251490fd 184 refraction * (1 - fresneleffect) * sphere->transparency) * sphere->surfaceColor;
Pokitto 0:398e251490fd 185 }
Pokitto 0:398e251490fd 186 else {
Pokitto 0:398e251490fd 187 // it's a diffuse object, no need to raytrace any further
Pokitto 0:398e251490fd 188 for (unsigned i = 0; i < spheres.size(); ++i) {
Pokitto 0:398e251490fd 189 if (spheres[i].emissionColor.x > 0) {
Pokitto 0:398e251490fd 190 // this is a light
Pokitto 0:398e251490fd 191 Vec3f transmission = 1;
Pokitto 0:398e251490fd 192 Vec3f lightDirection = spheres[i].center - phit;
Pokitto 0:398e251490fd 193 lightDirection.normalize();
Pokitto 0:398e251490fd 194 for (unsigned j = 0; j < spheres.size(); ++j) {
Pokitto 0:398e251490fd 195 if (i != j) {
Pokitto 0:398e251490fd 196 float t0, t1;
Pokitto 0:398e251490fd 197 if (spheres[j].intersect(phit + nhit * bias, lightDirection, t0, t1)) {
Pokitto 0:398e251490fd 198 transmission = 0;
Pokitto 0:398e251490fd 199 break;
Pokitto 0:398e251490fd 200 }
Pokitto 0:398e251490fd 201 }
Pokitto 0:398e251490fd 202 }
Pokitto 0:398e251490fd 203 surfaceColor += sphere->surfaceColor * transmission *
Pokitto 0:398e251490fd 204 std::max(float(0), nhit.dot(lightDirection)) * spheres[i].emissionColor;
Pokitto 0:398e251490fd 205 }
Pokitto 0:398e251490fd 206 }
Pokitto 0:398e251490fd 207 }
Pokitto 0:398e251490fd 208
Pokitto 0:398e251490fd 209 return surfaceColor + sphere->emissionColor;
Pokitto 0:398e251490fd 210 }
Pokitto 0:398e251490fd 211
Pokitto 0:398e251490fd 212 //[comment]
Pokitto 0:398e251490fd 213 // Main rendering function. We compute a camera ray for each pixel of the image
Pokitto 0:398e251490fd 214 // trace it and return a color. If the ray hits a sphere, we return the color of the
Pokitto 0:398e251490fd 215 // sphere at the intersection point, else we return the background color.
Pokitto 0:398e251490fd 216 //[/comment]
Pokitto 0:398e251490fd 217 void render(const std::vector<Sphere> &spheres, int rscale)
Pokitto 0:398e251490fd 218 {
Pokitto 0:398e251490fd 219 unsigned width = 220, height = 176;
Pokitto 0:398e251490fd 220 //Vec3f *image = new Vec3f[width * height], *pixel = image;
Pokitto 0:398e251490fd 221 Vec3f pixel; //jonne
Pokitto 0:398e251490fd 222 float invWidth = 1 / float(width), invHeight = 1 / float(height);
Pokitto 0:398e251490fd 223 float fov = 30, aspectratio = width / float(height);
Pokitto 0:398e251490fd 224 float angle = tan(M_PI * 0.5 * fov / 180.);
Pokitto 0:398e251490fd 225 // Trace rays
Pokitto 0:398e251490fd 226 for (unsigned y = 1; y < height+1; y+=rscale) {
Pokitto 0:398e251490fd 227 for (unsigned x = 1; x < width+1; x+=rscale) { //, ++pixel) {
Pokitto 0:398e251490fd 228 float xx = (2 * ((x + 0.5*rscale) * invWidth) - 1) * angle * aspectratio;
Pokitto 0:398e251490fd 229 float yy = (1 - 2 * ((y + 0.5*rscale) * invHeight)) * angle;
Pokitto 0:398e251490fd 230 Vec3f raydir(xx, yy, -1);
Pokitto 0:398e251490fd 231 raydir.normalize();
Pokitto 0:398e251490fd 232 //*pixel = trace(Vec3f(0), raydir, spheres, 0);
Pokitto 0:398e251490fd 233 pixel = trace(Vec3f(0), raydir, spheres, 0);
Pokitto 0:398e251490fd 234 //g.display.directPixel(x,y,g.display.RGBto565(pixel.x*255,pixel.y*255,pixel.z*255));
Pokitto 0:398e251490fd 235 g.display.directRectangle(x,y,x+rscale,y+rscale,g.display.RGBto565(pixel.x*255,pixel.y*255,pixel.z*255));
Pokitto 0:398e251490fd 236 //g.wait(100);
Pokitto 0:398e251490fd 237 }
Pokitto 0:398e251490fd 238 }
Pokitto 0:398e251490fd 239 // Save result to a PPM image (keep these flags if you compile under Windows)
Pokitto 0:398e251490fd 240 /*std::ofstream ofs("./untitled.ppm", std::ios::out | std::ios::binary);
Pokitto 0:398e251490fd 241 ofs << "P6\n" << width << " " << height << "\n255\n";
Pokitto 0:398e251490fd 242 for (unsigned i = 0; i < width * height; ++i) {
Pokitto 0:398e251490fd 243 ofs << (unsigned char)(std::min(float(1), image[i].x) * 255) <<
Pokitto 0:398e251490fd 244 (unsigned char)(std::min(float(1), image[i].y) * 255) <<
Pokitto 0:398e251490fd 245 (unsigned char)(std::min(float(1), image[i].z) * 255);
Pokitto 0:398e251490fd 246 }
Pokitto 0:398e251490fd 247 ofs.close();*/
Pokitto 0:398e251490fd 248 //delete [] image;
Pokitto 0:398e251490fd 249 }
Pokitto 0:398e251490fd 250
Pokitto 0:398e251490fd 251 //[comment]
Pokitto 0:398e251490fd 252 // In the main function, we will create the scene which is composed of 5 spheres
Pokitto 0:398e251490fd 253 // and 1 light (which is also a sphere). Then, once the scene description is complete
Pokitto 0:398e251490fd 254 // we render that scene, by calling the render() function.
Pokitto 0:398e251490fd 255 //[/comment]
Pokitto 0:398e251490fd 256 int main()
Pokitto 0:398e251490fd 257 {
Pokitto 0:398e251490fd 258 g.begin();
Pokitto 0:398e251490fd 259 //srand48(13);
Pokitto 0:398e251490fd 260 std::vector<Sphere> spheres;
Pokitto 0:398e251490fd 261 // position, radius, surface color, reflectivity, transparency, emission color
Pokitto 0:398e251490fd 262 spheres.push_back(Sphere(Vec3f( 0.0, -10004, -20), 10000, Vec3f(0.20, 0.20, 0.20), 0, 0.0));
Pokitto 0:398e251490fd 263 spheres.push_back(Sphere(Vec3f( 0.0, 0, -20), 4, Vec3f(0.32, 1.00, 0.16), 1, 0.5)); //green middle sphere
Pokitto 0:398e251490fd 264 spheres.push_back(Sphere(Vec3f( 5.0, -1, -15), 2, Vec3f(1.00, 0.16, 1.00), 1, 0.5)); //magenta
Pokitto 0:398e251490fd 265 spheres.push_back(Sphere(Vec3f( 5.0, 0, -25), 3, Vec3f(0.36, 0.16, 0.97), 1, 0.5)); //blue
Pokitto 0:398e251490fd 266 spheres.push_back(Sphere(Vec3f(-5.5, 0, -15), 3, Vec3f(1.00, 0.65, 0.30), 1, 0.5)); //orange
Pokitto 0:398e251490fd 267 // light
Pokitto 0:398e251490fd 268 spheres.push_back(Sphere(Vec3f( 0.0, 20, -30), 3, Vec3f(0.00, 0.00, 0.00), 0, 0.0, Vec3f(3)));
Pokitto 0:398e251490fd 269 render(spheres,32);
Pokitto 0:398e251490fd 270 render(spheres,16);
Pokitto 0:398e251490fd 271 render(spheres,8);
Pokitto 0:398e251490fd 272 render(spheres,4);
Pokitto 0:398e251490fd 273 render(spheres,2);
Pokitto 0:398e251490fd 274 render(spheres,1);
Pokitto 0:398e251490fd 275 while (g.isRunning()) {
Pokitto 0:398e251490fd 276 if(g.update(true)) {
Pokitto 0:398e251490fd 277
Pokitto 0:398e251490fd 278 }
Pokitto 0:398e251490fd 279 }
Pokitto 0:398e251490fd 280 return 0;
Pokitto 0:398e251490fd 281 }