From 2f5cead9a8bb53fc2874d0f385b139a5502eb744 Mon Sep 17 00:00:00 2001 From: Edin Omeragic Date: Mon, 29 Feb 2016 20:13:23 +0100 Subject: [PATCH] Added Go version. --- .gitattributes | 63 ++ .gitignore | 3 + c++/RayTracer.cpp | 536 +++++++++--------- c++/RayTracer/RayTracer.sln | 22 + c++/RayTracer/RayTracer.vcxproj | 73 +++ c++/RayTracer/RayTracer.vcxproj.filters | 22 + .../Release/RayTracer.Build.CppClean.log | 10 + c++/run-gcc.bat | 2 +- csharp/RayTracer.cs | 2 + delphi/RayTracer.pas | 171 +++--- delphi/run.bat | 2 +- go/RayTracer.go | 534 +++++++++++++++++ php/RayTracer.php | 319 ++++++----- readme.md | 9 +- 14 files changed, 1294 insertions(+), 474 deletions(-) create mode 100644 .gitattributes create mode 100644 c++/RayTracer/RayTracer.sln create mode 100644 c++/RayTracer/RayTracer.vcxproj create mode 100644 c++/RayTracer/RayTracer.vcxproj.filters create mode 100644 c++/RayTracer/Release/RayTracer.Build.CppClean.log create mode 100644 go/RayTracer.go diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index 511527e..c31f401 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ vb/obj vb/VBRayTracer.vbproj.user *.suo obj +*.pdb +*/Debug/* +*/Release/* \ No newline at end of file diff --git a/c++/RayTracer.cpp b/c++/RayTracer.cpp index b39ff65..9f6a054 100644 --- a/c++/RayTracer.cpp +++ b/c++/RayTracer.cpp @@ -2,7 +2,8 @@ #include #include #include -#include +#include +#include #ifdef _MSC_VER @@ -12,13 +13,15 @@ #endif #endif -struct RGB_COLOR +struct RgbColor { - byte r; - byte g; byte b; + byte g; + byte r; + byte a; }; + class Vector { public: @@ -26,56 +29,66 @@ class Vector double y; double z; - Vector() + Vector() : x(0), y(0), z(0) { - x = 0; - y = 0; - z = 0; } - Vector(double x, double y, double z) + Vector(double x, double y, double z) { this->x = x; this->y = y; this->z = z; } - static Vector times(double k, const Vector &v) + + static Vector cross(const Vector &v1, const Vector &v2) { - return Vector(k * v.x, k * v.y, k * v.z); + return Vector(v1.y * v2.z - v1.z * v2.y, + v1.z * v2.x - v1.x * v2.z, + v1.x * v2.y - v1.y * v2.x); } - - static Vector minus(const Vector &v1, const Vector &v2) + + double mag() const + { + return sqrt(this->x * this->x + this->y * this->y + this->z * this->z); + } + + Vector norm() const { - return Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); + double mag = this->mag(); + double div = (mag == 0) ? INFINITY : 1.0 / mag; + return this->scale(div); } - - static Vector plus(const Vector &v1, const Vector &v2) + + Vector cross(const Vector &v2) const { - return Vector(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); + return Vector(this->y * v2.z - this->z * v2.y, + this->z * v2.x - this->x * v2.z, + this->x * v2.y - this->y * v2.x); } - - static double dot(const Vector &v1, const Vector &v2) + + Vector scale(double k) const { - return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; + return Vector(k * this->x, k * this->y, k * this->z); + } + + Vector operator*(double k) + { + return this->scale(k); } - - static double mag(const Vector &v) + + double operator*(const Vector& v2) { - return sqrt(v.x * v.x + v.y * v.y + v.z * v.z); + return this->x * v2.x + this->y * v2.y + this->z * v2.z; } - - static Vector norm(const Vector &v) + + Vector operator+(const Vector& v2) { - double mag = Vector::mag(v); - double div = (mag == 0) ? INFINITY : 1.0 / mag; - return Vector::times(div, v); + return Vector(this->x + v2.x, this->y + v2.y, this->z + v2.z); } - - static Vector cross(const Vector &v1,const Vector &v2) + + Vector operator-(const Vector& v2) { - return Vector(v1.y * v2.z - v1.z * v2.y, - v1.z * v2.x - v1.x * v2.z, - v1.x * v2.y - v1.y * v2.x); + return Vector(this->x - v2.x, this->y - v2.y, this->z - v2.z); } }; @@ -93,11 +106,8 @@ class Color static Color background; static Color defaultColor; - Color() + Color() : r(0), g(0), b(0) { - r = 0; - g = 0; - b = 0; } Color(double r, double g, double b) @@ -107,34 +117,45 @@ class Color this->b = b; } - static Color scale(double k, const Color &v) + Color scale(double k) { - return Color(k * v.r, k * v.g, k * v.b); - } - - static Color plus(const Color &v1, const Color &v2) - { - return Color(v1.r + v2.r, v1.g + v2.g, v1.b + v2.b); + return Color(k * this->r, k * this->g, k * this->b); } static Color times(const Color &v1, const Color &v2) { return Color(v1.r * v2.r, v1.g * v2.g, v1.b * v2.b); } - - static RGB_COLOR toDrawingColor(const Color &c) + + Color operator*(double k) + { + return this->scale(k); + } + + Color operator*(const Color& v2) { - RGB_COLOR color; - color.r = (byte)legalize(c.r); - color.g = (byte)legalize(c.g); - color.b = (byte)legalize(c.b); + return Color(this->r * v2.r, this->g * v2.g, this->b * v2.b); + } + + Color operator+(const Color& v2) + { + return Color(this->r + v2.r, this->g + v2.g, this->b + v2.b); + } + RgbColor toDrawingColor() + { + RgbColor color; + color.r = (byte)legalize(this->r); + color.g = (byte)legalize(this->g); + color.b = (byte)legalize(this->b); + color.a = 255; return color; } - static int legalize(double c){ + static int legalize(double c) + { int x = (int)(c * 255); - if (x < 0) x = 0; + if (x < 0) x = 0; if (x > 255) x = 255; return x; } @@ -146,7 +167,6 @@ Color Color::black = Color(0.0, 0.0, 0.0); Color Color::background = Color::black; Color Color::defaultColor = Color::black; - class Camera { public: @@ -159,16 +179,15 @@ class Camera Camera (Vector pos, Vector lookAt) { - this->pos = pos; + this->pos = pos; Vector down = Vector(0.0, -1.0, 0.0); - Vector forward = Vector::minus(lookAt, pos); - this->forward = Vector::norm(forward); - this->right = Vector::times(1.5, Vector::norm(Vector::cross(this->forward, down))); - this->up = Vector::times(1.5, Vector::norm(Vector::cross(this->forward, this->right))); + Vector forward = lookAt - pos; + this->forward = forward.norm(); + this->right = this->forward.cross(down).norm() * 1.5; + this->up = this->forward.cross(this->right).norm() * 1.5; } }; - class Ray { public : @@ -188,36 +207,48 @@ class Thing; class Intersection { +private: + bool m_IsValid = false; public: Thing* thing; Ray ray; double dist; - Intersection(Thing* thing, Ray ray, double dist) + Intersection() + { + m_IsValid = false; + } + + Intersection(Thing* thing, Ray ray, double dist) { this->thing = thing; - this->ray = ray; - this->dist = dist; + this->ray = ray; + this->dist = dist; + this->m_IsValid = true; } + + bool IsValid(){ + return m_IsValid; + } }; class Surface { public: - virtual Color diffuse(Vector pos) { return Color::black; }; - virtual Color specular(Vector pos) { return Color::black; }; - virtual double reflect(Vector pos) { return 0; }; - virtual double roughness() { return 0; }; + virtual Color diffuse(Vector& pos) { return Color::black; }; + virtual Color specular(Vector& pos) { return Color::black; }; + virtual double reflect(Vector& pos) { return 0; }; + virtual double roughness() { return 0; }; }; class Thing { public: - virtual Intersection *intersect(Ray ray) = 0; - virtual Vector normal(Vector pos) = 0; - virtual Surface *surface()= 0; + virtual Intersection intersect(Ray& ray) = 0; + virtual Vector normal(Vector& pos) = 0; + virtual std::shared_ptr surface() = 0; }; @@ -234,195 +265,198 @@ class Light } }; - -class Scene +class Sphere : public Thing { +private: + std::shared_ptr m_surface; + double m_radius2; + Vector m_center; public: - virtual std::vector* things() = 0; - virtual std::vector* lights() = 0; - virtual Camera camera() = 0; -}; - -class Sphere : Thing -{ -public : - double radius2; - Vector center; - Surface *_surface; - Sphere(Vector center, double radius, Surface *surface) + Sphere(Vector center, double radius, std::shared_ptr const& surface) { - this->radius2 = radius * radius; - this->center = center; - this->_surface = surface; + this->m_radius2 = radius * radius; + this->m_center = center; + this->m_surface = surface; } - Vector normal(Vector pos) { return Vector::norm(Vector::minus(pos, this->center)); } + Vector normal(Vector& pos) + { + return (pos - this->m_center).norm(); + } - Intersection* intersect(Ray ray) + Intersection intersect(Ray& ray) { - Vector eo = Vector::minus(this->center, ray.start); - double v = Vector::dot(eo, ray.dir); + Vector eo = this->m_center - ray.start; + double v = eo * ray.dir; double dist = 0; + if (v >= 0) { - double disc = this->radius2 - (Vector::dot(eo, eo) - v * v); + double disc = this->m_radius2 - ((eo * eo) - (v * v)); if (disc >= 0) { dist = v - sqrt(disc); } } if (dist == 0) { - return nullptr; - } - else { - return new Intersection(this, ray, dist); + return Intersection(); } + return Intersection(this, ray, dist); } - Surface* surface() { - return _surface; + std::shared_ptr surface() + { + return m_surface; } }; -class Plane: Thing +class Plane : public Thing { private: Vector norm; double offset; + std::shared_ptr m_surface; public: - Surface* _surface; - Vector normal(Vector pos) { + Vector normal(Vector& pos) + { return this->norm; } - Intersection* intersect(Ray ray){ - double denom = Vector::dot(norm, ray.dir); + Intersection intersect(Ray& ray) + { + double denom = norm * ray.dir; if (denom > 0) { - return nullptr; - } - else { - double dist = (Vector::dot(norm, ray.start) + offset) / (-denom); - return new Intersection(this, ray, dist); + return Intersection(); } + double dist = ((norm * ray.start) + offset) / (-denom); + return Intersection(this, ray, dist); } - Plane(Vector norm, double offset, Surface* surface) + Plane(Vector norm, double offset, std::shared_ptr surface) { - this->_surface = surface; + this->m_surface = surface; this->norm = norm; this->offset = offset; } - Surface* surface() { - return _surface; + std::shared_ptr surface() + { + return m_surface; } }; -class ShinySurface: Surface +class ShinySurface : public Surface { public: - Color diffuse(Vector pos) + Color diffuse(Vector& pos) { return Color::white; } - Color specular(Vector pos){ + Color specular(Vector& pos) + { return Color::grey; } - double reflect(Vector pos) + double reflect(Vector& pos) { return 0.7; } - double roughness(){ + + double roughness() + { return 250.0; } }; -class CheckerboardSurface : Surface +class CheckerboardSurface : public Surface { public: - Color diffuse(Vector pos) + Color diffuse(Vector& pos) { - if ( ((int)(floor(pos.z) + floor(pos.x))) % 2 != 0) { - return Color::white; - } - else + if (((int)(floor(pos.z) + floor(pos.x))) % 2 != 0) { - return Color::black; + return Color::white; } + return Color::black; } - Color specular(Vector pos){ + Color specular(Vector& pos) + { return Color::white; } - double reflect(Vector pos) + double reflect(Vector& pos) { - if ( ((int)(floor(pos.z) + floor(pos.x))) % 2 != 0) { + if (((int)(floor(pos.z) + floor(pos.x))) % 2 != 0) + { return 0.1; - } else { - return 0.7; } + return 0.7; } - double roughness(){ + + double roughness() + { return 150.0; } }; + class Surfaces { public: - static ShinySurface* shiny; - static CheckerboardSurface* checkerboard; + static std::shared_ptr shiny; + static std::shared_ptr checkerboard; }; -ShinySurface* Surfaces::shiny = new ShinySurface(); -CheckerboardSurface* Surfaces::checkerboard = new CheckerboardSurface(); +std::shared_ptr Surfaces::shiny = std::make_shared(); +std::shared_ptr Surfaces::checkerboard = std::make_shared(); -class DefaultScene: Scene +class Scene +{ +public: + virtual std::vector> const& things() = 0; + virtual std::vector> const& lights() = 0; + virtual std::shared_ptr const& camera() = 0; +}; + +class DefaultScene : public Scene { private: - std::vector* _things; - std::vector* _lights; - Camera _camera; + std::vector> m_things; + std::vector> m_lights; + std::shared_ptr m_camera; public: DefaultScene() { - _things = new std::vector(); - _lights = new std::vector(); - - _things->push_back((Thing*)new Plane(Vector(0.0, 1.0, 0.0), 0.0, (Surface*)Surfaces::checkerboard)); - _things->push_back((Thing*)new Sphere(Vector(0.0, 1.0, -0.25), 1.0, (Surface*)Surfaces::shiny)); - _things->push_back((Thing*)new Sphere(Vector(-1.0, 0.5, 1.5), 0.5, (Surface*)Surfaces::shiny)); + m_things.emplace_back(new Plane(Vector(0.0, 1.0, 0.0), 0.0, Surfaces::checkerboard)); + m_things.emplace_back(new Sphere(Vector(0.0, 1.0, -0.25), 1.0, Surfaces::shiny)); + m_things.emplace_back(new Sphere(Vector(-1.0, 0.5, 1.5), 0.5, Surfaces::shiny)); + m_lights.emplace_back(new Light(Vector(-2.0, 2.5, 0.0), Color(0.49, 0.07, 0.07))); + m_lights.emplace_back(new Light(Vector(1.5, 2.5, 1.5), Color(0.07, 0.07, 0.49))); + m_lights.emplace_back(new Light(Vector(1.5, 2.5, -1.5), Color(0.07, 0.49, 0.071))); + m_lights.emplace_back(new Light(Vector(0.0, 3.5, 0.0), Color(0.21, 0.21, 0.35))); - Light *a = new Light(Vector(-2.0, 2.5, 0.0), Color(0.49, 0.07, 0.07)); - Light *b = new Light(Vector(1.5, 2.5, 1.5), Color(0.07, 0.07, 0.49)); - Light *c = new Light(Vector(1.5, 2.5, -1.5), Color(0.07, 0.49, 0.071)); - Light *d = new Light(Vector(0.0, 3.5, 0.0), Color(0.21, 0.21, 0.35)); - - _lights->push_back(a); - _lights->push_back(b); - _lights->push_back(c); - _lights->push_back(d); - - this->_camera = Camera(Vector(3.0, 2.0, 4.0), Vector(-1.0, 0.5, 0.0)); + this->m_camera = std::make_shared(Vector(3.0, 2.0, 4.0), Vector(-1.0, 0.5, 0.0)); } - std::vector* things() { - return _things; + std::vector> const& things() + { + return m_things; } - std::vector* lights() { - return _lights; + std::vector> const& lights() + { + return m_lights; } - Camera camera() { - return _camera; + std::shared_ptr const& camera() + { + return m_camera; } }; @@ -432,137 +466,125 @@ class RayTracerEngine private: static const int maxDepth = 5; - Intersection* intersections(Ray ray, Scene* scene) + Intersection intersections(Ray& ray, Scene& scene) { double closest = INFINITY; - Intersection *closestInter = nullptr; - std::vector *items = scene->things(); - for (std::vector::iterator thing = items->begin(); thing != items->end(); ++thing) + Intersection closestInter; + + for (auto& thing : scene.things()) { - Intersection* inter = (*thing)->intersect(ray); - if (inter != nullptr && inter->dist < closest) { + auto inter = thing->intersect(ray); + if (inter.IsValid() && inter.dist < closest) + { closestInter = inter; - closest = inter->dist; + closest = inter.dist; } } return closestInter; } - double testRay(Ray ray, Scene* scene) + double testRay(Ray& ray, Scene& scene) { - Intersection* isect = this->intersections(ray, scene); - if (isect != nullptr) - { - return isect->dist; - } - else + auto isect = this->intersections(ray, scene); + if (isect.IsValid()) { - return NAN; + return isect.dist; } + return NAN; } - - Color traceRay(Ray &ray, Scene* scene, int depth) { - Intersection *isect = this->intersections(ray, scene); - if (isect == nullptr) { - return Color::background; - } - else { - return this->shade(isect, scene, depth); + Color traceRay(Ray& ray, Scene& scene, int depth) + { + auto isect = this->intersections(ray, scene); + if (isect.IsValid()) + { + return this->shade(isect, scene, depth); } + return Color::background; } - - Color shade(Intersection* isect, Scene* scene, int depth) + Color shade(Intersection& isect, Scene& scene, int depth) { - Vector d = isect->ray.dir; - Vector pos = Vector::plus(Vector::times(isect->dist, d), isect->ray.start); - Vector normal = isect->thing->normal(pos); - - Vector reflectDir = Vector::minus(d, Vector::times(2, Vector::times(Vector::dot(normal, d), normal))); - Color naturalColor = Color::plus(Color::background, - this->getNaturalColor(isect->thing, pos, normal, reflectDir, scene)); - - Color reflectedColor = (depth >= this->maxDepth) ? Color::grey : this->getReflectionColor(isect->thing, pos, normal, reflectDir, scene, depth); - return Color::plus(naturalColor, reflectedColor); + Vector d = isect.ray.dir; + Vector pos = (d * isect.dist) + isect.ray.start; + Vector normal = isect.thing->normal(pos); + Vector reflectDir = d - ((normal * (normal * d)) * 2); + + Color naturalColor = Color::background + this->getNaturalColor(isect.thing, pos, normal, reflectDir, scene); + Color reflectedColor = (depth >= this->maxDepth) + ? Color::grey + : this->getReflectionColor(isect.thing, pos, normal, reflectDir, scene, depth); + + return naturalColor + reflectedColor; } - Color getReflectionColor(Thing* thing, Vector pos, Vector normal, Vector rd, Scene* scene, int depth) + Color getReflectionColor(Thing* thing, Vector& pos, Vector& normal, Vector& rd, Scene& scene, int depth) { - Ray ray(pos, rd); - return Color::scale(thing->surface()->reflect(pos), this->traceRay(ray, scene, depth + 1)); + Ray ray(pos, rd); + Color color = this->traceRay(ray, scene, depth + 1); + double factor = thing->surface()->reflect(pos); + return color.scale(factor); } - Color getNaturalColor(Thing* thing, Vector pos, Vector norm, Vector rd, Scene* scene) + Color getNaturalColor(Thing* thing, Vector& pos, Vector& norm, Vector& rd, Scene& scene) { - Color c = Color::black; + Color result = Color::black; + auto items = scene.lights(); - std::vector *items = scene->lights(); - - for (std::vector::iterator item = items->begin(); item != items->end(); ++item) + for(auto& item: items) { - Color newColor = addLight(c, (*item), pos, scene, thing, rd, norm); - c = newColor; + addLight(result, item, pos, scene, thing, rd, norm); } - return c; + return result; } - Color addLight(Color col, Light* light, Vector pos, Scene* scene, Thing* thing, Vector rd, Vector norm) + void addLight(Color& resultColor, std::shared_ptr const& light, Vector& pos, Scene& scene, Thing* thing, Vector& rd, Vector& norm) { - Vector ldis = Vector::minus(light->pos, pos); - Vector livec = Vector::norm(ldis); - double neatIsect = this->testRay(Ray(pos, livec), scene); + Vector ldis = light->pos - pos; + Vector livec = ldis.norm(); + Ray ray(pos, livec); + double neatIsect = this->testRay(ray, scene); - boolean isInShadow = (neatIsect == NAN) ? false : (neatIsect <= Vector::mag(ldis)); - if (isInShadow) { - return col; - } - else { - double illum = Vector::dot(livec, norm); - Color lcolor = (illum > 0) ? Color::scale(illum, light->color) - : Color::defaultColor; - double specular = Vector::dot(livec, Vector::norm(rd)); - Color scolor = (specular > 0) ? Color::scale(pow(specular, thing->surface()->roughness()), light->color) - : Color::defaultColor; - return Color::plus(col, Color::plus(Color::times(thing->surface()->diffuse(pos), lcolor), - Color::times(thing->surface()->specular(pos), scolor))); - } + bool isInShadow = (neatIsect == NAN) ? false : (neatIsect <= ldis.mag()); + if (isInShadow) { + return; + } + double illum = livec * norm; + double specular = livec * rd.norm(); + + Color lcolor = (illum > 0) ? (light->color * illum) : Color::defaultColor; + Color scolor = (specular > 0) ? (light->color * pow(specular, thing->surface()->roughness())) : Color::defaultColor; + resultColor = resultColor + lcolor * thing->surface()->diffuse(pos) + scolor * thing->surface()->specular(pos); } - Vector getPoint(int x, int y, Camera camera, int screenWidth, int screenHeight) + Vector getPoint(int x, int y, std::shared_ptr const& camera, int screenWidth, int screenHeight) { - double recenterX = (x - (screenWidth / 2.0)) / 2.0 / screenWidth; + double recenterX = (x - (screenWidth / 2.0)) / 2.0 / screenWidth; double recenterY = -(y - (screenHeight / 2.0)) / 2.0 / screenHeight; - return Vector::norm(Vector::plus(camera.forward, Vector::plus(Vector::times(recenterX, camera.right), Vector::times(recenterY, camera.up)))); + return (camera->forward + ((camera->right * recenterX) + (camera->up * recenterY))).norm(); } public: - - void render(Scene* scene, byte* bitmapData, int stride, int w, int h) + void render(Scene& scene, byte* bitmapData, int stride, int w, int h) { Ray ray; - ray.start = scene->camera().pos; - Camera camera = scene->camera(); + ray.start = scene.camera()->pos; + auto camera = scene.camera(); for (int y = 0; y < h; ++y) { - int pos = y * stride; + RgbColor* pColor = (RgbColor*)(&bitmapData[y * stride]); for (int x = 0; x < w; ++x) { - ray.dir = this->getPoint(x, y, camera, h, w); - Color color = this->traceRay(ray, scene, 0); - RGB_COLOR rgbColor = Color::toDrawingColor(color); - bitmapData[pos] = rgbColor.b; - bitmapData[pos + 1] = rgbColor.g; - bitmapData[pos + 2] = rgbColor.r; - bitmapData[pos + 3] = 255; - pos += 4; + ray.dir = this->getPoint(x, y, camera, h, w); + *pColor = this->traceRay(ray, scene, 0).toDrawingColor(); + pColor++; } } } }; -void SaveRGBBitmap(byte* pBitmapBits, LONG lWidth, LONG lHeight, LONG wBitsPerPixel, LPCSTR lpszFileName ) +void SaveRGBBitmap(byte* pBitmapBits, LONG lWidth, LONG lHeight, WORD wBitsPerPixel, LPCSTR lpszFileName) { BITMAPINFOHEADER bmpInfoHeader = {0}; bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER); @@ -580,43 +602,33 @@ void SaveRGBBitmap(byte* pBitmapBits, LONG lWidth, LONG lHeight, LONG wBitsPerPi bfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER); bfh.bfSize = bfh.bfOffBits + bmpInfoHeader.biSizeImage; - FILE * hFile; - hFile = fopen(lpszFileName,"wb"); - - if(!hFile) { - return; - } - - fwrite (&bfh , sizeof(char), sizeof(bfh), hFile); - fwrite (&bmpInfoHeader , sizeof(char), sizeof(bmpInfoHeader), hFile); - fwrite(pBitmapBits, sizeof(char), bmpInfoHeader.biSizeImage, hFile ); - fclose(hFile); + std::ofstream file(lpszFileName, std::ios::binary | std::ios::trunc); + file.write((const char*)&bfh, sizeof(bfh)); + file.write((const char*)&bmpInfoHeader, sizeof(bmpInfoHeader)); + file.write((const char*)pBitmapBits, bmpInfoHeader.biSizeImage); + file.close(); } - int main() { std::cout << "Started " << std::endl; long t1 = GetTickCount(); - Scene* scene = (Scene*) new DefaultScene(); - RayTracerEngine* rayTracer = new RayTracerEngine(); + DefaultScene scene; + RayTracerEngine rayTracer; int width = 500; int height = 500; int stride = width * 4; - byte *bitmapData = new byte[stride * height]; - - rayTracer->render(scene, bitmapData, stride,width,height); + + std::vector bitmapData(stride * height); + rayTracer.render(scene, &bitmapData[0], stride, width, height); long t2 = GetTickCount(); long time = t2 - t1; std::cout << "Completed in " << time << " ms" << std::endl; - SaveRGBBitmap(bitmapData, 500,500, 32, "cpp-raytracer.bmp"); + SaveRGBBitmap(&bitmapData[0], width, height, 32, "cpp-raytracer.bmp"); - delete rayTracer; - delete scene; - delete [] bitmapData; return 0; }; \ No newline at end of file diff --git a/c++/RayTracer/RayTracer.sln b/c++/RayTracer/RayTracer.sln new file mode 100644 index 0000000..b936402 --- /dev/null +++ b/c++/RayTracer/RayTracer.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RayTracer", "RayTracer.vcxproj", "{C3D9158F-AF17-4F0D-B34C-8780B32FF1C5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C3D9158F-AF17-4F0D-B34C-8780B32FF1C5}.Debug|Win32.ActiveCfg = Debug|Win32 + {C3D9158F-AF17-4F0D-B34C-8780B32FF1C5}.Debug|Win32.Build.0 = Debug|Win32 + {C3D9158F-AF17-4F0D-B34C-8780B32FF1C5}.Release|Win32.ActiveCfg = Release|Win32 + {C3D9158F-AF17-4F0D-B34C-8780B32FF1C5}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/c++/RayTracer/RayTracer.vcxproj b/c++/RayTracer/RayTracer.vcxproj new file mode 100644 index 0000000..046cf58 --- /dev/null +++ b/c++/RayTracer/RayTracer.vcxproj @@ -0,0 +1,73 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C3D9158F-AF17-4F0D-B34C-8780B32FF1C5} + RayTracer + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + + + true + + + + + Level3 + MaxSpeed + true + true + true + AnySuitable + + + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/c++/RayTracer/RayTracer.vcxproj.filters b/c++/RayTracer/RayTracer.vcxproj.filters new file mode 100644 index 0000000..ad470cb --- /dev/null +++ b/c++/RayTracer/RayTracer.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/c++/RayTracer/Release/RayTracer.Build.CppClean.log b/c++/RayTracer/Release/RayTracer.Build.CppClean.log new file mode 100644 index 0000000..74853dd --- /dev/null +++ b/c++/RayTracer/Release/RayTracer.Build.CppClean.log @@ -0,0 +1,10 @@ +d:\wamp\www\github\raytracer\c++\raytracer\release\vc120.pdb +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.obj +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.exe +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.pdb +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.tlog\cl.command.1.tlog +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.tlog\cl.read.1.tlog +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.tlog\cl.write.1.tlog +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.tlog\link.command.1.tlog +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.tlog\link.read.1.tlog +d:\wamp\www\github\raytracer\c++\raytracer\release\raytracer.tlog\link.write.1.tlog diff --git a/c++/run-gcc.bat b/c++/run-gcc.bat index 721544b..3d6be7e 100644 --- a/c++/run-gcc.bat +++ b/c++/run-gcc.bat @@ -1,2 +1,2 @@ -g++ RayTracer.cpp -O2 -std=c++11 -O2 -o gcc-raytracer.exe +g++ RayTracer.cpp -O2 -std=c++14 -o gcc-raytracer.exe gcc-raytracer.exe \ No newline at end of file diff --git a/csharp/RayTracer.cs b/csharp/RayTracer.cs index 544c152..433fb71 100644 --- a/csharp/RayTracer.cs +++ b/csharp/RayTracer.cs @@ -305,6 +305,7 @@ public CheckerboardSurface() { roughness = 150.0; } + public Color diffuse(Vector pos) { if ((Math.Floor(pos.z) + Math.Floor(pos.x)) % 2 != 0) { @@ -463,6 +464,7 @@ public void render(Scene scene, System.Drawing.Bitmap bmp) { int w = bmp.Width; int h = bmp.Height; + GetPointDelegate getPoint = (int x, int y, Camera camera) => { var recenterX = (x - (w / 2.0)) / 2.0 / w; diff --git a/delphi/RayTracer.pas b/delphi/RayTracer.pas index a2dc8b2..dc107e5 100644 --- a/delphi/RayTracer.pas +++ b/delphi/RayTracer.pas @@ -10,24 +10,24 @@ Light = class; Vector = record x,y,z:Double ; - class operator Add(a, b: Vector): Vector; - class operator Subtract(a, b: Vector): Vector; - class operator Multiply(k:Double ; b: Vector): Vector; + class operator Add(const a, b: Vector): Vector; + class operator Subtract(const a, b: Vector): Vector; + class operator Multiply( k:Double ; const b: Vector): Vector; constructor Create(x,y,z:Double ); - function dot(v:Vector):Double; + function dot(const v:Vector):Double; function norm():Vector; - function cross(v:Vector):Vector; + function cross(const v:Vector):Vector; function mag():Double ; end; RGBColor = record - r,g,b:Byte + a, b, g, r: Byte end; Color = record - r,g,b:Double ; + r,g,b:Double; class var white: Color; class var grey: Color; @@ -37,11 +37,11 @@ Color = record constructor Create(r,g,b:Double ); - class operator Add(a, b: Color): Color; - class operator Multiply(k:Double ; b: Color): Color; - class operator Multiply(a:Color; b: Color): Color; + class operator Add(const a, b: Color): Color; + class operator Multiply(k:Double; const b: Color): Color; + class operator Multiply(const a, b: Color): Color; - class function Clamp(v:Double ):Byte;static; + class function Clamp(v:Double):Byte; static; function ToDrawingColor:RGBColor; end; @@ -57,17 +57,20 @@ Camera = class(TObject) Thing = class; Ray = class; -Intersection = class(TObject) +Intersection = record thing: Thing; ray: Ray; - dist: Double ; - constructor Create(thing:Thing; ray:Ray; dist:Double ); -end; + dist: Double; + constructor Create(thing:Thing; ray:Ray; dist:Double); + class function Invalid : Intersection; static; + + function IsValid : boolean; + end; Thing = class(TObject) public function intersect(ray: Ray) : Intersection; virtual; abstract; - function normal (pos: Vector): Vector;virtual; abstract; + function normal (pos: Vector): Vector; virtual; abstract; function surface:Surface;virtual; abstract; end; @@ -82,25 +85,25 @@ Ray = class(TObject) Surface = class abstract public - function diffuse(pos:Vector):Color; virtual; abstract; - function specular(pos:Vector):Color; virtual; abstract; - function reflect(pos:Vector):Double ; virtual; abstract; + function diffuse(const pos:Vector):Color; virtual; abstract; + function specular(const pos:Vector):Color; virtual; abstract; + function reflect(const pos:Vector):Double ; virtual; abstract; function roughness:Double ;virtual;abstract; end; ShinySurface = class(Surface) public - function diffuse(pos: Vector): Color; override; - function specular(pos: Vector): Color; override; - function reflect(pos: Vector): Double ; override; + function diffuse(const pos: Vector): Color; override; + function specular(const pos: Vector): Color; override; + function reflect(const pos: Vector): Double ; override; function roughness: Double ; override; end; CheckerboardSurface = class(Surface) public - function diffuse(pos: Vector): Color; override; - function specular(pos: Vector): Color; override; - function reflect(pos: Vector): Double ; override; + function diffuse(const pos: Vector): Color; override; + function specular(const pos: Vector): Color; override; + function reflect(const pos: Vector): Double ; override; function roughness: Double ; override; end; @@ -124,9 +127,8 @@ RayTracerEngine = class function traceRay(ray: Ray; scene: Scene; depth: integer): Color; function shade(isect: Intersection; scene: Scene; depth: integer):Color; - function getReflectionColor(thing: Thing; pos: Vector; normal: - Vector; rd: Vector; scene: Scene; depth: integer):Color; - function getNaturalColor(thing: Thing; pos: Vector; norm: Vector; rd: Vector; scene: Scene):Color; + function getReflectionColor(thing: Thing; const pos, normal, rd: Vector; scene: Scene; depth: integer):Color; + function getNaturalColor(thing: Thing; const pos, norm, rd: Vector; scene: Scene):Color; procedure render(scene:Scene;img:Graphics.TBitmap); end; @@ -175,14 +177,14 @@ constructor Vector.Create(x,y,z:Double ); self.z := z; end; -function Vector.cross(v: Vector): Vector; +function Vector.cross(const v: Vector): Vector; begin Result.x := y * v.z - z * v.y; Result.y := z * v.x - x * v.z; Result.z := x * v.y - y * v.x; end; -function Vector.dot(v: Vector): Double ; +function Vector.dot(const v: Vector): Double ; begin Result := x * v.x + y * v.y + @@ -194,7 +196,7 @@ function Vector.mag: Double ; Result := Sqrt(x * x + y * y + z * z); end; -class operator Vector.Multiply(k: Double ; b: Vector): Vector; +class operator Vector.Multiply(k: Double ;const b: Vector): Vector; begin Result.x := k * b.x; Result.y := k * b.y; @@ -215,14 +217,14 @@ function Vector.norm: Vector; Result := divBy * self; end; -class operator Vector.Subtract(a, b: Vector): Vector; +class operator Vector.Subtract(const a, b: Vector): Vector; begin Result.x := a.x - b.x; Result.y := a.y - b.y; Result.z := a.z - b.z; end; -class operator Vector.Add(a, b: Vector): Vector; +class operator Vector.Add(const a, b: Vector): Vector; begin Result.x := a.x + b.x; Result.y := a.y + b.y; @@ -231,14 +233,14 @@ function Vector.norm: Vector; { Color } -class operator Color.Add(a, b: Color): Color; +class operator Color.Add(const a, b: Color): Color; begin Result.r := a.r + b.r; Result.g := a.g + b.g; Result.b := a.b + b.b; end; -class operator Color.Multiply(k: Double ; b: Color): Color; +class operator Color.Multiply(k: Double ; const b: Color): Color; begin Result.r := k*b.r; Result.g := k*b.g; @@ -260,7 +262,7 @@ constructor Color.Create(r, g, b: Double ); self.b := b; end; -class operator Color.Multiply(a, b: Color): Color; +class operator Color.Multiply(const a, b: Color): Color; begin Result.r := a.r * b.r; Result.g := a.g * b.g; @@ -272,6 +274,7 @@ function Color.ToDrawingColor: RGBColor; Result.r := Color.Clamp(self.r * 255.0); Result.g := Color.Clamp(self.g * 255.0); Result.b := Color.Clamp(self.b * 255.0); + Result.a := 255; end; { Camera } @@ -315,6 +318,18 @@ constructor Intersection.Create(thing: Thing; ray: Ray; dist: Double ); self.dist := dist; end; +class function Intersection.Invalid : Intersection; +begin + result.thing := nil; + result.ray := nil; + result.dist := 0; +end; + +function Intersection.IsValid : boolean; +begin + result := Assigned(self.thing); +end; + { Light } constructor Light.Create(pos: Vector; color: Color); @@ -351,7 +366,7 @@ function Sphere.intersect(ray: Ray): Intersection; end; if (dist = 0) then - Result := nil + Result := Intersection.Invalid() else begin Result := Intersection.Create(self, ray, dist); @@ -371,12 +386,12 @@ function Sphere.surface: Surface; { ShinySurface } -function ShinySurface.diffuse(pos: Vector): Color; +function ShinySurface.diffuse(const pos: Vector): Color; begin Result := Color.white; end; -function ShinySurface.reflect(pos: Vector): Double ; +function ShinySurface.reflect(const pos: Vector): Double ; begin Result := 0.7; end; @@ -386,13 +401,13 @@ function ShinySurface.roughness: Double ; Result := 250; end; -function ShinySurface.specular(pos: Vector): Color; +function ShinySurface.specular(const pos: Vector): Color; begin Result := Color.grey; end; { CheckerboardSurface } -function CheckerboardSurface.diffuse(pos: Vector): Color; +function CheckerboardSurface.diffuse(const pos: Vector): Color; begin if ((floor(pos.z) + floor(pos.x)) mod 2) <> 0 then Result := Color.white @@ -400,7 +415,7 @@ function CheckerboardSurface.diffuse(pos: Vector): Color; Result := Color.black; end; -function CheckerboardSurface.reflect(pos: Vector): Double ; +function CheckerboardSurface.reflect(const pos: Vector): Double ; begin if ((floor(pos.z) + floor(pos.x)) mod 2) <> 0 then Result := 0.1 @@ -413,7 +428,7 @@ function CheckerboardSurface.roughness: Double ; Result := 150.0; end; -function CheckerboardSurface.specular(pos: Vector): Color; +function CheckerboardSurface.specular(const pos: Vector): Color; begin Result := Color.white; end; @@ -434,7 +449,7 @@ function Plane.intersect(ray: Ray): Intersection; denom := self.norm.dot(ray.dir); if (denom > 0) then begin - Result := nil; + Result := Intersection.Invalid(); end else begin dist := (norm.dot(ray.start) + offset) / (-denom); @@ -460,11 +475,15 @@ constructor RayTracerEngine.Create; Self.maxDepth := 5; end; -function RayTracerEngine.getNaturalColor(thing: Thing; pos, norm, rd: Vector; +function RayTracerEngine.getNaturalColor(thing: Thing; const pos, norm, rd: Vector; scene: Scene): Color; + +var + resultColor:Color; + item: Light; + diffuseColor, specularColor: Color; - - function addLight(col:Color;light:Light):Color; + function addLight(light:Light): Color; var ldis, livec :Vector; testRay:Ray; @@ -486,7 +505,7 @@ function RayTracerEngine.getNaturalColor(thing: Thing; pos, norm, rd: Vector; else isInShadow := (neatIsect <= ldis.mag); - if isInShadow then exit(col); + if isInShadow then exit(); illum := livec.dot(norm); if illum > 0 then @@ -501,28 +520,25 @@ function RayTracerEngine.getNaturalColor(thing: Thing; pos, norm, rd: Vector; else scolor := Color.defaultColor; - Result := col + ((thing.surface.diffuse(pos) * lcolor) + - (thing.surface.specular(pos) * scolor)); + resultColor := resultColor + ((diffuseColor * lcolor) + + (specularColor * scolor)); end; -var - c:Color; - i,n:Integer; - lights:TList; + begin - c := Color.defaultColor; - n := scene.lights.Count -1; - lights := scene.lights; + resultColor := Color.defaultColor; + diffuseColor := thing.surface.diffuse(pos); + specularColor := thing.surface.specular(pos); - for i:= 0 to n do + for item in scene.lights do begin - c := addLight(c, lights.Items[i] ); + addLight(item); end; - Result := c; + Result := resultColor; end; -function RayTracerEngine.getReflectionColor(thing: Thing; pos, normal, +function RayTracerEngine.getReflectionColor(thing: Thing; const pos, normal, rd: Vector; scene: Scene; depth: integer): Color; var r:Ray; @@ -539,21 +555,20 @@ function RayTracerEngine.intersections(ray: Ray; scene: Scene): Intersection; closest:Double ; inter,closestInter: Intersection; i,n:Integer; - + item:Thing; begin closest := MaxInt; - closestInter := nil; - n := scene.things.Count-1; - + closestInter := Intersection.Invalid(); + + n := scene.things.Count - 1; + for i := 0 to n do begin - inter := scene.things.Items[i].intersect(ray); - if (inter <> nil) and (inter.dist < closest) then + inter := scene.things[i].intersect(ray); + if (inter.IsValid()) and (inter.dist < closest) then begin - if closestInter <> nil then closestInter.Free; - - closestInter := inter; - closest := inter.dist; + closestInter := inter; + closest := inter.dist; end; end; result := closestInter; @@ -578,8 +593,8 @@ procedure RayTracerEngine.render(scene: Scene; img: Graphics.TBitmap); end; begin - w := img.Width -1; - h := img.Height -1; + w := img.Width - 1; + h := img.Height - 1; start := img.ScanLine[0]; stride := integer(img.ScanLine[1]) - integer(img.ScanLine[0]); @@ -633,7 +648,7 @@ function RayTracerEngine.testRay(ray: Ray; scene: Scene): Double ; isect: Intersection; begin isect := self.intersections(ray, scene); - if isect <> nil then + if isect.IsValid() then begin exit(isect.dist); end; @@ -645,14 +660,13 @@ function RayTracerEngine.traceRay(ray: Ray; scene: Scene; depth: integer): Color isect:Intersection; begin isect := self.intersections(ray, scene); - if (isect = nil) then + if (isect.IsValid()) then begin - Result := Color.background; + Result := self.shade(isect, scene, depth); end else begin - Result := self.shade(isect, scene, depth); + Result := Color.background; end; - isect.Free; end; { Scene } @@ -679,7 +693,6 @@ constructor Scene.Create; lights.Add(Light.Create(Vector.Create(0.0, 3.5, 0.0), Color.Create(0.21, 0.21, 0.35))); self.xcamera := Camera.Create(Vector.Create(3.0, 2.0, 4.0), Vector.Create(-1.0, 0.5, 0.0)); - end; initialization diff --git a/delphi/run.bat b/delphi/run.bat index fad6185..9105c17 100644 --- a/delphi/run.bat +++ b/delphi/run.bat @@ -1,4 +1,4 @@ set outputDir=bin if not exist %outputDir% (mkdir %outputDir%) -"C:\Program Files (x86)\Embarcadero\RAD Studio\7.0\bin\dcc32.exe" -Ebin DelphiRayTracer.dpr +"C:\Program Files (x86)\Embarcadero\RAD Studio\7.0\bin\dcc32.exe" -Q+ -Ebin DelphiRayTracer.dpr bin\DelphiRayTracer.exe \ No newline at end of file diff --git a/go/RayTracer.go b/go/RayTracer.go new file mode 100644 index 0000000..326ff9d --- /dev/null +++ b/go/RayTracer.go @@ -0,0 +1,534 @@ +package main; + +import "fmt" +import "time" +import "math" +import "image" +import "image/color" +import "image/png" +import "bufio" +import "os" + +type Vector struct { + x float64; + y float64; + z float64; +} + +type Color struct { + r float64; + g float64; + b float64; +} + +type Ray struct { + start Vector; + dir Vector; +} + +type Intersection struct { + thing Thing; + ray *Ray; + dist float64; +} + +type Colors struct { + white Color; + grey Color; + black Color; + background Color; + defaultColor Color; +} + +func (isect *Intersection) isNull() bool { + return isect.ray == nil; +} + +func (v Vector) mul(k float64) Vector { + return Vector{k * v.x, k * v.y, k * v.z}; +} + +func (v Vector) add(v2 Vector) Vector { + return Vector{v.x + v2.x, v.y + v2.y, v.z + v2.z}; +} + +func (v Vector) sub(v2 Vector) Vector { + return Vector{v.x - v2.x, v.y - v2.y, v.z - v2.z}; +} + +func (v Vector) dot(v2 Vector) float64 { + return v.x * v2.x + v.y * v2.y + v.z * v2.z; +} + +func (v Vector) mag() float64 { + return math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z); +} + +func (v Vector) norm() Vector { + var magnitude = v.mag(); + var div float64; + + if (magnitude == 0) { + div = math.Inf(1); + } else { + div = 1.0 / magnitude; + } + return v.mul(div); +} + +func (v Vector) cross(v2 Vector) Vector { + return Vector{v.y * v2.z - v.z * v2.y, + v.z * v2.x - v.x * v2.z, + v.x * v2.y - v.y * v2.x}; +} + + +func (c Color) scale(k float64) Color { + return Color{k * c.r, k * c.g, k * c.b}; +} + +func (c Color) add(color Color) Color { + return Color{c.r + color.r, c.g + color.g, c.b + color.b}; +} + +func (c Color) toDrawingColor() color.Color { + return color.RGBA{legalize(c.r), legalize(c.g), legalize(c.b), 255} +} + +func (c Color) times(color Color) Color { + return Color{c.r * color.r, c.g * color.g, c.b * color.b}; +} + + +func legalize(c float64) uint8 { + if c < 0.0 { return 0;} + if c > 1.0 { return 255;} + return byte(c * 255); +} + +var gColors Colors; + +func init() { + gColors.white = Color{1.0, 1.0, 1.0}; + gColors.grey = Color{0.5, 0.5, 0.5}; + gColors.black = Color{0.0, 0.0, 0.0}; + gColors.background = gColors.black; + gColors.defaultColor = gColors.black; +} + +func main() { + fmt.Println("Starting"); + start := time.Now() + + width := 500 + height := 500 + img := image.NewRGBA(image.Rect(0, 0, width, height)) + + + var scene = CreateDefaultScene() + var rayTracer = RayTracerEngine{maxDepth:5} + + rayTracer.render(scene, img, width, height) + + elapsed := time.Since(start) + + var file, _ = os.Create("go-raytracer.png") + var writer = bufio.NewWriter(file) + + png.Encode(writer, img) + + writer.Flush() + + fmt.Printf("Completed in %s", elapsed) +} + +type Camera struct { + forward Vector; + right Vector; + up Vector; + pos Vector; +} + +func CreateCamera(pos Vector, lookAt Vector) Camera { + var result = Camera{}; + result.pos = pos; + + var down = Vector{0.0, -1.0, 0.0}; + var forward = lookAt.sub(pos); + + result.forward = forward.norm(); + + var fwXdown = result.forward.cross(down); + fwXdown = fwXdown.norm() + result.right = fwXdown.mul(1.5); + + var fwXright = result.forward.cross(result.right); + fwXright = fwXright.norm() + result.up = fwXright.mul(1.5); + + return result; +} + + +type Surface interface { + diffuse(pos Vector) Color; + specular(pos Vector) Color; + reflect(pos Vector) float64; + roughness() float64 +} + +type Thing interface { + intersect(ray Ray) Intersection + normal(pos Vector) Vector + surface() Surface +} + +type Light struct { + pos Vector; + color Color; +} + +func CreateLight(pos Vector, color Color) Light { + var result Light; + result.pos = pos; + result.color = color; + return result; +} + +type Scene interface { + Things() []Thing + Lights() []Light + Camera() Camera +} + +/*Thing*/ +type Sphere struct { + radius2 float64; + center Vector; + mSurface Surface; +} + +func CreateSphere(center Vector, radius float64, surface Surface) *Sphere { + return &Sphere{ + radius * radius, + center, + surface, + }; +} + +func (sphere *Sphere) normal(pos Vector) Vector { + var diff = pos.sub(sphere.center); + + return diff.norm() +} + +func (sphere *Sphere) intersect(ray Ray) Intersection { + var result Intersection; + + var eo = sphere.center.sub(ray.start); + var v = eo.dot(ray.dir); + var dist float64; + + if (v >= 0) { + var disc = sphere.radius2 - (eo.dot(eo) - v * v); + if (disc >= 0) { + dist = v - math.Sqrt(disc); + } + } + + if (dist == 0) { + return result; + } + result = Intersection{sphere, &ray, dist}; + return result; +} + +func (sphere *Sphere) surface() Surface { + return sphere.mSurface; +} + +type Plane struct { + norm Vector; + offset float64; + mSurface Surface; +} + +func (plane *Plane) normal(pos Vector) Vector { + return plane.norm; +} + +func (plane *Plane) intersect(ray Ray) Intersection { + var result Intersection; + + var denom = plane.norm.dot(ray.dir); + if (denom > 0) { + return result; + } + var dist = (plane.norm.dot(ray.start) + plane.offset) / (-denom); + + result = Intersection{plane, &ray, dist}; + return result; +} + +func CreatePlane(norm Vector, offset float64, surface Surface) *Plane { + return &Plane{ + norm, + offset, + surface, + } +} + +func (plane *Plane) surface() Surface { + return plane.mSurface; +} + +/* ShinySurface */ +type ShinySurface struct { +} + +func (surface *ShinySurface) diffuse(pos Vector) Color{ + return gColors.white; +} + +func (surface *ShinySurface) specular(pos Vector) Color{ + return gColors.grey; +} + +func (surface *ShinySurface) reflect(pos Vector) float64 { + return 0.7; +} + +func (surface *ShinySurface) roughness() float64 { + return 250.0; +} + +/* CheckerboardSurface */ +type CheckerboardSurface struct { +} + +func (surface *CheckerboardSurface) diffuse(pos Vector) Color { + var val = (math.Floor(pos.z) + math.Floor(pos.x)); + if (math.Mod(val, 2.0) != 0) { + return gColors.white; + } + return gColors.black; +} + +func (surface *CheckerboardSurface) specular(pos Vector) Color{ + return gColors.white; +} + +func (surface *CheckerboardSurface) reflect(pos Vector) float64 { + var val = (math.Floor(pos.z) + math.Floor(pos.x)); + if (math.Mod(val, 2.0) != 0) { + return 0.1; + } + return 0.7; +} + +func (surface *CheckerboardSurface) roughness() float64 { + return 150.0; +} + + +type DefaultScene struct { + things []Thing; + lights []Light; + camera Camera; +} + +func CreateDefaultScene() *DefaultScene { + var result = &DefaultScene{}; + var shiny = &ShinySurface{}; + var checkerboard = &CheckerboardSurface{}; + + var plane1 = CreatePlane(Vector{0.0, 1.0, 0.0}, 0.0, checkerboard) + var sphere1 = CreateSphere(Vector{0.0, 1.0, -0.25}, 1.0, shiny) + var sphere2 = CreateSphere(Vector{-1.0, 0.5, 1.5}, 0.5, shiny) + + + result.things = []Thing{plane1, sphere1, sphere2} + + result.lights = []Light{ + CreateLight(Vector{-2.0, 2.5, 0.0}, Color{0.49, 0.07, 0.07}), + CreateLight(Vector{1.5, 2.5, 1.5}, Color{0.07, 0.07, 0.49}), + CreateLight(Vector{1.5, 2.5, -1.5}, Color{0.07, 0.49, 0.071}), + CreateLight(Vector{0.0, 3.5, 0.0}, Color{0.21, 0.21, 0.35}), + } + + result.camera = CreateCamera(Vector{3.0, 2.0, 4.0}, Vector{-1.0, 0.5, 0.0}); + return result; +} + +func (scene *DefaultScene) Things() []Thing { + return scene.things +} + +func (scene *DefaultScene) Lights() []Light { + return scene.lights; +} + +func (scene *DefaultScene) Camera() Camera { + return scene.camera; +} + +type RayTracerEngine struct { + maxDepth int +} + +func (rayTracer *RayTracerEngine) intersections(ray Ray, scene Scene) Intersection { + var closest = math.Inf(1); + var closestInter Intersection; + + for _, thing := range scene.Things() { + var inter = thing.intersect(ray); + if (!inter.isNull() && inter.dist < closest) { + closestInter = inter; + closest = inter.dist; + } + } + return closestInter; +} + +func (rayTracer *RayTracerEngine) testRay(ray Ray, scene Scene) float64 { + var isect = rayTracer.intersections(ray, scene); + if (!isect.isNull()) { + return isect.dist; + } + return math.NaN() +} + +func (rayTracer *RayTracerEngine) traceRay(ray Ray, scene Scene, depth int) Color { + var isect = rayTracer.intersections(ray, scene); + if (isect.isNull()) { + return gColors.background; + } + return rayTracer.shade(isect, scene, depth); +} + +func (rayTracer *RayTracerEngine) shade(isect Intersection, scene Scene, depth int) Color { + var d = isect.ray.dir; + var pos = d.mul(isect.dist); + pos = pos.add(isect.ray.start); + var normal = isect.thing.normal(pos); + + var normalDotD = normal.dot(d) + var vec = normal.mul(normalDotD) + vec = vec.mul(2.0) + + var reflectDir = d.sub(vec); + + var naturalColor = rayTracer.getNaturalColor(isect.thing, pos, normal, reflectDir, scene) + naturalColor = naturalColor.add(gColors.background); + + getReflectionColor := func() Color { + var ray = Ray{pos, reflectDir}; + var reflect = isect.thing.surface().reflect(pos); + var color = rayTracer.traceRay(ray, scene, depth + 1) + color = color.scale(reflect); + return color; + } + + var reflectedColor Color; + if (depth >= rayTracer.maxDepth) { + reflectedColor = gColors.grey; + } else { + reflectedColor = getReflectionColor(); + } + var resultColor = naturalColor.add(reflectedColor); + return resultColor; +} + +func (rayTracer *RayTracerEngine) getNaturalColor(thing Thing, pos Vector, norm Vector, rd Vector, scene Scene) Color { + var resultColor = gColors.black + var surface = thing.surface() + var rayDirNormal = rd.norm() + + var colDiffuse = surface.diffuse(pos) + var colSpecular = surface.specular(pos) + + var lcolor Color + var scolor Color + + var ray Ray + ray.start = pos + + addLight := func (light Light) { + var ldis = light.pos.sub(pos); + var livec = ldis.norm(); + + var neatIsect = rayTracer.testRay(Ray{pos, livec} , scene) + var isInShadow bool + if math.IsNaN(neatIsect) { + isInShadow = false + } else { + isInShadow = neatIsect <= ldis.mag() + } + + if (isInShadow) { + return; + } + + var illum = livec.dot(norm) + var specular = livec.dot(rayDirNormal) + + lcolor = gColors.defaultColor + scolor = gColors.defaultColor + + if illum > 0 { + lcolor = light.color.scale(illum); + } + if specular > 0 { + scolor = light.color.scale(math.Pow(specular, surface.roughness())); + } + + var diffuseColor = lcolor.times(colDiffuse); + var specularColor = scolor.times(colSpecular); + + resultColor = resultColor.add(diffuseColor) + resultColor = resultColor.add(specularColor) + } + + for _, light := range scene.Lights() { + addLight(light); + } + + return resultColor; +} + + +func (rayTracer *RayTracerEngine) getPoint(x int, y int, camera Camera, screenWidth int, screenHeight int, scale int) Vector { + var recenterX = (float64(x) - (float64(screenWidth) / 2.0)) / 2.0 / float64(scale); + var recenterY = -(float64(y) - (float64(screenHeight) / 2.0)) / 2.0 / float64(scale); + + var vx = camera.right.mul(recenterX); + var vy = camera.up.mul(recenterY); + var v = vx.add(vy); + + var z = camera.forward.add(v) + + + z = z.norm() + return z; +} + +func (rayTracer *RayTracerEngine) render(scene Scene, img *image.RGBA, w int, h int) { + var camera = scene.Camera(); + var ray Ray; + ray.start = camera.pos; + + var scale = h; + if scale > w { + scale = w; + } + + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + var dir = rayTracer.getPoint(x, y, camera, w, h, scale); + ray.dir = dir; + + color := rayTracer.traceRay(ray, scene, 0); + img.Set (x, y, color.toDrawingColor()) + } + } +} \ No newline at end of file diff --git a/php/RayTracer.php b/php/RayTracer.php index ac83730..3ab34f9 100644 --- a/php/RayTracer.php +++ b/php/RayTracer.php @@ -1,6 +1,40 @@ img = imagecreatetruecolor($w, $h); + } + + public function __destruct() + { + imagedestroy($this->img); + } + + public function setPixel($x, $y, RGBColor $color) + { + $gdColor = imagecolorallocate($this->img, $color->r, $color->g, $color->b); + imagesetpixel($this->img, $x, $y, $gdColor); + imagecolordeallocate($this->img, $gdColor); + } + + public function saveAsPng($fileName) + { + imagepng($this->img, $fileName); + } +} class RayTracer { @@ -8,22 +42,22 @@ public static function run() { $w = 500; $h = 500; - - $img = imagecreatetruecolor($w, $h); - $t1 = microtime(true); - $s = new DefaultScene(); - $rte = new RayTracerEngine(); - $rte->render($s, $img, $w, $h); - $t2 = microtime(true); - - imagepng($img, "php-ray-tracer.png"); + $image = new Image($w, $h); + + $t1 = microtime(true); + $scene = new DefaultScene(); + $rayTracer = new RayTracerEngine(); + $rayTracer->render($scene, $image, $w, $h); + $t2 = microtime(true); + + $image->saveAsPng("php-ray-tracer.png"); $t = $t2 - $t1; + echo "Rendered in $t seconds, image size is $w x $h \n"; } } - class Vector { public $x; @@ -37,22 +71,40 @@ public function __construct($x, $y, $z) $this->z = $z; } - public static function times($k,Vector $v) { return new Vector($k * $v->x, $k * $v->y, $k * $v->z); } - public static function minus(Vector $v1,Vector $v2) { return new Vector($v1->x - $v2->x, $v1->y - $v2->y, $v1->z - $v2->z); } - public static function plus(Vector $v1,Vector $v2) { return new Vector($v1->x + $v2->x, $v1->y + $v2->y, $v1->z + $v2->z); } - public static function dot(Vector $v1, Vector $v2) { return $v1->x * $v2->x + $v1->y * $v2->y + $v1->z * $v2->z; } + public static function times($k,Vector $v) + { + return new Vector($k * $v->x, $k * $v->y, $k * $v->z); + } - public static function mag(Vector $v) { - return sqrt( $v->x * $v->x + $v->y * $v->y + $v->z * $v->z); + public static function minus(Vector $v1,Vector $v2) + { + return new Vector($v1->x - $v2->x, $v1->y - $v2->y, $v1->z - $v2->z); } - public static function norm(Vector $v) { + public static function plus(Vector $v1,Vector $v2) + { + return new Vector($v1->x + $v2->x, $v1->y + $v2->y, $v1->z + $v2->z); + } + + public static function dot(Vector $v1, Vector $v2) + { + return $v1->x * $v2->x + $v1->y * $v2->y + $v1->z * $v2->z; + } + + public static function mag(Vector $v) + { + return sqrt($v->x * $v->x + $v->y * $v->y + $v->z * $v->z); + } + + public static function norm(Vector $v) + { $mag = Vector::mag($v); - $div = ($mag == 0) ? INFINITY : 1.0 / $mag; + $div = ($mag == 0) ? INF : 1.0 / $mag; return Vector::times($div, $v); } - public static function cross(Vector $v1,Vector $v2) { + public static function cross(Vector $v1,Vector $v2) + { return new Vector($v1->y * $v2->z - $v1->z * $v2->y, $v1->z * $v2->x - $v1->x * $v2->z, $v1->x * $v2->y - $v1->y * $v2->x); @@ -71,32 +123,42 @@ class Color public static $background; public static $defaultColor; - public function __construct($r,$g,$b) { + public function __construct($r,$g,$b) + { $this->r = $r; $this->g = $g; $this->b = $b; } + + public static function scale($k,Color $v) + { + return new Color($k * $v->r, $k * $v->g, $k * $v->b); + } + public static function plus(Color $v1, Color $v2) + { + return new Color($v1->r + $v2->r, $v1->g + $v2->g, $v1->b + $v2->b); + } - public static function scale($k,Color $v) { return new Color($k * $v->r, $k * $v->g, $k * $v->b); } - public static function plus(Color $v1, Color $v2) { return new Color($v1->r + $v2->r, $v1->g + $v2->g, $v1->b + $v2->b); } - public static function times(Color $v1, Color $v2) { return new Color($v1->r * $v2->r, $v1->g * $v2->g, $v1->b * $v2->b); } + public static function times(Color $v1, Color $v2) + { + return new Color($v1->r * $v2->r, $v1->g * $v2->g, $v1->b * $v2->b); + } public static function toDrawingColor(Color $c) { - $r = self::legalize($c->r); - $g = self::legalize($c->g); - $b = self::legalize($c->b); - - return [$r,$g, $b]; + $color = new RGBColor; + $color->r = self::legalize($c->r); + $color->g = self::legalize($c->g); + $color->b = self::legalize($c->b); + return $color; } static function legalize($c) { - $x = (int)($c*255); - if ($x < 0) $x = 0; - if ($x > 255) $x = 255; - return $x; + if ($c < 0) $c = 0; + if ($c > 1) $c = 1; + return (int)($c*255); } } @@ -115,11 +177,11 @@ class Camera public function __construct(Vector $pos, Vector $lookAt) { - $this->pos = $pos; - $down = new Vector(0.0, -1.0, 0.0); + $this->pos = $pos; + $down = new Vector(0.0, -1.0, 0.0); $this->forward = Vector::norm(Vector::minus($lookAt, $this->pos)); - $this->right = Vector::times(1.5, Vector::norm(Vector::cross($this->forward, $down))); - $this->up = Vector::times(1.5, Vector::norm(Vector::cross($this->forward, $this->right))); + $this->right = Vector::times(1.5, Vector::norm(Vector::cross($this->forward, $down))); + $this->up = Vector::times(1.5, Vector::norm(Vector::cross($this->forward, $this->right))); } } @@ -135,7 +197,6 @@ public function __construct(Vector $start=null, Vector $dir=null) } } - class Intersection { public $thing; @@ -150,25 +211,24 @@ public function __construct(Thing $thing, Ray $ray, $dist) } } - -interface Surface { - function diffuse (Vector $pos); +interface Surface +{ + function diffuse(Vector $pos); function specular(Vector $pos); function reflect(Vector $pos); function roughness(); } - -interface Thing { +interface Thing +{ /** @return Intersection */ - function intersect(Ray $ray); + function intersect(Ray $ray); /** @return Vector */ function normal (Vector $pos); /** @return Surface */ function surface(); } - class Light { public $pos; @@ -180,7 +240,8 @@ public function __construct(Vector $pos, Color $color) } } -interface Scene { +interface Scene +{ public function things(); public function lights(); public function camera(); @@ -198,8 +259,13 @@ public function __construct(Vector $center, $radius, Surface $surface) $this->center = $center; $this->surface = $surface; } - public function normal(Vector $pos) {return Vector::norm(Vector::minus($pos, $this->center));} - public function intersect(Ray $ray) + + public function normal(Vector $pos) + { + return Vector::norm(Vector::minus($pos, $this->center)); + } + + public function intersect(Ray $ray) { $eo = Vector::minus($this->center, $ray->start); $v = Vector::dot($eo, $ray->dir); @@ -210,14 +276,15 @@ public function intersect(Ray $ray) $dist = $v - sqrt($disc); } } + if ($dist == 0) { return null; - } else { - return new Intersection($this, $ray, $dist); } + return new Intersection($this, $ray, $dist); } - public function surface() { + public function surface() + { return $this->surface; } } @@ -236,12 +303,11 @@ public function normal(Vector $pos) public function intersect(Ray $ray) { $denom = Vector::dot($this->norm, $ray->dir); - if ($denom > 0){ + if ($denom > 0) { return null; - } else { - $dist = (Vector::dot($this->norm, $ray->start) + $this->offset) / (-$denom); - return new Intersection($this, $ray, $dist); - } + } + $dist = (Vector::dot($this->norm, $ray->start) + $this->offset) / (-$denom); + return new Intersection($this, $ray, $dist); } public function __construct(Vector $norm, $offset, $surface) @@ -251,12 +317,12 @@ public function __construct(Vector $norm, $offset, $surface) $this->offset = $offset; } - public function surface() { + public function surface() + { return $this->surface; } } - class Surfaces { public static $shiny; @@ -265,55 +331,59 @@ class Surfaces class ShinySurface implements Surface { - public function diffuse(Vector $pos) { + public function diffuse(Vector $pos) + { return Color::$white; } - public function specular(Vector $pos) { + public function specular(Vector $pos) + { return Color::$grey; } - public function reflect(Vector $pos) { + public function reflect(Vector $pos) + { return 0.7; } - public function roughness() { + public function roughness() + { return 250.0; } } class CheckerboardSurface implements Surface { - public function diffuse(Vector $pos) { + public function diffuse(Vector $pos) + { if ((floor($pos->z) + floor($pos->x)) % 2 != 0) { return Color::$white; - } else { - return Color::$black; } + return Color::$black; } - public function specular(Vector $pos) { + public function specular(Vector $pos) + { return Color::$white; } - public function reflect(Vector $pos) { + public function reflect(Vector $pos) + { if ((floor($pos->z) + floor($pos->x)) % 2 != 0) { return 0.1; - } else { - return 0.7; } + return 0.7; } - public function roughness() { + public function roughness() + { return 150.0; } } - Surfaces::$shiny = new ShinySurface(); Surfaces::$checkerboard = new CheckerboardSurface(); - class DefaultScene implements Scene { private $things; @@ -327,8 +397,7 @@ public function __construct() new Sphere(new Vector(0.0, 1.0, -0.25), 1.0, Surfaces::$shiny), new Sphere(new Vector(-1.0, 0.5, 1.5), 0.5, Surfaces::$shiny) ]; - - + $this->lights = [ new Light(new Vector(-2.0, 2.5, 0.0), new Color(0.49, 0.07, 0.07)), new Light(new Vector(1.5, 2.5, 1.5), new Color(0.07, 0.07, 0.49)), @@ -339,31 +408,35 @@ public function __construct() $this->camera= new Camera(new Vector(3.0, 2.0, 4.0), new Vector(-1.0, 0.5, 0.0)); } - public function things() { + public function things() + { return $this->things; } - public function lights() { + public function lights() + { return $this->lights; } - public function camera() { + public function camera() + { return $this->camera; } } - class RayTracerEngine { private $maxDepth = 5; - private function intersections(Ray $ray,Scene $scene) { - $closest = INFINITY; + private function intersections(Ray $ray,Scene $scene) + { + $closest = INF; $closestInter = null; foreach ($scene->things() as $thing) { $inter = $thing->intersect($ray); - if ($inter != null && $inter->dist < $closest) { + if ($inter != null && $inter->dist < $closest) + { $closestInter = $inter; $closest = $inter->dist; } @@ -375,22 +448,18 @@ private function testRay(Ray $ray,Scene $scene) { $isect = $this->intersections($ray, $scene); if ($isect != null) { return $isect->dist; - } else { - return null; } + return null; } - private function traceRay(Ray $ray, Scene $scene, $depth) { $isect = $this->intersections($ray, $scene); if ($isect == null) { return Color::$background; - } else { - return $this->shade($isect, $scene, $depth); } + return $this->shade($isect, $scene, $depth); } - private function shade(Intersection $isect,Scene $scene, $depth) { $d = $isect->ray->dir; @@ -403,70 +472,67 @@ private function shade(Intersection $isect,Scene $scene, $depth) return Color::plus($naturalColor, $reflectedColor); } - - private function getReflectionColor(Thing $thing,Vector $pos,Vector $normal,Vector $rd, Scene $scene,$depth) + private function getReflectionColor(Thing $thing, Vector $pos, Vector $normal, Vector $reflectDir, Scene $scene,$depth) { - return Color::scale($thing->surface()->reflect($pos), $this->traceRay(new Ray($pos, $rd), $scene, $depth + 1)); + return Color::scale($thing->surface()->reflect($pos), $this->traceRay(new Ray($pos, $reflectDir), $scene, $depth + 1)); } - private function getNaturalColor(Thing $thing, Vector $pos,Vector $norm,Vector $rd,Scene $scene) + private function getNaturalColor(Thing $thing, Vector $pos, Vector $norm, Vector $reflectDir, Scene $scene) { - $c = Color::$black; + $natColor = Color::$black; foreach ($scene->lights() as $item) { - $c = self::addLight($c, $item, $pos, $scene, $thing, $rd, $norm); + $natColor = self::addLight($natColor, $item, $pos, $scene, $thing, $reflectDir, $norm); } - return $c; + return $natColor; } - public function addLight(Color $col, Light $light, Vector $pos, Scene $scene, Thing $thing, Vector $rd, Vector $norm) + public function addLight(Color $col, Light $light, Vector $pos, Scene $scene, Thing $thing, Vector $reflectDir, Vector $norm) { $ldis = Vector::minus($light->pos, $pos); $livec = Vector::norm($ldis); $neatIsect = $this->testRay(new Ray($pos, $livec), $scene); - $isInShadow = ($neatIsect == null) ? false : ($neatIsect <= Vector::mag($ldis)); + $isInShadow = (($neatIsect != null) && ($neatIsect <= Vector::mag($ldis))); if ($isInShadow) { return $col; - } else { - $illum = Vector::dot($livec, $norm); - $lcolor = ($illum > 0) ? Color::scale($illum, $light->color) : Color::$defaultColor; - $specular = Vector::dot($livec, Vector::norm($rd)); - - $scolor = ($specular > 0) ? Color::scale(pow($specular, $thing->surface()->roughness()), $light->color) - : Color::$defaultColor; - - return Color::plus($col, Color::plus(Color::times($thing->surface()->diffuse($pos), $lcolor), - Color::times($thing->surface()->specular($pos), $scolor))); - } - } + } + + $illum = Vector::dot($livec, $norm); + $lcolor = Color::$defaultColor; + + if ($illum > 0) { + $lcolor = Color::scale($illum, $light->color); + } + + $specular = Vector::dot($livec, Vector::norm($reflectDir)); + $scolor = Color::$defaultColor; + $surface = $thing->surface(); + + if ($specular > 0) { + $scolor = Color::scale(pow($specular, $surface->roughness()), $light->color); + } + + return Color::plus($col, Color::plus(Color::times($surface->diffuse($pos), $lcolor), + Color::times($surface->specular($pos), $scolor))); + } - public function render(Scene $scene, $img, $w, $h) + public function render(Scene $scene, Image $image, $w, $h) { $camera = $scene->camera(); $camPos = $camera->pos; - for ($y = 0; $y < $h ; ++$y) + for ($y = 0; $y < $h; ++$y) + for ($x = 0; $x < $w; ++$x) { - for ($x = 0; $x < $w; ++$x) - { - $ray = new Ray( - $camPos, - self::getPoint($x, $y, $camera, $h, $w) - ); - - $color = $this->traceRay($ray, $scene, 0); - - $c = Color::toDrawingColor($color); - $imgColor = imagecolorallocate($img, $c[0], $c[1], $c[2]); - imagesetpixel($img, $x, $y, $imgColor); - imagecolordeallocate($img, $imgColor); - } + $ray = new Ray($camPos, self::getPoint($x, $y, $camera, $w, $h)); + $color = Color::toDrawingColor($this->traceRay($ray, $scene, 0)); + $image->setPixel($x, $y, $color); } } - static function getPoint($x, $y,Camera $camera, $screenWidth, $screenHeight) + static function getPoint($x, $y, Camera $camera, $screenWidth, $screenHeight) { $recenterX = ($x - ($screenWidth / 2.0)) / 2.0 / $screenWidth; $recenterY = - ($y - ($screenHeight / 2.0)) / 2.0 / $screenHeight; @@ -479,5 +545,4 @@ static function getPoint($x, $y,Camera $camera, $screenWidth, $screenHeight) } } -$rt = new RayTracer(); -$rt->run(); \ No newline at end of file +RayTracer::run(); \ No newline at end of file diff --git a/readme.md b/readme.md index 8d2cd95..afaba50 100644 --- a/readme.md +++ b/readme.md @@ -7,12 +7,13 @@ Raytracer benchmarks based on [Typescript](http://www.typescriptlang.org) sample Language / Compiler | Time [ms] ------------------------- | ------------- C++ (Visual C++ 17.0) | 410 ms +C++ (GCC 5.2) | 430 ms +D (DMD 2.070) | 515 ms C# | 600 ms Java 9 | 650 ms VB.NET 12 | 650 ms -C++ (GCC 5.2) | 1020 ms -Delphi 2010 | 1330 ms +Delphi 2010 | 950 ms +Go | 1050 ms PHP (PHP 7.0) | 25500 ms Ruby 2.2 | 47800 ms -Python 3.5 | 68000 ms - +Python 3.5 | 68000 ms \ No newline at end of file