From ddab323e4abc3878efdfe6bb550d6ae5a6253634 Mon Sep 17 00:00:00 2001 From: Veselin Georgiev Date: Wed, 6 Jan 2016 15:29:27 +0200 Subject: [PATCH] Changes in lectures 10 and 11. Added: - Lights (we can have multiple lights). - Light sampling (soft lights), used in RectLight. - Depth-of-field (camera effect). - Stereoscopy (camera effect), for use with anaglyph glasses. --- data/boxed.qdmg | 20 ++--- data/meshes.qdmg | 197 +++++++++++++++++++++++++++++++++++++++++++++++ data/zaphod.qdmg | 10 +-- src/camera.cpp | 42 +++++++++- src/camera.h | 36 ++++++++- src/lights.cpp | 26 +++++++ src/lights.h | 95 +++++++++++++++++++++++ src/main.cpp | 76 +++++++++++++++--- src/mesh.cpp | 2 +- src/mesh.h | 3 + src/scene.cpp | 20 ++--- src/scene.h | 7 +- src/shading.cpp | 92 ++++++++++++++-------- src/util.cpp | 9 +++ src/util.h | 2 + 15 files changed, 560 insertions(+), 77 deletions(-) create mode 100644 data/meshes.qdmg create mode 100644 src/lights.cpp create mode 100644 src/lights.h diff --git a/data/boxed.qdmg b/data/boxed.qdmg index 33a49a1..a96e8a2 100644 --- a/data/boxed.qdmg +++ b/data/boxed.qdmg @@ -1,10 +1,10 @@ GlobalSettings { - frameWidth 640 - frameHeight 480 - ambientLight (0.075, 0.075, 0.075) + frameWidth 1024 + frameHeight 768 + //ambientLight (0.075, 0.075, 0.075) + ambientLight (0, 0, 0) maxTraceDepth 8 - lightPos (50, 50, -20) - lightIntensity 2048 + wantAA false } Camera camera { @@ -18,14 +18,14 @@ Camera camera { //////////////////////// LIGHTS //////////////////////////////////////////// -/* + RectLight l2 { xSubd 4 ySubd 4 translate (50, 50, -20) scale (8, 8, 8) color (1, 1, 1) - power 62.5 + power 1100.5 rotate (0, 45, 0) } @@ -33,12 +33,12 @@ RectLight l_small { translate (0, 28, 50) rotate (0, -62, 0) scale (10, 10, 10) - color (1, 1, 1) - power 40 + color (1, 0.2, 1) + power 2000 xSubd 4 ySubd 4 } -*/ + //////////////////////// GEOMETRIES //////////////////////////////////////// diff --git a/data/meshes.qdmg b/data/meshes.qdmg new file mode 100644 index 0000000..756e634 --- /dev/null +++ b/data/meshes.qdmg @@ -0,0 +1,197 @@ +// + +// 1. First, some global settings +GlobalSettings { + frameWidth 1024 + frameHeight 768 + ambientLight (0.28, 0.30, 0.35) + maxTraceDepth 5 + saturation 0.25 +} + +PointLight { + pos (160, 80, 40) + color (1, 1, 1) + power 10000 +} + +// 2. A camera +// (to enable the spherical lens camera (which introduces heavy spherical abberation)), replace "Camera" with "SphericalLensCamera" + +Camera camera { + position (120, 22, 60) + yaw 11.46 + pitch -15 + roll 0.0 + fov 90 + aspectRatio 1.333 + stereoSeparation 0.25 // comment this out to disable the stereoscopic effect + dof on + numSamples 100 + fNumber 5.6 + autofocus off + focalPlaneDist 21.48 +} + +/* +// top view +Camera camera { + pos (130, 85, 75) + yaw 11.46 + pitch -90 + roll 0.0 + fov 90 + aspect 1.333 +} +*/ + +Plane floor { + y 0 + limit 128 +} + +CheckerTexture checker { + color1 (0.5, 0.5, 0.25) + color2 (0.25, 0.25, 0.25) + scaling 0.75 +} + +Lambert floorShader { + color (1, 1, 1) + texture checker +} + +// 3. A floor node, using a plane as a geometry, and a flat shader with a checker texture +Node floorNode { + geometry floor + shader floorShader + translate (100, 0, 96) +} + + +Mesh dice { + file "geom/truncated_cube.obj" + faceted true +} + +BumpTexture diceBump { + file "texture/zar-bump.bmp" + strength 5 +} + +Mesh teapot { + file "geom/teapot_hires.obj" +} + +Mesh wineglass { + file "geom/newwine.obj" + backfaceCulling false +} + +CheckerTexture teapot_checker { + color1 (0.9, 0.1, 0.1) + color2 (0.05, 0.05, 0.07) + scaling 50 +} + +Lambert teapot_lambert { + color (1, 1, 1) + texture teapot_checker +} + +Phong white_flat { + color (0.7, 0.7, 0.7) + specularExponent 200 +} + +Refl reflection { + multiplier 0.96 +} + +Layered teapot_layered { + layer teapot_lambert (1, 1, 1) + layer white_flat (0.16, 0.16, 0.16) + layer reflection (0.05, 0.07, 0.09) +} + +BitmapTexture dice_texture { + file "texture/zar-texture.bmp" +} + +Lambert flat { + color (1,1,1) + texture dice_texture +} + +Refr refraction { + ior 1.5 + multiplier 0.96 +} + +Fresnel fresnel { + ior 1.5 +} + +Layered glass { + layer refraction (1, 1, 1) + layer reflection (1, 1, 1) fresnel +} + +Sphere ball { + R 0.5 +} + +Refl ball_sfc { + glossiness 0.92 + numSamples 10 +} + +Node ball1 { + geometry ball + shader ball_sfc + translate (125.5, 19, 81) +} + +Node ball2 { + geometry ball + shader ball_sfc + translate (126, 16, 80.7) +} + +Node ball3 { + geometry ball + shader ball_sfc + translate (126.2, 12.5, 79.5) +} + +Node wineglass { + geometry wineglass + //shader white_flat + shader glass + translate (126, 0, 80) + scale (17, 17, 17) +} + +Node dice { + geometry dice + shader flat + translate (100, 10.1, 96) + rotate (63, 0, 0) + scale (2.5, 2.5, 2.5) + bump diceBump +} + +Node teapotNode { + geometry teapot + shader teapot_layered + translate (110, 19, 90) + rotate (-27, 0, 30) + scale (7.5, 7.5, 7.5) +} + + + +// 5. The cubemap environment: +CubemapEnvironment environment { + folder "env/forest" +} diff --git a/data/zaphod.qdmg b/data/zaphod.qdmg index 7375176..d371abd 100644 --- a/data/zaphod.qdmg +++ b/data/zaphod.qdmg @@ -13,17 +13,15 @@ GlobalSettings { frameHeight 430 ambientLight (0.0, 0.0, 0.0) - lightPos (200, 200, -200) # Simulates my incadescent lamp in the center of the room - lightIntensity 100000 } -/* + PointLight l1 { pos (200, 200, -200) # Simulates my incadescent lamp in the center of the room power 100000 color (0.351, 0.332, 0.187) # Simulates the yellowish color of the incadescent lamp } -*/ + Camera camera { position (1.5, 17, -19.5) @@ -34,8 +32,8 @@ Camera camera { aspectRatio 1.5 dof on focalPlaneDist 25.29 - fNumber 11 # This is also not quite correct, the actual aperture was f/2.0 - numSamples 400 + fNumber 2.0 # This is also not quite correct, the actual aperture was f/2.0 + numSamples 100 } //////////////////////// LIGHTS //////////////////////////////////////////// diff --git a/src/camera.cpp b/src/camera.cpp index 5e157b8..7961a2f 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -21,10 +21,14 @@ * @File camera.cpp * @Brief Implementation of the raytracing camera. */ +#include #include "camera.h" #include "matrix.h" #include "util.h" #include "sdl.h" +#include "geometry.h" +#include "scene.h" +using std::min; void Camera::beginFrame() { @@ -51,12 +55,29 @@ void Camera::beginFrame() topRight *= rotation; bottomLeft *= rotation; + frontDir = Vector(0, 0, 1) * rotation; + upDir = Vector(0, 1, 0) * rotation; + rightDir = Vector(1, 0, 0) * rotation; + topLeft += this->position; topRight += this->position; bottomLeft += this->position; + + if (autofocus) { + IntersectionInfo info; + double closest = 1e99; + Ray ray = getScreenRay(scene.settings.frameWidth / 2, + scene.settings.frameHeight / 2); + for (auto& node: scene.nodes) { + if (node->intersect(ray, info)) + closest = min(closest, info.distance); + } + printf("Autofocus: found distance: %.2lf\n", closest); + focalPlaneDist = closest; + } } -Ray Camera::getScreenRay(double xScreen, double yScreen) +Ray Camera::getScreenRay(double xScreen, double yScreen, int whichCamera) { Vector throughPoint = topLeft + (topRight - topLeft) * (xScreen / frameWidth()) @@ -66,5 +87,24 @@ Ray Camera::getScreenRay(double xScreen, double yScreen) ray.dir = throughPoint - this->position; ray.dir.normalize(); ray.start = this->position; + if (whichCamera != CAMERA_CENTRAL) { + ray.start += (whichCamera == CAMERA_RIGHT ? +1 : -1) * stereoSeparation * rightDir; + } + return ray; +} + +Ray Camera::getDOFRay(double xScreen, double yScreen, int whichCamera) +{ + Ray ray = getScreenRay(xScreen, yScreen, whichCamera); + double cosTheta = dot(ray.dir, frontDir); + double M = focalPlaneDist / cosTheta; + Vector target = ray.start + ray.dir * M; + + double u, v; + genDiscPoint(apertureSize, u, v); + + ray.start = ray.start + u * upDir + v * rightDir; + ray.dir = target - ray.start; + ray.dir.normalize(); return ray; } diff --git a/src/camera.h b/src/camera.h index a3c78dc..438e755 100644 --- a/src/camera.h +++ b/src/camera.h @@ -28,20 +28,43 @@ #include "matrix.h" #include "scene.h" +enum { + CAMERA_CENTRAL, + CAMERA_LEFT, + CAMERA_RIGHT, +}; + class Camera: public SceneElement { Vector topLeft, topRight, bottomLeft; Matrix rotation; + double apertureSize; + Vector frontDir, upDir, rightDir; public: Vector position; double yaw, pitch, roll; //!< in degrees double aspectRatio; double fov; //!< in degrees + bool dof; + double fNumber; + double focalPlaneDist; + int numSamples; + bool autofocus; + double stereoSeparation; + Color leftMask, rightMask; Camera() { position.makeZero(); yaw = pitch = roll = 0; aspectRatio = 4./3.; fov = 90; + dof = false; + fNumber = 2.0; + numSamples = 32; + focalPlaneDist = 100; + autofocus = false; + stereoSeparation = 0.0; + leftMask = Color(1, 0, 0); + rightMask = Color(0, 1, 1); } void fillProperties(ParsedBlock& pb) @@ -53,12 +76,23 @@ class Camera: public SceneElement { pb.getDoubleProp("yaw", &yaw); pb.getDoubleProp("pitch", &pitch, -90, 90); pb.getDoubleProp("roll", &roll); + pb.getBoolProp("dof", &dof); + pb.getDoubleProp("fNumber", &fNumber, 0); + pb.getIntProp("numSamples", &numSamples, 1); + pb.getDoubleProp("focalPlaneDist", &focalPlaneDist, 0.1); + pb.getBoolProp("autofocus", &autofocus); + pb.getDoubleProp("stereoSeparation", &stereoSeparation, 0.0); + pb.getColorProp("leftMask", &leftMask); + pb.getColorProp("rightMask", &rightMask); + + apertureSize = 4.5 / fNumber; } ElementType getElementType() const { return ELEM_CAMERA; } void beginFrame(); - Ray getScreenRay(double xScreen, double yScreen); + Ray getScreenRay(double xScreen, double yScreen, int whichCamera = CAMERA_CENTRAL); + Ray getDOFRay(double xScreen, double yScreen, int whichCamera = CAMERA_CENTRAL); }; #endif // __CAMERA_H__ diff --git a/src/lights.cpp b/src/lights.cpp new file mode 100644 index 0000000..e65bcc8 --- /dev/null +++ b/src/lights.cpp @@ -0,0 +1,26 @@ + +#include "lights.h" +#include "util.h" + +int RectLight::getNumSamples() +{ + return xSubd * ySubd; +} + +void RectLight::getNthSample(int sampleIdx, const Vector& shadePos, + Vector& samplePos, Color& color) +{ + double x = (sampleIdx % xSubd + randomFloat()) / xSubd; + double y = (sampleIdx / xSubd + randomFloat()) / ySubd; + + samplePos = Vector(x - 0.5, 0, y - 0.5); + + Vector shadePos_LS = T.undoPoint(shadePos); + + if (shadePos_LS.y < 0) color = this->color * power; + else color.makeZero(); + + samplePos = T.point(samplePos); +} + + diff --git a/src/lights.h b/src/lights.h new file mode 100644 index 0000000..7aa0a2d --- /dev/null +++ b/src/lights.h @@ -0,0 +1,95 @@ + +/*************************************************************************** + * Copyright (C) 2009-2015 by Veselin Georgiev, Slavomir Kaslev et al * + * admin@raytracing-bg.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +/** + * @File heightfield.h + * @Brief Contains the Heightfield geometry class. + */ +#ifndef __LIGHTS_H__ +#define __LIGHTS_H__ + +#include "scene.h" +#include "transform.h" + +class Light: public SceneElement { +protected: + Color color; + float power; +public: + Light() { color = Color(1, 1, 1); power = 1; } + virtual ~Light() {} + + ElementType getElementType() const { return ELEM_LIGHT; } + + virtual int getNumSamples() = 0; + + virtual void getNthSample(int sampleIdx, const Vector& shadePos, + Vector& samplePos, Color& color) = 0; + + void fillProperties(ParsedBlock& pb) + { + pb.getColorProp("color", &color); + pb.getFloatProp("power", &power); + } +}; + +class PointLight: public Light { + Vector pos; +public: + void fillProperties(ParsedBlock& pb) + { + Light::fillProperties(pb); + pb.getVectorProp("pos", &pos); + } + + int getNumSamples() + { + return 1; + } + + void getNthSample(int sampleIdx, const Vector& shadePos, + Vector& samplePos, Color& color) + { + color = this->color * power; + samplePos = pos; + } +}; + +class RectLight: public Light { + int xSubd, ySubd; + Transform T; +public: + void fillProperties(ParsedBlock& pb) + { + Light::fillProperties(pb); + pb.getIntProp("xSubd", &xSubd, 1); + pb.getIntProp("ySubd", &ySubd, 1); + pb.getTransformProp(T); + } + + int getNumSamples(); + + void getNthSample(int sampleIdx, const Vector& shadePos, + Vector& samplePos, Color& color); + +}; + +#endif // __LIGHTS_H__ + diff --git a/src/main.cpp b/src/main.cpp index 4d38287..277f3c8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,6 +38,8 @@ Color raytrace(Ray ray) if (scene.environment) return scene.environment->getEnvironment(ray.dir); else return Color(0, 0, 0); } else { + if (ray.debug && ray.depth == 0) + printf("Found intersection at %.2lf\n", closestInfo.distance); closestInfo.rayDir = ray.dir; if (closestNode->bump) closestNode->bump->modifyNormal(closestInfo); @@ -72,9 +74,36 @@ void debugRayTrace(int x, int y) raytrace(ray); } -void render() +Color raytraceSinglePixel(double x, double y) +{ + auto getRay = scene.camera->dof ? + [](double x, double y, int whichCamera) { + return scene.camera->getDOFRay(x, y, whichCamera); + } : + [](double x, double y, int whichCamera) { + return scene.camera->getScreenRay(x, y, whichCamera); + }; + + if (scene.camera->stereoSeparation > 0) { + Ray leftRay = getRay(x, y, CAMERA_LEFT); + Ray rightRay= getRay(x, y, CAMERA_RIGHT); + Color colorLeft = raytrace(leftRay); + Color colorRight = raytrace(rightRay); + if (scene.settings.saturation != 1) { + colorLeft.adjustSaturation(scene.settings.saturation); + colorRight.adjustSaturation(scene.settings.saturation); + + } + return colorLeft * scene.camera->leftMask + + colorRight* scene.camera->rightMask; + } else { + Ray ray = getRay(x, y, CAMERA_CENTRAL); + return raytrace(ray); + } +} + +Color renderAAPixel(int x, int y) { - scene.beginFrame(); const double kernel[5][2] = { { 0.0, 0.0 }, { 0.6, 0.0 }, @@ -82,19 +111,42 @@ void render() { 0.3, 0.3 }, { 0.6, 0.6 }, }; + Color sum(0, 0, 0); + for (int i = 0; i < COUNT_OF(kernel); i++) + sum += raytraceSinglePixel(x + kernel[i][0], y + kernel[i][1]); + return sum / double(COUNT_OF(kernel)); +} + +Color renderDOFPixel(int x, int y) +{ + Color sum(0, 0, 0); + for (int i = 0; i < scene.camera->numSamples; i++) { + sum += raytraceSinglePixel(x + randomFloat(), y + randomFloat()); + } + return sum / scene.camera->numSamples; +} + +Color renderPixel(int x, int y) +{ + if (scene.camera->dof) { + return renderDOFPixel(x, y); + } else { + if (scene.settings.wantAA) { + return renderAAPixel(x, y); + } else { + return raytraceSinglePixel(x, y); + } + } +} + +void render() +{ + scene.beginFrame(); vector buckets = getBucketsList(); for (Rect& r: buckets) { for (int y = r.y0; y < r.y1; y++) for (int x = r.x0; x < r.x1; x++) { - if (scene.settings.wantAA) { - Color sum(0, 0, 0); - for (int i = 0; i < COUNT_OF(kernel); i++) - sum += raytrace(scene.camera->getScreenRay(x + kernel[i][0], y + kernel[i][1])); - vfb[y][x] = sum / double(COUNT_OF(kernel)); - } else { - Ray ray = scene.camera->getScreenRay(x, y); - vfb[y][x] = raytrace(ray); - } + vfb[y][x] = renderPixel(x, y); } if (!displayVFBRect(r, vfb)) return; } @@ -107,7 +159,7 @@ int renderSceneThread(void* /*unused*/) return 0; } -const char* DEFAULT_SCENE = "data/heightfield.qdmg"; +const char* DEFAULT_SCENE = "data/meshes.qdmg"; int main ( int argc, char** argv ) { diff --git a/src/mesh.cpp b/src/mesh.cpp index 0676c2b..aac7e47 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -151,7 +151,7 @@ bool intersectTriangleFast(const Ray& ray, const Vector& A, const Vector& B, con bool Mesh::intersectTriangle(const Ray& ray, const Triangle& t, IntersectionInfo& info) { - if (dot(ray.dir, t.gnormal) > 0) return false; + if (backfaceCulling && dot(ray.dir, t.gnormal) > 0) return false; Vector A = vertices[t.v[0]]; Vector B = vertices[t.v[1]]; Vector C = vertices[t.v[2]]; diff --git a/src/mesh.h b/src/mesh.h index 1fa3734..ee14175 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -77,10 +77,12 @@ class Mesh: public Geometry { public: bool faceted; + bool backfaceCulling; Mesh() { faceted = false; useKDTree = true; + backfaceCulling = true; kdroot = NULL; } ~Mesh(); @@ -90,6 +92,7 @@ class Mesh: public Geometry { void fillProperties(ParsedBlock& pb) { pb.getBoolProp("faceted", &faceted); + pb.getBoolProp("backfaceCulling", &backfaceCulling); char fn[256]; if (pb.getFilenameProp("file", fn)) { if (!loadFromOBJ(fn)) { diff --git a/src/scene.cpp b/src/scene.cpp index 3f11527..5315396 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -45,7 +45,7 @@ #include "mesh.h" #include "random_generator.h" #include "heightfield.h" -//#include "lights.h" +#include "lights.h" #include using std::vector; using std::string; @@ -530,7 +530,7 @@ bool DefaultSceneParser::parse(const char* filename, Scene* ss) case ELEM_NODE: s->nodes.push_back((Node*)curObj); break; case ELEM_ENVIRONMENT: s->environment = (Environment*) curObj; break; case ELEM_CAMERA: s->camera = (Camera*)curObj; break; - //case ELEM_LIGHT: s->lights.push_back((Light*) curObj); break; + case ELEM_LIGHT: s->lights.push_back((Light*) curObj); break; default: break; } } else { @@ -562,7 +562,7 @@ bool DefaultSceneParser::parse(const char* filename, Scene* ss) return false; } const int element_types_order[] = { - ELEM_SETTINGS, ELEM_CAMERA, ELEM_ENVIRONMENT, /*ELEM_LIGHT,*/ ELEM_GEOMETRY, ELEM_TEXTURE, ELEM_SHADER, ELEM_NODE, //ELEM_ATMOSPHERIC + ELEM_SETTINGS, ELEM_CAMERA, ELEM_ENVIRONMENT, ELEM_LIGHT, ELEM_GEOMETRY, ELEM_TEXTURE, ELEM_SHADER, ELEM_NODE, //ELEM_ATMOSPHERIC }; // process all parsed blocks, but first process all singletons, then process geometries first, etc. for (int ei = 0; ei < (int) (sizeof(element_types_order) / sizeof(element_types_order[0])); ei++) { @@ -769,7 +769,7 @@ Scene::~Scene() disposeArray(superNodes); disposeArray(textures); disposeArray(shaders); - //disposeArray(lights); + disposeArray(lights); if (environment) delete environment; environment = NULL; if (camera) delete camera; @@ -789,7 +789,7 @@ void Scene::beginRender() for (auto& element: shaders) element->beginRender(); for (auto& element: superNodes) element->beginRender(); for (auto& element: nodes) element->beginRender(); - //for (auto& element: lights) element->beginRender(); + for (auto& element: lights) element->beginRender(); camera->beginRender(); settings.beginRender(); if (environment) environment->beginRender(); @@ -802,7 +802,7 @@ void Scene::beginFrame() for (auto& element: shaders) element->beginFrame(); for (auto& element: superNodes) element->beginFrame(); for (auto& element: nodes) element->beginFrame(); - //for (auto& element: lights) element->beginFrame(); + for (auto& element: lights) element->beginFrame(); camera->beginFrame(); settings.beginFrame(); if (environment) environment->beginFrame(); @@ -816,6 +816,7 @@ GlobalSettings::GlobalSettings() dbg = false; maxTraceDepth = 4; ambientLight.makeZero(); + saturation = 0; } void GlobalSettings::fillProperties(ParsedBlock& pb) @@ -826,9 +827,7 @@ void GlobalSettings::fillProperties(ParsedBlock& pb) pb.getIntProp("maxTraceDepth", &maxTraceDepth); pb.getBoolProp("dbg", &dbg); pb.getBoolProp("wantAA", &wantAA); - pb.getVectorProp("lightPos", &lightPos); - pb.getColorProp("ambientLight", &ambientLight); - pb.getDoubleProp("lightIntensity", &lightIntensity); + pb.getFloatProp("saturation", &saturation, 0, 1); } SceneElement* DefaultSceneParser::newSceneElement(const char* className) @@ -856,6 +855,9 @@ SceneElement* DefaultSceneParser::newSceneElement(const char* className) if (!strcmp(className, "Bumps")) return new Bumps; if (!strcmp(className, "Heightfield")) return new Heightfield; if (!strcmp(className, "Const")) return new Const; + if (!strcmp(className, "PointLight")) return new PointLight; + if (!strcmp(className, "RectLight")) return new RectLight; + return NULL; } diff --git a/src/scene.h b/src/scene.h index 45b3384..89a6d46 100644 --- a/src/scene.h +++ b/src/scene.h @@ -37,6 +37,7 @@ enum ElementType { ELEM_ENVIRONMENT, ELEM_CAMERA, ELEM_SETTINGS, + ELEM_LIGHT, }; class SceneParser; @@ -48,6 +49,7 @@ class Texture; class Environment; class Camera; class Bitmap; +class Light; struct Transform; class ParsedBlock; @@ -243,8 +245,6 @@ struct GlobalSettings: public SceneElement { // Lighting: Color ambientLight; //!< ambient color - Vector lightPos; //!< default point light position - double lightIntensity; //!< default point light intensity // AA-related: bool wantAA; //!< Is Anti-Aliasing on? @@ -253,6 +253,7 @@ struct GlobalSettings: public SceneElement { int maxTraceDepth; //!< Maximum recursion depth bool dbg; //!< A debugging flag (if on, various raytracing-related procedures will dump debug info to stdout). + float saturation; GlobalSettings(); void fillProperties(ParsedBlock& pb); @@ -265,7 +266,7 @@ struct Scene { std::vector nodes; std::vector superNodes; // also Nodes, but without a shader attached; don't represent an scene object directly std::vector textures; - //std::vector lights; + std::vector lights; Environment* environment; Camera* camera; GlobalSettings settings; diff --git a/src/shading.cpp b/src/shading.cpp index 4eb94d5..fbf2d53 100644 --- a/src/shading.cpp +++ b/src/shading.cpp @@ -24,6 +24,7 @@ #include #include "shading.h" #include "bitmap.h" +#include "lights.h" bool visibilityCheck(const Vector& start, const Vector& end); @@ -36,14 +37,14 @@ Color CheckerTexture::sample(const IntersectionInfo& info) return checkerColor; } -double getLightContrib(const IntersectionInfo& info) +Color getLightContrib(const IntersectionInfo& info, const Vector& lightPos, const Color& lightColor) { - double distanceToLightSqr = (info.ip - scene.settings.lightPos).lengthSqr(); + double distanceToLightSqr = (info.ip - lightPos).lengthSqr(); - if (!visibilityCheck(info.ip + info.normal * 1e-6, scene.settings.lightPos)) { - return 0; + if (!visibilityCheck(info.ip + info.normal * 1e-6, lightPos)) { + return Color(0, 0, 0); } else { - return scene.settings.lightIntensity / distanceToLightSqr; + return lightColor / distanceToLightSqr; } } @@ -51,38 +52,64 @@ Color Lambert::shade(const Ray& ray, const IntersectionInfo& info) { Color diffuse = texture ? texture->sample(info) : this->color; - Vector v2 = info.ip - scene.settings.lightPos; // from light towards the intersection point - Vector v1 = faceforward(ray.dir, info.normal); // orient so that surface points to the light - v2.normalize(); - double lambertCoeff = dot(v1, -v2); - - return scene.settings.ambientLight * diffuse - + diffuse * lambertCoeff * getLightContrib(info); + Color result(0, 0, 0); + for (auto& light: scene.lights) { + int N = light->getNumSamples(); + Color sum (0, 0, 0); + for (int i = 0; i < N; i++) { + Vector lightPos; + Color lightColor; + light->getNthSample(i, info.ip, lightPos, lightColor); + Vector v2 = info.ip - lightPos; // from light towards the intersection point + Vector v1 = faceforward(ray.dir, info.normal); // orient so that surface points to the light + v2.normalize(); + double lambertCoeff = dot(v1, -v2); + sum += diffuse * lambertCoeff * getLightContrib(info, lightPos, lightColor); + } + result += sum / N; + } + result += scene.settings.ambientLight * diffuse; + return result; } Color Phong::shade(const Ray& ray, const IntersectionInfo& info) { - Color diffuse = texture ? texture->sample(info) : this->color; - Vector v2 = info.ip - scene.settings.lightPos; // from light towards the intersection point - Vector v1 = faceforward(ray.dir, info.normal); // orient so that surface points to the light - v2.normalize(); - double lambertCoeff = dot(v1, -v2); - double fromLight = getLightContrib(info); + Color diffuse = texture ? texture->sample(info) : this->color; - Vector r = reflect(v2, v1); - Vector toCamera = -ray.dir; - double cosGamma = dot(toCamera, r); - double phongCoeff; - if (cosGamma > 0) - phongCoeff = pow(cosGamma, specularExponent); - else - phongCoeff = 0; + Color result(0, 0, 0); + for (auto& light: scene.lights) { + int N = light->getNumSamples(); + Color sum (0, 0, 0); + for (int i = 0; i < N; i++) { + Vector lightPos; + Color lightColor; + light->getNthSample(i, info.ip, lightPos, lightColor); + Vector v2 = info.ip - lightPos; // from light towards the intersection point + Vector v1 = faceforward(ray.dir, info.normal); // orient so that surface points to the light + v2.normalize(); + double lambertCoeff = dot(v1, -v2); + Color fromLight = getLightContrib(info, lightPos, lightColor); + + Vector r = reflect(v2, v1); + Vector toCamera = -ray.dir; + double cosGamma = dot(toCamera, r); + double phongCoeff; + if (cosGamma > 0) + phongCoeff = pow(cosGamma, specularExponent); + else + phongCoeff = 0; + + sum += diffuse * lambertCoeff * fromLight + + (phongCoeff * specularMultiplier * fromLight); + + } + result += sum / N; + } + result += scene.settings.ambientLight * diffuse; + return result; - return scene.settings.ambientLight * diffuse - + diffuse * lambertCoeff * fromLight - + Color(1, 1, 1) * (phongCoeff * specularMultiplier * fromLight); } @@ -129,11 +156,8 @@ Color Refl::shade(const Ray& ray, const IntersectionInfo& info) for (int i = 0; i < count; i++) { Vector a, b; orthonormalSystem(n, a, b); - double angle = randomFloat() * 2 * PI; - double radius = randomFloat() * 1; double x, y; - x = cos(angle) * radius; - y = sin(angle) * radius; + genDiscPoint(1, x, y); // x *= tan((1 - glossiness) * PI/2); y *= tan((1 - glossiness) * PI/2); @@ -171,7 +195,7 @@ Color Refr::shade(const Ray& ray, const IntersectionInfo& info) // leaving the geometry refr = refract(ray.dir, -info.normal, ior); } - if (refr.lengthSqr() == 0) return Color(1, 0, 0); + if (refr.lengthSqr() == 0) return Color(0, 0, 0); Ray newRay = ray; newRay.start = info.ip - faceforward(ray.dir, info.normal) * 0.000001; newRay.dir = refr; diff --git a/src/util.cpp b/src/util.cpp index 150c8f5..2d563d7 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -31,6 +31,7 @@ #include #include +#include "util.h" using namespace std; string upCaseString(string s) @@ -64,3 +65,11 @@ bool fileExists(const char* fn) struct stat st; return (0 == stat(temp, &st)); } + +void genDiscPoint(double maxRadius, double& x, double& y) +{ + double angle = randomFloat() * 2 * PI; + double radius = randomFloat() * maxRadius; + x = cos(angle) * radius; + y = sin(angle) * radius; +} diff --git a/src/util.h b/src/util.h index 2ad388b..efdbc1e 100644 --- a/src/util.h +++ b/src/util.h @@ -43,6 +43,8 @@ inline int nearestInt(float x) { return (int) floor(x + 0.5f); } /// This is not a very good implementation. A better method is to be employed soon. inline float randomFloat() { return rand() / (float) RAND_MAX; } +void genDiscPoint(double radius, double& x, double& y); + std::string upCaseString(std::string s); //!< returns the string in UPPERCASE std::string extensionUpper(const char* fileName); //!< Given a filename, return its extension in UPPERCASE